Main Page   Class Hierarchy   Alphabetical List   Data Structures   File List   Data Fields   Globals  

rs274ngc.cc

Go to the documentation of this file.
00001 /*
00002   rs274ngc.cc
00003 
00004   Interpreter for Next Generation Controller (NGC) dialect of RS-274.
00005 
00006   Modification history:
00007 
00008   7-Aug-2000  FMP took 'static' off _interpreter_settings for non-
00009   standalone EMC also, since we want access to these for viewing.
00010   3-Aug-2000  FMP removed CANON_UPDATE_POSITION(), and accomplished the same
00011   thing in the canonical interface implementation directly, where it belonged.
00012   24-Feb-2000  FMP added call to CANON_UPDATE_POSITION() in rs274ngc_synch()
00013   20-Dec-1999  FMP made fix to convert_straight_comp2 to handle problem
00014   with atan2(0,0).
00015   17-Nov-1999  FMP fixed argument reversal in one call to ERROR_MACRO;
00016   removed two redundant sections of code in rs274ngc_init().
00017   10-Jun-1999  FMP added TOLERANCE_CONCAVE_CORNER instead of hard-coded
00018   0.001, the angle in radians above which a corner is flagged as concave.
00019   The value in rs274ngc.hh was made 0.01, half a degree, since the smaller
00020   value was invisible to programmers and they complained.
00021   14-May-1999  FMP added printing of _interpreter_linetext in error
00022   macros; moved globals to top of file since _interpreter_linetext is
00023   now referenced in many functions.
00024   19-Feb-1999  FMP added setting of parameters 5211-5213 in
00025   convert_axis_offsets() and rs274ngc_init()
00026   17-Feb-1999  FMP took axis_offset_x,y,z out of origin resetting in
00027   convert_stop(), so that the G92 axis offsets persist after a program end.
00028   Previously they were cleared, and since they were not recorded in the
00029   parameters there was no way to recover them.
00030   12-Feb-1999  TRK fixed convert_stop so that offsets were set to zero,
00031   fixing a bug in G92; FMP checked into RCS; added this header and the
00032   RCS ident tag.
00033   */
00034 
00035 /* code header
00036 
00037 ABOUT THE rs274ngc.cc FILE:
00038 
00039 The rs274ngc.cc file contains the source code for the rs274ngc
00040 interpreter, excluding a main routine and initialization routines.
00041 The file has two sections: the kernel functions (which are not
00042 intended to be called by programs using the interpreter), and
00043 the interface functions (which are intended to be called).
00044 The names of the interface functions start with "rs274ngc".
00045 
00046 Error handling throughout the system is by returning a status value of
00047 RS274NGC_OK or RS274NGC_ERROR from each function where there is a
00048 possibility of error.  An appropriate error message will be printed
00049 for each error.  If an error occurs, processing is always stopped, and
00050 control is passed back up through the function call hierarchy to the
00051 highest level function called which is defined in this file. Error
00052 reporting is handled by the CANON_ERROR function defined externally.
00053 
00054 Since returned values are usually used as just described to handle the
00055 possibility of errors, an alternative method of passing calculated
00056 values is required. In general, if function A needs a value for
00057 variable V calculated by function B, this is handled by passing a
00058 pointer to V from A to B, and B calculates and sets V.
00059 
00060 There are a lot of functions named read_XXXX. All such functions read
00061 characters from a string using a counter. They all reset the counter
00062 to point at the character in the string following the last one used by
00063 the function. The counter is passed around from function to function
00064 by using pointers to it. The first character read by each of these
00065 functions is expected to be a member of some set of characters (often
00066 a specific character), and each function checks the first character.
00067 
00068 This version of the interpreter not saving input lines. A list
00069 of all lines will be needed in future versions to implement loops,
00070 and probably for other purposes.
00071 
00072 This version is does not use any noticeable additional memory as it
00073 runs. Only comments in the NC code are malloc'ed, and that memory is
00074 freed when the next line of code is read. There should not be any
00075 memory leaks.
00076 
00077 This version does not suppress superfluous commands, such as a command
00078 to start the spindle when the spindle is already turning, or a command
00079 to turn on flood coolant, when flood coolant is already on.  When the
00080 interpreter is being used for direct control of the machining center,
00081 suppressing superfluous commands might confuse the user and could be
00082 dangerous, but when it is used to translate from one file to another,
00083 suppression can produce more concise output. Future versions might
00084 include an option for suppressing superfluous commands.
00085 
00086 */
00087 
00088 /****************************************************************************/
00089 
00090 #include <stdio.h>
00091 #include <stdlib.h>
00092 #include <math.h>
00093 #include <string.h>
00094 #include <ctype.h>              // for read_text CR, LF removal
00095 #include "canon.hh"
00096 #include "rs274ngc.hh"
00097 
00098 // ident tag
00099 static char ident[] = "$Id: rs274ngc.cc,v 1.4 2000/10/27 20:34:44 terrylr Exp $";
00100 
00101 /* the current interpreter settings */
00102 
00103 #ifdef STAND_ALONE_INTERP
00104 
00105 #define DEBUG_EMC
00106 setup _interpreter_settings;
00107 
00108 #else
00109 
00110 #define DEBUG_EMC
00111 setup _interpreter_settings;
00112 
00113 #endif
00114 
00115 /*
00116 
00117 The following two function prototypes are for two functions which are
00118 used recursively by each other and therefore have forward references
00119 regardless of the order in which they are defined.
00120 
00121 */
00122 
00123 int read_real_value(char * line, int * counter,
00124                     double * double_ptr, double * parameters);
00125 int read_real_expression(char * line, int * counter,
00126                             double * hold2, double * parameters);
00127 
00128 /* Interpreter global arrays for g_codes and m_codes. The nth entry
00129 in each array is the modal group number corresponding to the nth
00130 code. Entries which are -1 represent illegal codes. Remember g_codes
00131 in this interpreter are multiplied by 10.
00132 
00133 The modal g groups and group numbers defined in [NCMS, pages 71 - 73]
00134 are used here, except the canned cycles (g80 - g89), which comprise
00135 modal g group 9 in [NCMS], are treated here as being in the same modal
00136 group (group 1) with the straight moves and arcs (g0, g1, g2,g3).
00137 The straight_probe move, g38.2, is in group 1; it is not defined in
00138 [NCMS].
00139 
00140 Some g_codes (g4, g10, g53, g92, and g92.2 here - many more in [NCMS])
00141 are non-modal. [NCMS] puts all these in the same group 0.  Logically,
00142 there are two subgroups, those which require coordinate values and
00143 those which do not. Here we are using separate groups for these two.
00144 In group 0 we have put the ones that take coordinate values (g10,
00145 g92), and g92.2, which cancels g92.  In group 4 (which is not
00146 otherwise being used) we have put the ones that do not use coordinate
00147 values (g4, g53). We are allowing only one g_code from the combined
00148 groups 0 and 4 on a line. Those in group 0 may not be on the same line
00149 as those in group 1 (except g80) because they would be competing for
00150 the axis values. Those in group 4 may be used on the same line as
00151 those in group 1.
00152 
00153 There is no evident reason why only one of our modal group 4 should be
00154 allowed on a line, and it is not clear what the intent of [NCMS] is
00155 regarding how many of these may be on the same line. We are following
00156 the rule given above, however.
00157 
00158 The groups are:
00159 group  0 = {g10,g92,g92.2} - setup, set axis offsets (non-modal)
00160 group  1 = {g0,g1,g2,g3,g38.2,g80,g81,g82,g83,g84,g85,g86,g87,g88,g89} - motion
00161 group  2 = {g17,g18,g19}   - plane selection
00162 group  3 = {g90,g91}       - distance mode
00163 group  4 = {g4, g53}       - dwell, motion in abs coords (non-modal)
00164 group  5 = {g93,g94}       - spindle speed mode
00165 group  6 = {g20,g21}       - units
00166 group  7 = {g40,g41,g42}   - cutter diameter compensation
00167 group  8 = {g43,g49}       - tool length offset
00168 group 10 = {g98,g99}       - return mode in canned cycles
00169 group 12 = {g54,g55,g56,g57,g58,g59,g59.1,g59.2,g59.3} - coordinate system
00170 group 13 = {g61,g64}       - motion mode (exact stop or cutting)
00171 
00172 */
00173 
00174 static int gees[] SET_TO {
00175 /*   0 */   1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00176 /*  20 */   1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00177 /*  40 */   4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00178 /*  60 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00179 /*  80 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00180 /* 100 */   0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00181 /* 120 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00182 /* 140 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00183 /* 160 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 2,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00184 /* 180 */   2,-1,-1,-1,-1,-1,-1,-1,-1,-1, 2,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00185 /* 200 */   6,-1,-1,-1,-1,-1,-1,-1,-1,-1, 6,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00186 /* 220 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00187 /* 240 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00188 /* 260 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00189 /* 280 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00190 /* 300 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00191 /* 320 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00192 /* 340 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00193 /* 360 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00194 /* 380 */  -1,-1, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00195 /* 400 */   7,-1,-1,-1,-1,-1,-1,-1,-1,-1, 7,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00196 /* 420 */   7,-1,-1,-1,-1,-1,-1,-1,-1,-1, 8,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00197 /* 440 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00198 /* 460 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00199 /* 480 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 8,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00200 /* 500 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00201 /* 520 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 4,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00202 /* 540 */  12,-1,-1,-1,-1,-1,-1,-1,-1,-1,12,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00203 /* 560 */  12,-1,-1,-1,-1,-1,-1,-1,-1,-1,12,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00204 /* 580 */  12,-1,-1,-1,-1,-1,-1,-1,-1,-1,12,12,12,12,-1,-1,-1,-1,-1,-1,
00205 /* 600 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,13,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00206 /* 620 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00207 /* 640 */  13,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00208 /* 660 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00209 /* 680 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00210 /* 700 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00211 /* 720 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00212 /* 740 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00213 /* 760 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00214 /* 780 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00215 /* 800 */   1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00216 /* 820 */   1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00217 /* 840 */   1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00218 /* 860 */   1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00219 /* 880 */   1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00220 /* 900 */   3,-1,-1,-1,-1,-1,-1,-1,-1,-1, 3,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00221 /* 920 */   0,-1, 0,-1,-1,-1,-1,-1,-1,-1, 5,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00222 /* 940 */   5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00223 /* 960 */  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00224 /* 980 */  10,-1,-1,-1,-1,-1,-1,-1,-1,-1,10,-1,-1,-1,-1,-1,-1,-1,-1,-1};
00225 
00226 /*
00227 
00228 Modal groups and modal group numbers for M codes are described in [NCMS,
00229 page 7]. We have added M60 as an extension of the language
00230 so the language can be used to control the K&T 800 machine.
00231 
00232 The groups are:
00233 group 4 = {m0,m1,m2,m30,m60} - stopping
00234 group 6 = {m6}               - tool change
00235 group 7 = {m3,m4,m5}         - spindle turning
00236 group 8 = {m7,m8,m9}         - coolant
00237 group 9 = {m48,m49}          - feed and speed override switch bypass
00238 
00239 */
00240 
00241 static int ems[] SET_TO {
00242    4,  4,  4,  7,  7,  7,  6,  8,  8,  8,
00243   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
00244   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
00245    4, -1, -1, -1, -1, -1, -1, -1, -1, -1,
00246   -1, -1, -1, -1, -1, -1, -1, -1,  9,  9,
00247   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
00248    4, -1, -1, -1, -1, -1, -1, -1, -1, -1,
00249   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
00250   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
00251   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
00252 
00253 /*
00254 
00255 This is the list of error messages that may be generated. Messages
00256 numbered 0 through 199 result from errors in the input to the interpreter.
00257 Messages numbered 200 and above result from bugs in the interpreter and
00258 should never be received.
00259 
00260 */
00261 
00262 char * interp_errors[] SET_TO {
00263 /*   0 */ "Unspecified error", /*  see note above */
00264 /*   1 */ "All axes missing with G92", /* check_g_codes */
00265 /*   2 */ "Arc radius too small to reach end point", /* arc_data_r */
00266 /*   3 */ "Argument to acos out of range", /* execute_unary */
00267 /*   4 */ "Argument to asin out of range", /* execute_unary */
00268 /*   5 */ "Attempt to divide by zero", /* execute_binary1 */
00269 /*   6 */ "Bad character used", /* read_one_item */
00270 /*   7 */ "Bad format unsigned integer", /* read_integer_unsigned */
00271 /*   8 */ "Bad number format", /* read_real_number */
00272 /*   9 */ "Bad tool radius value with cutter radius comp",
00273         /* convert_arc_comp1, convert_arc_comp2, convert_straight_comp1,
00274            convert_straight_comp2 */
00275 /*  10 */ "Cannot change axis offsets with cutter radius comp",
00276         /* convert_axis_offsets */
00277 /*  11 */ "Cannot change units with cutter radius comp",
00278         /* convert_length_units */
00279 /*  12 */ "Cannot create backup file", /* rs274ngc_save_parameters */
00280 /*  13 */ "Cannot do G1 with zero feed rate", /* convert_straight */
00281 /*  14 */ "Cannot do zero repeats of cycle", /* convert_cycle */
00282 /*  15 */ "Cannot have concave corner with cutter radius comp",
00283         /* convert_straight_comp2 */
00284 /*  16 */ "Cannot make arc with zero feed rate", /* convert_arc */
00285 /*  17 */ "Cannot open backup file", /* rs274ngc_save_parameters */
00286 /*  18 */ "Cannot open file", /* rs274ngc_restore_parameters */
00287 /*  19 */ "Cannot open variable file", /* rs274ngc_save_parameters */
00288 /*  20 */ "Cannot probe in inverse time feed mode", /* convert_motion */
00289 /*  21 */ "Cannot probe with cutter radius compensation on",
00290         /* convert_motion */
00291 /*  22 */ "Cannot probe with zero feed rate", /* convert_motion */
00292 /*  23 */ "Cannot put an M code for stopping with G38.2", /* check_items */
00293 /*  24 */ "Cannot raise negative number to non-integer power",
00294         /* execute_binary1 */
00295 /*  25 */ "Cannot turn cutter radius comp on out of XY-plane",
00296         /* convert_cutter_compensation_on */
00297 /*  26 */ "Cannot turn cutter radius comp on when already on",
00298         /* convert_cutter_compensation_on */
00299 /*  27 */ "Cannot use G0 with cutter radius comp", /* convert_straight */
00300 /*  28 */ "Cannot use G53 in incremental distance mode", /* check_g_codes */
00301 /*  29 */ "Cannot use G53 with cutter radius comp", /* convert_straight */
00302 /*  30 */ "Cannot use XZ plane with cutter radius comp",
00303         /* convert_set_plane */
00304 /*  31 */ "Cannot use YZ plane with cutter radius comp",
00305         /* convert_set_plane */
00306 /*  32 */ "Cannot use a G code for motion with G10", /* check_g_codes */
00307 /*  33 */ "Cannot use a G motion with G92", /* check_g_codes */
00308 /*  34 */ "Cannot use axis commands with G4", /* check_g_codes */
00309 /*  35 */ "Cannot use axis commands with G80", /* check_g_codes */
00310 /*  36 */ "Cannot use inverse time feed with cutter radius comp",
00311         /* convert_straight */
00312 /*  37 */ "Cannot use two G codes from group 0", /* check_g_codes */
00313 /*  38 */ "Command too long", /* close_and_downcase, rs274ngc_execute */
00314 /*  39 */ "Concave corner with cutter radius comp", /* convert_arc_comp2 */
00315 /*  40 */ "Coordinate setting given with G80", /* convert_motion */
00316 /*  41 */ "Current point same as end point of arc",
00317         /* arc_data_comp_r, arc_data_r */
00318 /*  42 */ "Cutter gouging with cutter radius comp",
00319         /* convert_straight_comp1 */
00320 /*  43 */ "D word missing with cutter radius comp on",
00321         /* convert_cutter_compensation_on */
00322 /*  44 */ "D word on line with no cutter comp on (G41 or G42) command",
00323         /* check_other_codes */
00324 /*  45 */ "Dwell time missing with G4", /* check_g_codes */
00325 /*  46 */ "Equal sign missing in parameter setting",
00326         /* read_parameter_setting */
00327 /*  47 */ "F word missing with inverse time G1 move", /* convert_straight */
00328 /*  48 */ "F word missing with inverse time arc move", /* convert_motion */
00329 /*  49 */ "File ended with no stopping command given", /* rs274ngc_read */
00330 /*  50 */ "G code out of range", /* read_g */
00331 /*  51 */ "H word on line with no tool length comp (G43) command",
00332         /* check_other_codes */
00333 /*  52 */ "I word given for arc in YZ-plane", /* convert_arc */
00334 /*  53 */ "I word missing with G87", /* convert_cycle */
00335 /*  54 */ "I word on line with no G code (G2, G3, G87) that uses it",
00336         /* check_other_codes */
00337 /*  55 */ "J word given for arc in XZ-plane", /* convert_arc */
00338 /*  56 */ "J word missing with G87", /* convert_cycle */
00339 /*  57 */ "J word on line with no G code (G2, G3, G87) that uses it",
00340         /* check_other_codes */
00341 /*  58 */ "K word given for arc in XY-plane", /* convert_arc */
00342 /*  59 */ "K word missing with G87", /* convert_cycle */
00343 /*  60 */ "K word on line with no G code (G2, G3, G87) that uses it",
00344         /* check_other_codes */
00345 /*  61 */ "L word on line with no canned cycle or G10 to use it",
00346         /* check_other_codes */
00347 /*  62 */ "Left bracket missing after slash with atan operator",
00348         /* read_atan */
00349 /*  63 */ "Left bracket missing after unary operation name", /* read_unary */
00350 /*  64 */ "Line number greater than 99999", /* read_line_number */
00351 /*  65 */ "Line with G10 does not have L2", /* check_g_codes */
00352 /*  66 */ "M code greater than 99", /* read_m */
00353 /*  67 */ "Mixed radius-ijk format for arc", /* convert_arc */
00354 /*  68 */ "Multiple D words on one line", /* read_d */
00355 /*  69 */ "Multiple F words on one line", /* read_f */
00356 /*  70 */ "Multiple I words on one line", /* read_i */
00357 /*  71 */ "Multiple J words on one line", /* read_j */
00358 /*  72 */ "Multiple K words on one line", /* read_k */
00359 /*  73 */ "Multiple L words on one line", /* read_l */
00360 /*  74 */ "Multiple P words on one line", /* read_p */
00361 /*  75 */ "Multiple Q words on one line", /* read_q */
00362 /*  76 */ "Multiple R words on one line", /* read_r */
00363 /*  77 */ "Multiple S word spindle speed settings on one line",
00364         /* read_spindle_speed */
00365 /*  78 */ "Multiple T words (tool ids) on one line", /* read_tool */
00366 /*  79 */ "Multiple X words on one line", /* read_x */
00367 /*  80 */ "Multiple Y words on one line", /* read_y */
00368 /*  81 */ "Multiple Z words on one line", /* read_z */
00369 /*  82 */ "Multiple tool length offsets on one line", /* read_tool_length_offset */
00370 /*  83 */ "Must use G0 or G1 with G53", /* check_g_codes */
00371 /*  84 */ "Negative D code used", /* read_d */
00372 /*  85 */ "Negative F word found", /* read_f */
00373 /*  86 */ "Negative G code used", /* read_g */
00374 /*  87 */ "Negative L word used", /* read_l */
00375 /*  88 */ "Negative M code used", /* read_m */
00376 /*  89 */ "Negative P value used", /* read_p */
00377 /*  90 */ "Negative Q value used", /* read_q */
00378 /*  91 */ "Negative argument to sqrt", /* execute_unary */
00379 /*  92 */ "Negative spindle speed found", /* read_spindle_speed */
00380 /*  93 */ "Negative tool id used", /* read_tool */
00381 /*  94 */ "Negative tool length offset used", /* read_tool_length_offset */
00382 /*  95 */ "Nested comment found", /* close_and_downcase */
00383 /*  96 */ "No characters found in reading real value", /* read_real_value */
00384 /*  97 */ "No digits found where real number should be", /* read_real_number */
00385 /*  98 */ "Non-integer value for integer", /* read_integer_value */
00386 /*  99 */ "Null missing after newline", /* close_and_downcase */
00387 /* 100 */ "Offset index missing", /* convert_tool_length_offset */
00388 /* 101 */ "P value not an integer with G10 L2", /* check_g_codes */
00389 /* 102 */ "P value out of range with G10 L2", /* check_g_codes */
00390 /* 103 */ "P word (dwell time) missing with G82", /* convert_cycle */
00391 /* 104 */ "P word (dwell time) missing with G86", /* convert_cycle */
00392 /* 105 */ "P word (dwell time) missing with G88", /* convert_cycle */
00393 /* 106 */ "P word (dwell time) missing with G89", /* convert_cycle */
00394 /* 107 */"P word on line with no G code (G4 G10 G82 G86 G88 G89) that uses it",
00395         /* check_other_codes */
00396 /* 108 */ "Parameter number out of range",
00397         /* read_parameter, read_parameter_setting, rs274ngc_restore_parameters,
00398            rs274ngc_save_parameters */
00399 /* 109 */ "Q word (depth increment) missing with G83", /* convert_cycle */
00400 /* 110 */ "Q word on line with no G83 cycle that uses it",
00401         /* check_other_codes */
00402 /* 111 */ "Queue is not empty after probing",
00403         /* rs274ngc_execute, rs274ngc_read */
00404 /* 112 */ "R clearance plane unspecified in canned cycle", /* convert_cycle */
00405 /* 113 */ "R value less than Z value in canned cycle", /* convert_cycle */
00406 /* 114 */ "R word on line with no G code (arc or cycle) that uses it",
00407         /* check_other_codes */
00408 /* 115 */ "R, I, J, and K words all missing for arc", /* convert_arc */
00409 /* 116 */ "Radius to end of arc differs from radius to start of arc",
00410         /* arc_data_comp_ijk, arc_data_ijk */
00411 /* 117 */ "Radius too small to reach end point", /* arc_data_comp_r */
00412 /* 118 */ "Selected tool slot number too large", /* convert_tool_select */
00413 /* 119 */ "Slash missing", /* read_atan */
00414 /* 120 */ "Spindle not turning clockwise in G84 canned cycle",
00415         /* convert_cycle_g84 */
00416 /* 121 */ "Spindle not turning in G86 canned cycle", /* convert_cycle_g86 */
00417 /* 122 */ "Spindle not turning in G87 canned cycle", /* convert_cycle_g87 */
00418 /* 123 */ "Spindle not turning in G88 canned cycle", /* convert_cycle_g88 */
00419 /* 124 */ "Too many G codes on line", /* check_g_codes */
00420 /* 125 */ "Too many M codes on line", /* check_m_codes */
00421 /* 126 */ "Tool index out of bounds", /* convert_tool_length_offset */
00422 /* 127 */ "Tool radius not less than arc radius with cutter radius comp",
00423         /* arc_data_comp_r, convert_arc_comp2 */
00424 /* 128 */ "Two G codes used from same modal group", /* read_g */
00425 /* 129 */ "Two M codes used from same modal group", /* read_m */
00426 /* 130 */ "Unable to open file", /* rs274ngc_open */
00427 /* 131 */ "Unclosed comment found", /* close_and_downcase */
00428 /* 132 */ "Unclosed expression", /* read_operation */
00429 /* 133 */ "Unknown G code used", /* read_g */
00430 /* 134 */ "Unknown M code used", /* read_m */
00431 /* 135 */ "Unknown operation name starting with A", /* read_operation */
00432 /* 136 */ "Unknown operation name starting with M", /* read_operation */
00433 /* 137 */ "Unknown operation name starting with O", /* read_operation */
00434 /* 138 */ "Unknown operation name starting with X", /* read_operation */
00435 /* 139 */ "Unknown operation", /* read_operation */
00436 /* 140 */ "Unknown word starting with A", /* read_operation_unary */
00437 /* 141 */ "Unknown word starting with C", /* read_operation_unary */
00438 /* 142 */ "Unknown word starting with E", /* read_operation_unary */
00439 /* 143 */ "Unknown word starting with F", /* read_operation_unary */
00440 /* 144 */ "Unknown word starting with L", /* read_operation_unary */
00441 /* 145 */ "Unknown word starting with R", /* read_operation_unary */
00442 /* 146 */ "Unknown word starting with S", /* read_operation_unary */
00443 /* 147 */ "Unknown word starting with T", /* read_operation_unary */
00444 /* 148 */ "Unknown word where unary operation could be",
00445         /* read_operation_unary */
00446 /* 149 */ "X and Y words missing for arc in XY-plane", /* convert_arc */
00447 /* 150 */ "X and Z words missing for arc in XZ-plane", /* convert_arc */
00448 /* 151 */ "X, Y, and Z words all missing with G0 or G1", /* convert_straight */
00449 /* 152 */ "X, Y, and Z words all missing with G38.2", /* convert_probe */
00450 /* 153 */ "Y and Z words missing for arc in YZ-plane", /* convert_arc */
00451 /* 154 */ "Z value unspecified in canned cycle", /* convert_cycle */
00452 /* 155 */ "Zero or negative argument to ln", /* execute_unary */
00453 
00454 
00455 "", "", "", "",
00456 "", "", "", "", "", "", "", "", "", "",
00457 "", "", "", "", "", "", "", "", "", "",
00458 "", "", "", "", "", "", "", "", "", "",
00459 "", "", "", "", "", "", "", "", "", "",
00460 
00461 /* 200 */ "Bad setting of g_mode in check_g_codes",
00462 /* 201 */ "Code is not G0 or G1 in convert_straight",
00463 /* 202 */ "Code is not G0 to G3 or G80 to G89 in convert_motion",
00464 /* 203 */ "Code is not G10, G92, or G92.2 in convert_modal_0",
00465 /* 204 */ "Code is not G17, G18, or G19 in convert_set_plane",
00466 /* 205 */ "Code is not G2 or G3 in arc_data_comp",
00467 /* 206 */ "Code is not G2 or G3 in arc_data_ijk",
00468 /* 207 */ "Code is not G20 or G21 in convert_length_units",
00469 /* 208 */ "Code is not G40, G41, or G42 in convert_cutter_compensation",
00470 /* 209 */ "Code is not G43 or G49 in convert_tool_length_offset",
00471 /* 210 */ "Code is not G54 to G59.3 in convert_coordinate_system",
00472 /* 211 */ "Code is not G61 or G64 in convert_control_mode",
00473 /* 212 */ "Code is not G90 or G91 in convert_distance_mode",
00474 /* 213 */ "Code is not G92 or G92.2 in convert_axis_offsets",
00475 /* 214 */ "Code is not G93 or G94 in convert_feed_mode",
00476 /* 215 */ "Code is not G98 or G99 in convert_retract_mode",
00477 /* 216 */ "Code is not M0, M1, M2, M30 or M60 in convert_stop",
00478 /* 217 */ "Convert_cycle should not have been called",
00479 /* 218 */ "Distance mode is neither absolute nor incremental in convert_cycle",
00480 /* 219 */ "Plane is not XY, YZ, or XZ in convert_arc",
00481 /* 220 */ "Read_comment should not have been called",
00482 /* 221 */ "Read_d should not have been called",
00483 /* 222 */ "Read_f should not have been called",
00484 /* 223 */ "Read_g should not have been called",
00485 /* 224 */ "Read_i should not have been called",
00486 /* 225 */ "Read_j should not have been called",
00487 /* 226 */ "Read_k should not have been called",
00488 /* 227 */ "Read_l should not have been called",
00489 /* 228 */ "Read_line_number should not have been called",
00490 /* 229 */ "Read_m should not have been called",
00491 /* 230 */ "Read_p should not have been called",
00492 /* 231 */ "Read_parameter should not have been called",
00493 /* 232 */ "Read_parameter_setting should not have been called",
00494 /* 233 */ "Read_q should not have been called",
00495 /* 234 */ "Read_r should not have been called",
00496 /* 235 */ "Read_real_expression should not have been called",
00497 /* 236 */ "Read_spindle_speed should not have been called",
00498 /* 237 */ "Read_tool should not have been called",
00499 /* 238 */ "Read_tool_length_offset should not have been called",
00500 /* 239 */ "Read_x should not have been called",
00501 /* 240 */ "Read_y should not have been called",
00502 /* 241 */ "Read_z should not have been called",
00503 /* 242 */ "Side fails to be right or left in convert_straight_comp1",
00504 /* 243 */ "Side fails to be right or left in convert_straight_comp2",
00505 /* 244 */ "Sscanf failure in read_integer_unsigned",
00506 /* 245 */ "Sscanf failure in read_real_number",
00507 /* 246 */ "Unknown operation in execute_binary1",
00508 /* 247 */ "Unknown operation in execute_binary2",
00509 /* 248 */ "Unknown operation in execute_unary",
00510 
00511 /* 249 */ ""};
00512 
00513 /* these globals are used to implement the external interface and
00514    are referenced by the error macros */
00515 
00516 #ifdef STAND_ALONE_INTERP
00517 
00518 int _textline SET_TO 0;         /* set to 1 in rs274ngc_open */
00519 char _interpreter_filename[INTERP_TEXT_SIZE];   /* file name */
00520 char _interpreter_linetext[INTERP_TEXT_SIZE];    /* raw text */
00521 char _interpreter_blocktext[INTERP_TEXT_SIZE]; /*parsed text */
00522 block _interpreter_block SET_TO {0};    /* parsed next block */
00523 FILE *_interpreter_fp SET_TO NULL;           /* file pointer */
00524 int _interpreter_status SET_TO 0;
00525 
00526 #else
00527 
00528 static int _textline SET_TO 0;         /* set to 1 in rs274ngc_open */
00529 static char _interpreter_filename[INTERP_TEXT_SIZE];   /* file name */
00530 static char _interpreter_linetext[INTERP_TEXT_SIZE];    /* raw text */
00531 static char _interpreter_blocktext[INTERP_TEXT_SIZE]; /*parsed text */
00532 static block _interpreter_block SET_TO {0};    /* parsed next block */
00533 static FILE *_interpreter_fp SET_TO NULL;           /* file pointer */
00534 static int _interpreter_status SET_TO 0;
00535 
00536 #endif
00537 
00538 static int _interpreter_length SET_TO 0;    /* length of next line */
00539 static int _interpreter_active_g_codes[RS274NGC_ACTIVE_G_CODES];
00540 static int _interpreter_active_m_codes[RS274NGC_ACTIVE_M_CODES];
00541 static double _interpreter_active_settings[RS274NGC_ACTIVE_SETTINGS];
00542 
00543 // the file to use for storing params
00544 char RS274NGC_PARAMETER_FILE[INTERP_TEXT_SIZE] =
00545                          DEFAULT_RS274NGC_PARAMETER_FILE;
00546 
00547 /****************************************************************************/
00548 
00549 /* utility_error_number
00550 
00551 Returned Value: int (index number of error message in error_array)
00552 
00553 Side effects: (none)
00554 
00555 Called by: ERROR_MACRO, BUG_MACRO
00556 
00557 This reads through the error message array until it finds one that
00558 matches the given error message; then it returns the index of that
00559 message in the array.  If the error message is not found, this returns
00560 zero, the index of a string that says "Unspecified error". If zero
00561 is ever returned, that indicates a bug in the error message array,
00562 because all error messages in the system are supposed to be in that
00563 array.
00564 
00565 The initial value of index is always either 0 or 200 when this is
00566 called, since the input error messages start at index 0 and the
00567 internal error messages start at index 200 in the array.
00568 
00569 This is a slow way to report errors, but all of these errors stop
00570 the show, anyway, so an extra millisecond or two is not significant.
00571 
00572 */
00573 
00574 int utility_error_number(/* ARGUMENT VALUES           */
00575  char * message,         /* string: the message text  */
00576  char * error_array[],   /* array of error messages   */
00577  int index)              /* index to start at         */
00578 {
00579   for (;error_array[index][0] ISNT 0; index++)
00580     {
00581       if (strcmp(message, error_array[index]) IS 0)
00582         return index;
00583     }
00584   return 0;
00585 }
00586 
00587 /****************************************************************************/
00588 
00589 /* execute_binary1
00590 
00591 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
00592    If any of the following errors occur, this returns RS274NGC_ERROR.
00593    Otherwise, it returns RS274NGC_OK.
00594    1. the operation is unknown.
00595    2. An attempt is made to divide by zero.
00596    3. An attempt is made to raise a negative number to a non-integer power.
00597 
00598 Side effects:
00599    The result from performing the operation is put into what left points at.
00600 
00601 Called by: read_real_expression.
00602 
00603 This executes the operations: DIVIDED_BY, MODULO, POWER, TIMES.
00604 
00605 */
00606 
00607 int execute_binary1( /* ARGUMENT VALUES                 */
00608  double * left,      /* pointer to the left operand     */
00609  int operation,      /* integer code for the operation  */
00610  double * right)     /* pointer to the right operand    */
00611 {
00612   static char name[] SET_TO "execute_binary1";
00613   switch (operation)
00614     {
00615     case DIVIDED_BY:
00616       if (*right IS 0.0)
00617         ERROR_MACRO(_interpreter_linetext, name, "Attempt to divide by zero");
00618       else
00619         {
00620           *left SET_TO (*left / *right);
00621           return RS274NGC_OK;
00622         }
00623     case MODULO: /* always calculates a positive answer */
00624       *left SET_TO fmod(*left, *right);
00625       if (*left < 0.0)
00626         {
00627           *left SET_TO (*left + fabs(*right));
00628         }
00629       return RS274NGC_OK;
00630     case POWER:
00631       if ((*left < 0.0) AND (floor(*right) ISNT *right))
00632         ERROR_MACRO(_interpreter_linetext, name, "Cannot raise negative number to non-integer power");
00633       *left SET_TO pow(*left, *right);
00634       return RS274NGC_OK;
00635     case TIMES:
00636       *left SET_TO (*left * *right);
00637       return RS274NGC_OK;
00638     default:
00639       BUG_MACRO(name, "Unknown operation in execute_binary1");
00640     }
00641 }
00642 
00643 /****************************************************************************/
00644 
00645 /* execute_binary2
00646 
00647 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
00648    If the operation is unknown, this returns RS274NGC_ERROR.
00649    Otherwise, it returns RS274NGC_OK.
00650 
00651 Side effects:
00652    The result from performing the operation is put into what left points at.
00653 
00654 Called by: read_real_expression.
00655 
00656 This executes the operations: AND2, EXCLUSIVE_OR, MINUS,
00657 NON_EXCLUSIVE_OR, PLUS. The manual [NCMS] does not say what
00658 the calculated value of the three logical operations should be. This
00659 function calculates either 1.0 (meaning true) or 0.0 (meaning false).
00660 Any non-zero input value is taken as meaning true, and only 0.0 means
00661 false.
00662 
00663 
00664 */
00665 
00666 int execute_binary2( /* ARGUMENT VALUES                 */
00667  double * left,      /* pointer to the left operand     */
00668  int operation,      /* integer code for the operation  */
00669  double * right)     /* pointer to the right operand    */
00670 {
00671   static char name[] SET_TO "execute_binary2";
00672   switch (operation)
00673     {
00674     case AND2:
00675       *left SET_TO ((*left IS 0.0) OR (*right IS 0.0)) ? 0.0 : 1.0;
00676       return RS274NGC_OK;
00677     case EXCLUSIVE_OR:
00678       *left SET_TO (((*left IS 0.0) AND (*right ISNT 0.0)) OR
00679                     ((*left ISNT 0.0) AND (*right IS 0.0))) ? 1.0 : 0.0;
00680       return RS274NGC_OK;
00681     case MINUS:
00682       *left SET_TO (*left - *right);
00683       return RS274NGC_OK;
00684     case NON_EXCLUSIVE_OR:
00685       *left SET_TO ((*left ISNT 0.0) OR (*right ISNT 0.0)) ? 1.0 : 0.0;
00686       return RS274NGC_OK;
00687     case PLUS:
00688       *left SET_TO (*left + *right);
00689       return RS274NGC_OK;
00690     default:
00691       BUG_MACRO(name, "Unknown operation in execute_binary2");
00692     }
00693 }
00694 
00695 /****************************************************************************/
00696 
00697 /* execute_unary
00698 
00699 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
00700    If any of the following occur, this returns RS274NGC_ERROR.
00701    Otherwise, it returns RS274NGC_OK.
00702    1. the operation is unknown
00703    2. the argument to acos or asin is not between minus and plus one
00704    3. the argument to the natural logarithm is not positive
00705    4. the argument to square root is negative
00706 
00707 Side effects:
00708    The result from performing the operation on the value in double_ptr
00709    is put into what double_ptr points at.
00710 
00711 Called by: read_unary.
00712 
00713 This executes the operations: ABS, ACOS, ASIN, COS, EXP, FIX, FUP, LN
00714 ROUND, SIN, SQRT, TAN
00715 
00716 All angle measures in the input or output are in degrees.
00717 
00718 */
00719 
00720 int execute_unary (   /* ARGUMENT VALUES                 */
00721  double * double_ptr, /* pointer to the operand          */
00722  int operation)       /* integer code for the operation  */
00723 {
00724   static char name[] SET_TO "execute_unary";
00725   switch (operation)
00726     {
00727     case ABS:
00728       if (*double_ptr < 0.0)
00729           *double_ptr SET_TO (-1.0 * *double_ptr);
00730       return RS274NGC_OK;
00731     case ACOS:
00732       if ((*double_ptr < -1.0) OR (*double_ptr > 1.0))
00733         ERROR_MACRO(_interpreter_linetext, name, "Argument to acos out of range");
00734       else
00735         {
00736           *double_ptr SET_TO acos(*double_ptr);
00737           *double_ptr SET_TO ((*double_ptr * 180.0)/ PI);
00738           return RS274NGC_OK;
00739         }
00740     case ASIN:
00741       if ((*double_ptr < -1.0) OR (*double_ptr > 1.0))
00742         ERROR_MACRO(_interpreter_linetext, name, "Argument to asin out of range");
00743       else
00744         {
00745           *double_ptr SET_TO asin(*double_ptr);
00746           *double_ptr SET_TO ((*double_ptr * 180.0)/ PI);
00747           return RS274NGC_OK;
00748         }
00749     case COS:
00750       *double_ptr SET_TO cos((*double_ptr * PI)/180.0);
00751       return RS274NGC_OK;
00752     case EXP:
00753       *double_ptr SET_TO exp(*double_ptr);
00754       return RS274NGC_OK;
00755     case FIX:
00756       *double_ptr SET_TO floor(*double_ptr);
00757       return RS274NGC_OK;
00758     case FUP:
00759       *double_ptr SET_TO ceil(*double_ptr);
00760       return RS274NGC_OK;
00761     case LN:
00762       if (*double_ptr <= 0.0)
00763         ERROR_MACRO(_interpreter_linetext, name, "Zero or negative argument to ln");
00764       else
00765         {
00766           *double_ptr SET_TO log(*double_ptr);
00767           return RS274NGC_OK;
00768         }
00769     case ROUND:
00770       *double_ptr SET_TO (double)
00771         ((int) (*double_ptr + ((*double_ptr < 0.0) ? -0.5 : 0.5)));
00772       return RS274NGC_OK;
00773     case SIN:
00774       *double_ptr SET_TO sin((*double_ptr * PI)/180.0);
00775       return RS274NGC_OK;
00776     case SQRT:
00777       if (*double_ptr < 0.0)
00778         ERROR_MACRO(_interpreter_linetext, name, "Negative argument to sqrt");
00779       else
00780         {
00781           *double_ptr SET_TO sqrt(*double_ptr);
00782           return RS274NGC_OK;
00783         }
00784     case TAN:
00785       *double_ptr SET_TO tan((*double_ptr * PI)/180.0);
00786       return RS274NGC_OK;
00787     default:
00788       BUG_MACRO(name, "Unknown operation in execute_unary");
00789     }
00790 }
00791 
00792 /****************************************************************************/
00793 
00794 /* read_atan
00795 
00796 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
00797    If any of the following errors occur, this returns RS274NGC_ERROR.
00798    Otherwise, it returns RS274NGC_OK.
00799    1. the first character to read is not a slash
00800    2. the second character to read is not a left bracket
00801    3. read_real_expression returns RS274NGC_ERROR while reading
00802       the second expression
00803 
00804 Side effects:
00805    The computed value is put into what double_ptr points at.
00806    The counter is reset to point to the first character after the
00807    characters which make up the value.
00808 
00809 Called by:
00810    read_unary
00811 
00812 When this function is called, the characters "atan" and the first
00813 argument have already been read, and the value of the first argument
00814 is stored in double_ptr.  This function attempts to read a slash and
00815 the second argument to the atan function, starting at the index given
00816 by the counter and then to compute the value of the atan operation
00817 applied to the two arguments.  The computed value is inserted into
00818 what double_ptr points at.
00819 
00820 The computed value is in the range from -180 degrees to +180 degrees.
00821 The range is not specified in the manual [NCMS, page 51],
00822 although using degrees (not radians) is specified.
00823 
00824 */
00825 
00826 int read_atan(        /* ARGUMENT VALUES                                */
00827  char * line,         /* string: line of RS274/NGC code being processed */
00828  int * counter,       /* pointer to a counter for position on line      */
00829  double * double_ptr, /* pointer to double to be read                   */
00830  double * parameters) /* array of system parameters                     */
00831 {
00832   static char name[] SET_TO "read_atan";
00833   double argument2;
00834 
00835   if (line [*counter] ISNT '/')
00836     ERROR_MACRO(_interpreter_linetext, name, "Slash missing");
00837 
00838   *counter SET_TO (*counter + 1);
00839 
00840   if(line[*counter] ISNT '[')
00841     ERROR_MACRO(_interpreter_linetext, name, "Left bracket missing after slash with atan operator");
00842 
00843   if (read_real_expression (line, counter, &argument2, parameters)
00844       IS RS274NGC_ERROR)
00845     ERROR_MACRO_PASS(name);
00846 
00847   *double_ptr SET_TO atan2(*double_ptr, argument2); /* value in radians */
00848   *double_ptr SET_TO ((*double_ptr * 180.0)/PI);    /* convert to degrees */
00849   return RS274NGC_OK;
00850 }
00851 
00852 /****************************************************************************/
00853 
00854 /* read_integer_value
00855 
00856 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
00857    If read_real_value returns RS274NGC_ERROR or if the returned value is not
00858    close to an integer, this returns RS274NGC_ERROR. Otherwise, it
00859    returns RS274NGC_OK.
00860 
00861 Side effects:
00862    The number read from the line is put into what integer_ptr points at.
00863 
00864 Called by:
00865    read_d
00866    read_l
00867    read_m
00868    read_parameter
00869    read_parameter_setting
00870    read_tool
00871    read_tool_length_offset
00872 
00873 This reads an integer (positive, negative or zero) from a string,
00874 starting from the position given by *counter. The value being
00875 read may be written with a decimal point or it may be an expression
00876 involving non-integers, as long as the result comes out within 0.0001
00877 of an integer.
00878 
00879 This proceeds by calling read_real_value and checking that it is
00880 close to an integer, then returning the integer it is close to.
00881 
00882 */
00883 
00884 int read_integer_value(   /* ARGUMENT VALUES                                */
00885  char * line,             /* string: line of RS274/NGC code being processed */
00886  int * counter,           /* pointer to a counter for position on the line  */
00887  int * integer_ptr,       /* pointer to the value being read                */
00888  double * parameters)     /* array of system parameters                     */
00889 {
00890   static char name[] SET_TO "read_integer_value";
00891   double float_value;
00892 
00893   if (read_real_value(line, counter, &float_value, parameters)
00894       IS RS274NGC_ERROR)
00895     ERROR_MACRO_PASS(name);
00896   *integer_ptr SET_TO (int)floor(float_value);
00897   if ((float_value - *integer_ptr) > 0.9999)
00898     {
00899       *integer_ptr SET_TO (int)ceil(float_value);
00900     }
00901   else if ((float_value - *integer_ptr) > 0.0001)
00902     ERROR_MACRO(_interpreter_linetext, name, "Non-integer value for integer");
00903   return RS274NGC_OK;
00904 }
00905 
00906 /****************************************************************************/
00907 
00908 /* read_operation
00909 
00910 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
00911    If the operation is unknown or if the line ends without closing the
00912    expression, this returns RS274NGC_ERROR. Otherwise, it returns RS274NGC_OK.
00913 
00914 Side effects:
00915    An integer representing the operation is put into what operation points at.
00916    The counter is reset to point to the first character after the operation.
00917 
00918 Called by: read_real_expression
00919 
00920 */
00921 
00922 int read_operation(   /* ARGUMENT VALUES                                */
00923  char * line,         /* string: line of RS274/NGC code being processed */
00924  int * counter,       /* pointer to a counter for position on the line  */
00925  int * operation)     /* pointer to operation to be read                */
00926 {
00927   static char name[] SET_TO "read_operation";
00928   char c;
00929 
00930   c SET_TO line[*counter];
00931   *counter SET_TO (*counter + 1);
00932   switch(c)
00933     {
00934     case '+':
00935       *operation SET_TO PLUS;
00936       return RS274NGC_OK;
00937     case '-':
00938       *operation SET_TO MINUS;
00939       return RS274NGC_OK;
00940     case '/':
00941       *operation SET_TO DIVIDED_BY;
00942       return RS274NGC_OK;
00943     case '*':
00944       if(strncmp((line + *counter), "*", 1) IS 0)
00945         {
00946           *operation SET_TO POWER;
00947           *counter SET_TO (*counter + 1);
00948           return RS274NGC_OK;
00949         }
00950       else
00951         {
00952           *operation SET_TO TIMES;
00953           return RS274NGC_OK;
00954         }
00955     case ']':
00956       *operation SET_TO RIGHT_BRACKET;
00957       return RS274NGC_OK;
00958     case 'a':
00959       if(strncmp((line + *counter), "nd", 2) IS 0)
00960         {
00961           *operation SET_TO AND2;
00962           *counter SET_TO (*counter + 2);
00963           return RS274NGC_OK;
00964         }
00965       else
00966         ERROR_MACRO(_interpreter_linetext, name, "Unknown operation name starting with A");
00967     case 'm':
00968       if(strncmp((line + *counter), "od", 2) IS 0)
00969         {
00970           *operation SET_TO MODULO;
00971           *counter SET_TO (*counter + 2);
00972           return RS274NGC_OK;
00973         }
00974       else
00975         ERROR_MACRO(_interpreter_linetext, name, "Unknown operation name starting with M");
00976     case 'o':
00977       if(strncmp((line + *counter), "r", 1) IS 0)
00978         {
00979           *operation SET_TO NON_EXCLUSIVE_OR;
00980           *counter SET_TO (*counter + 1);
00981           return RS274NGC_OK;
00982         }
00983       else
00984         ERROR_MACRO(_interpreter_linetext, name, "Unknown operation name starting with O");
00985     case 'x':
00986       if(strncmp((line + *counter), "or", 2) IS 0)
00987         {
00988           *operation SET_TO EXCLUSIVE_OR;
00989           *counter SET_TO (*counter + 2);
00990           return RS274NGC_OK;
00991         }
00992       else
00993         ERROR_MACRO(_interpreter_linetext, name, "Unknown operation name starting with X");
00994     case 0:
00995       ERROR_MACRO(_interpreter_linetext, name, "Unclosed expression");
00996     default:
00997       ERROR_MACRO(_interpreter_linetext, name, "Unknown operation");
00998     }
00999 }
01000 
01001 /****************************************************************************/
01002 
01003 /* read_operation_unary
01004 
01005 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
01006    If the operation is not a known operation, this returns RS274NGC_ERROR.
01007    Otherwise, this returns RS274NGC_OK.
01008 
01009 Side effects:
01010    An integer code for the name of the operation read from the
01011    line is put into what operation points at.
01012    The counter is reset to point to the first character after the
01013    characters which make up the operation name.
01014 
01015 Called by:
01016    read_unary
01017 
01018 This attempts to read the name of a unary operation out of the line,
01019 starting at the index given by the counter.
01020 
01021 */
01022 
01023 int read_operation_unary( /* ARGUMENT VALUES                                */
01024  char * line,             /* string: line of RS274/NGC code being processed */
01025  int * counter,           /* pointer to a counter for position on the line  */
01026  int * operation)         /* pointer to operation to be read                */
01027 {
01028   static char name[] SET_TO "read_operation_unary";
01029   char c;
01030 
01031   c SET_TO line[*counter];
01032   *counter SET_TO (*counter + 1);
01033   switch (c)
01034     {
01035     case 'a':
01036       if(strncmp((line + *counter), "bs", 2) IS 0)
01037         {
01038           *operation SET_TO ABS;
01039           *counter SET_TO (*counter + 2);
01040           return RS274NGC_OK;
01041         }
01042       else if(strncmp((line + *counter), "cos", 3) IS 0)
01043         {
01044           *operation SET_TO ACOS;
01045           *counter SET_TO (*counter + 3);
01046           return RS274NGC_OK;
01047         }
01048       else if(strncmp((line + *counter), "sin", 3) IS 0)
01049         {
01050           *operation SET_TO ASIN;
01051           *counter SET_TO (*counter + 3);
01052           return RS274NGC_OK;
01053         }
01054       else if(strncmp((line + *counter), "tan", 3) IS 0)
01055         {
01056           *operation SET_TO ATAN;
01057           *counter SET_TO (*counter + 3);
01058           return RS274NGC_OK;
01059         }
01060       else
01061         ERROR_MACRO(_interpreter_linetext, name, "Unknown word starting with A");
01062     case 'c':
01063       if(strncmp((line + *counter), "os", 2) IS 0)
01064         {
01065           *operation SET_TO COS;
01066           *counter SET_TO (*counter + 2);
01067           return RS274NGC_OK;
01068         }
01069       else
01070         ERROR_MACRO(_interpreter_linetext, name, "Unknown word starting with C");
01071     case 'e':
01072       if(strncmp((line + *counter), "xp", 2) IS 0)
01073         {
01074           *operation SET_TO EXP;
01075           *counter SET_TO (*counter + 2);
01076           return RS274NGC_OK;
01077         }
01078       else
01079         ERROR_MACRO(_interpreter_linetext, name, "Unknown word starting with E");
01080     case 'f':
01081       if(strncmp((line + *counter), "ix", 2) IS 0)
01082         {
01083           *operation SET_TO FIX;
01084           *counter SET_TO (*counter + 2);
01085           return RS274NGC_OK;
01086         }
01087       else if(strncmp((line + *counter), "up", 2) IS 0)
01088         {
01089           *operation SET_TO FUP;
01090           *counter SET_TO (*counter + 2);
01091           return RS274NGC_OK;
01092         }
01093       else
01094         ERROR_MACRO(_interpreter_linetext, name, "Unknown word starting with F");
01095     case 'l':
01096       if(strncmp((line + *counter), "n", 1) IS 0)
01097         {
01098           *operation SET_TO LN;
01099           *counter SET_TO (*counter + 1);
01100           return RS274NGC_OK;
01101         }
01102       else
01103         ERROR_MACRO(_interpreter_linetext, name, "Unknown word starting with L");
01104     case 'r':
01105       if(strncmp((line + *counter), "ound", 4) IS 0)
01106         {
01107           *operation SET_TO ROUND;
01108           *counter SET_TO (*counter + 4);
01109           return RS274NGC_OK;
01110         }
01111       else
01112         ERROR_MACRO(_interpreter_linetext, name, "Unknown word starting with R");
01113     case 's':
01114       if(strncmp((line + *counter), "in", 2) IS 0)
01115         {
01116           *operation SET_TO SIN;
01117           *counter SET_TO (*counter + 2);
01118           return RS274NGC_OK;
01119         }
01120       else if(strncmp((line + *counter), "qrt", 3) IS 0)
01121         {
01122           *operation SET_TO SQRT;
01123           *counter SET_TO (*counter + 3);
01124           return RS274NGC_OK;
01125         }
01126       else
01127         ERROR_MACRO(_interpreter_linetext, name, "Unknown word starting with S");
01128     case 't':
01129       if(strncmp((line + *counter), "an", 2) IS 0)
01130         {
01131           *operation SET_TO TAN;
01132           *counter SET_TO (*counter + 2);
01133           return RS274NGC_OK;
01134         }
01135       else
01136         ERROR_MACRO(_interpreter_linetext, name, "Unknown word starting with T");
01137     default:
01138       ERROR_MACRO(_interpreter_linetext, name, "Unknown word where unary operation could be");
01139     }
01140 }
01141 
01142 /****************************************************************************/
01143 
01144 /* utility_find_turn
01145 
01146 Returned Value: double (angle between two radii of a circle)
01147 
01148 Side effects: none
01149 
01150 Called by:
01151    utility_find_arc_length
01152    convert_arc_comp1
01153    convert_arc_comp2
01154    convert_arc_xy
01155    convert_arc_yz
01156    convert_arc_zx
01157 
01158 */
01159 
01160 double utility_find_turn( /* ARGUMENT VALUES            */
01161  double x1,       /* X-coordinate of start point        */
01162  double y1,       /* Y-coordinate of start point        */
01163  double center_x, /* X-coordinate of arc center         */
01164  double center_y, /* Y-coordinate of arc center         */
01165  int turn,        /* no. of full or partial circles CCW */
01166  double x2,       /* X-coordinate of end point          */
01167  double y2)       /* Y-coordinate of end point          */
01168 {
01169   double alpha;
01170   double beta;
01171   double theta;  /* amount of turn of arc CCW - negative if CW */
01172 
01173   if (turn IS 0)
01174     return 0.0;
01175   alpha SET_TO atan2((y1 - center_y), (x1 - center_x));
01176   beta SET_TO atan2((y2 - center_y), (x2 - center_x));
01177   if (turn > 0)
01178     {
01179       if (beta <= alpha)
01180         beta SET_TO (beta + TWO_PI);
01181       theta SET_TO ((beta - alpha) + ((turn - 1) * TWO_PI));
01182     }
01183   else /* turn < 0 */
01184     {
01185       if (alpha <= beta)
01186         alpha SET_TO (alpha + TWO_PI);
01187       theta SET_TO ((beta - alpha) + ((turn + 1) * TWO_PI));
01188     }
01189   return (theta);
01190 }
01191 
01192 /****************************************************************************/
01193 
01194 /* arc_data_comp_ijk
01195 
01196 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
01197    If any of the following errors occur, this returns RS274NGC_ERROR.
01198    Otherwise, it returns RS274NGC_OK.
01199    1. The two calculable values of the radius differ by more than
01200       tolerance.
01201    2. BUG - move is not G_2 or G_3.
01202 
01203 Side effects:
01204    This finds and sets the values of center_x, center_y, and turn.
01205 
01206 Called by: convert_arc_comp1
01207 
01208 This finds the center coordinates and number of full or partial turns
01209 counterclockwise of a helical or circular arc (call it arc1) in
01210 ijk-format in the XY plane.  Arc2 is constructed so that it is tangent
01211 to a circle whose radius is tool_radius and whose center is at the
01212 point (current_x, current_y) and passes through the point (end_x,
01213 end_y). Arc1 has the same center as arc2. The radius of arc1 is one
01214 tool radius larger or smaller than the radius of arc2.
01215 
01216 */
01217 
01218 int arc_data_comp_ijk(  /* ARGUMENT VALUES                               */
01219  int move,           /* either G_2 (cw arc) or G_3 (ccw arc)             */
01220  int side,           /* either RIGHT or LEFT                             */
01221  double tool_radius, /* radius of the tool                               */
01222  double current_x,   /* first coordinate of current point                */
01223  double current_y,   /* second coordinate of current point               */
01224  double end_x,       /* first coordinate of arc end point                */
01225  double end_y,       /* second coordinate of arc end point               */
01226  double i_number,    /* first coordinate offset of center from current   */
01227  double j_number,    /* second coordinate offset of center from current  */
01228  double * center_x,  /* pointer to first coordinate of center of arc     */
01229  double * center_y,  /* pointer to second coordinate of center of arc    */
01230  int * turn,         /* pointer to number of full or partial circles CCW */
01231  double tolerance)   /* tolerance of differing radii                     */
01232 {
01233   static char name[] SET_TO "arc_data_comp_ijk";
01234   double arc_radius;
01235   double radius2;
01236   *center_x SET_TO (current_x + i_number);
01237   *center_y SET_TO (current_y + j_number);
01238   arc_radius SET_TO hypot((*center_x - current_x), (*center_y - current_y));
01239   radius2 SET_TO hypot((*center_x - end_x), (*center_y - end_y));
01240   radius2 SET_TO
01241     (((side IS LEFT ) AND (move IS 30)) OR
01242      ((side IS RIGHT) AND (move IS 20))) ?
01243        (radius2 - tool_radius): (radius2 + tool_radius);
01244   if (fabs(arc_radius - radius2) > tolerance)
01245     ERROR_MACRO(_interpreter_linetext, name,
01246                 "Radius to end of arc differs from radius to start of arc");
01247     /* This catches an arc too small for the tool, also */
01248   if (move IS G_2)
01249     *turn SET_TO -1;
01250   else if (move IS G_3)
01251     *turn SET_TO 1;
01252   else
01253     BUG_MACRO(name, "Code is not G2 or G3 in arc_data_comp");
01254   return RS274NGC_OK;
01255 }
01256 
01257 /****************************************************************************/
01258 /* arc_data_comp_r
01259 
01260 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
01261    If any of the following errors occur, this returns RS274NGC_ERROR.
01262    Otherwise, it returns RS274NGC_OK.
01263    1. The arc radius is too small to reach the end point.
01264    2. The current point is the same as the end point of the arc (so that
01265       it is not possible to locate the center of the circle).
01266    3. The arc radius is not greater than the tool_radius.
01267 
01268 Side effects:
01269    This finds and sets the values of center_x, center_y, and turn.
01270 
01271 Called by: convert_arc_comp1
01272 
01273 This finds the center coordinates and number of full or partial turns
01274 counterclockwise of a helical or circular arc (call it arc1) in
01275 r-format in the XY plane.  Arc2 is constructed so that it is tangent
01276 to a circle whose radius is tool_radius and whose center is at the
01277 point (current_x, current_y) and passes through the point (end_x,
01278 end_y). Arc1 has the same center as arc2. The radius of arc1 is one
01279 tool radius larger or smaller than the radius of arc2.
01280 
01281 If the value of the big_radius argument is negative, that means [NCMS,
01282 page 21] that an arc larger than a semicircle is to be made.
01283 Otherwise, an arc of a semicircle or less is made.
01284 
01285 The algorithm implemented here is to construct a line L from the
01286 current point to the end point, and a perpendicular to it from the
01287 center of the arc which intersects L at point P. Since the distance
01288 from the end point to the center and the distance from the current
01289 point to the center are known, two equations for the length of the
01290 perpendicular can be written. The right sides of the equations can be
01291 set equal to one another and the resulting equation solved for the
01292 length of the line from the current point to P. Then the location of
01293 P, the length of the perpendicular, the angle of the perpendicular,
01294 and the location of the center, can be found in turn.
01295 
01296 */
01297 
01298 int arc_data_comp_r( /* ARGUMENT VALUES                                  */
01299  int move,           /* either G_2 (cw arc) or G_3 (ccw arc)             */
01300  int side,           /* either RIGHT or LEFT                             */
01301  double tool_radius, /* radius of the tool                               */
01302  double current_x,   /* first coordinate of current point                */
01303  double current_y,   /* second coordinate of current point               */
01304  double end_x,       /* first coordinate of arc end point                */
01305  double end_y,       /* second coordinate of arc end point               */
01306  double big_radius,  /* radius of arc                                    */
01307  double * center_x,  /* pointer to first coordinate of center of arc     */
01308  double * center_y,  /* pointer to second coordinate of center of arc    */
01309  int * turn)         /* pointer to number of full or partial circles CCW */
01310 {
01311   static char name[] SET_TO "arc_data_comp_r";
01312   double abs_radius; /* absolute value of big_radius */
01313   double radius2;    /* distance from center to current point */
01314   double distance;   /* length of line L from current to end */
01315   double mid_length; /* length from current point to point P */
01316   double offset;     /* length of line from P to center */
01317   double alpha;      /* direction of line from current to end */
01318   double theta;      /* direction of line from P to center */
01319   double mid_x;      /* x-value of point P */
01320   double mid_y;      /* y-value of point P */
01321 
01322   if ((end_x IS current_x) AND (end_y IS current_y))
01323     ERROR_MACRO(_interpreter_linetext, name, "Current point same as end point of arc");
01324   abs_radius SET_TO fabs(big_radius);
01325   if ((abs_radius < tool_radius) AND (((side IS LEFT ) AND (move IS G_3)) OR
01326                                       ((side IS RIGHT) AND (move IS G_2))))
01327     ERROR_MACRO(_interpreter_linetext, name,
01328      "Tool radius not less than arc radius with cutter radius comp");
01329 
01330   distance SET_TO hypot((end_x - current_x), (end_y - current_y));
01331   alpha SET_TO atan2 ((end_y - current_y), (end_x - current_x));
01332   theta SET_TO (((move IS G_3) AND (big_radius > 0)) OR
01333                 ((move IS G_2) AND (big_radius < 0))) ?
01334                   (alpha + PI2) : (alpha - PI2);
01335   radius2 SET_TO (((side IS LEFT ) AND (move IS G_3)) OR
01336                   ((side IS RIGHT) AND (move IS G_2))) ?
01337                     (abs_radius - tool_radius) : (abs_radius + tool_radius);
01338   if (distance > (radius2 + abs_radius))
01339     ERROR_MACRO(_interpreter_linetext, name, "Radius too small to reach end point");
01340   mid_length SET_TO (((radius2 * radius2) + (distance * distance) -
01341                       (abs_radius * abs_radius)) / (2.0 * distance));
01342   mid_x SET_TO (current_x + (mid_length * cos(alpha)));
01343   mid_y SET_TO (current_y + (mid_length * sin(alpha)));
01344   offset SET_TO sqrt((radius2 * radius2) - (mid_length * mid_length));
01345   *center_x SET_TO mid_x + (offset * cos(theta));
01346   *center_y SET_TO mid_y + (offset * sin(theta));
01347   *turn SET_TO (move IS G_2) ? -1 : 1;
01348 
01349   return RS274NGC_OK;
01350 }
01351 
01352 /****************************************************************************/
01353 /* arc_data_ijk
01354 
01355 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
01356    If any of the following errors occur, this returns RS274NGC_ERROR.
01357    Otherwise, it returns RS274NGC_OK.
01358    1. The two calculable values of the radius differ by more than
01359       tolerance.
01360    2. BUG - The move code is not G_2 or G_3.
01361 
01362 Side effects:
01363    This finds and sets the values of center_x, center_y, and turn.
01364 
01365 Called by:
01366    convert_arc_xy
01367    convert_arc_xz
01368    convert_arc_yz
01369    convert_arc_comp2
01370 
01371 This finds the center coordinates and number of full or partial turns
01372 counterclockwise of a helical or circular arc in ijk-format. This
01373 function is used by the arc functions for all three planes, so "x" and
01374 "y" really mean "first_coordinate" and "second_coordinate" wherever
01375 they are used here as suffixes of variable names. The i and j prefixes
01376 are handled similarly.
01377 
01378 */
01379 
01380 int arc_data_ijk(   /* ARGUMENT VALUES                                 */
01381  int move,          /* either G_2 (cw arc) or G_3 (ccw arc)            */
01382  double current_x,  /* first coordinate of current point               */
01383  double current_y,  /* second coordinate of current point              */
01384  double end_x,      /* first coordinate of arc end point               */
01385  double end_y,      /* second coordinate of arc end point              */
01386  double i_number,   /* first coordinate offset of center from current  */
01387  double j_number,   /* second coordinate offset of center from current */
01388  double * center_x, /* pointer to first coordinate of center of arc    */
01389  double * center_y, /* pointer to second coordinate of center of arc   */
01390  int * turn,        /* pointer to no. of full or partial circles CCW   */
01391  double tolerance)  /* tolerance of differing radii                    */
01392 {
01393   static char name[] SET_TO "arc_data_ijk";
01394   double radius;    /* radius to current point */
01395   double radius2;   /* radius to end point     */
01396   *center_x SET_TO (current_x + i_number);
01397   *center_y SET_TO (current_y + j_number);
01398   radius SET_TO hypot((*center_x - current_x), (*center_y - current_y));
01399   radius2 SET_TO hypot((*center_x - end_x), (*center_y - end_y));
01400   if (fabs(radius - radius2) > tolerance)
01401     ERROR_MACRO(_interpreter_linetext, name,
01402                 "Radius to end of arc differs from radius to start of arc");
01403   if (move IS G_2)
01404     *turn SET_TO -1;
01405   else if (move IS G_3)
01406     *turn SET_TO 1;
01407   else
01408     BUG_MACRO(name, "Code is not G2 or G3 in arc_data_ijk");
01409   return RS274NGC_OK;
01410 }
01411 
01412 /****************************************************************************/
01413 /* arc_data_r
01414 
01415 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
01416    If any of the following errors occur, this returns RS274NGC_ERROR.
01417    Otherwise, it returns RS274NGC_OK.
01418    1. The radius is too small to reach the end point.
01419    2. The current point is the same as the end point of the arc
01420       (so that it is not possible to locate the center of the circle).
01421 
01422 Side effects:
01423    This finds and sets the values of center_x, center_y, and turn.
01424 
01425 Called by:
01426    convert_arc_xy
01427    convert_arc_xz
01428    convert_arc_yz
01429    convert_arc_comp2
01430 
01431 This finds the center coordinates and number of full or partial turns
01432 counterclockwise of a helical or circular arc in the r format. This
01433 function is used by the arc functions for all three planes, so "x" and
01434 "y" really mean "first_coordinate" and "second_coordinate" wherever
01435 they are used here as suffixes of variable names.
01436 
01437 If the value of the radius argument is negative, that means [NCMS,
01438 page 21] that an arc larger than a semicircle is to be made.
01439 Otherwise, an arc of a semicircle or less is made.
01440 
01441 The algorithm used here is based on finding the midpoint M of the line
01442 L between the current point and the end point of the arc. The center
01443 of the arc lies on a line through M perpendicular to L.
01444 
01445 */
01446 
01447 int arc_data_r(     /* ARGUMENT VALUES                               */
01448  int move,          /* either G_2 (cw arc) or G_3 (ccw arc)          */
01449  double current_x,  /* first coordinate of current point             */
01450  double current_y,  /* second coordinate of current point            */
01451  double end_x,      /* first coordinate of arc end point             */
01452  double end_y,      /* second coordinate of arc end point            */
01453  double radius,     /* radius of arc                                 */
01454  double * center_x, /* pointer to first coordinate of center of arc  */
01455  double * center_y, /* pointer to second coordinate of center of arc */
01456  int * turn)        /* pointer to no. of full or partial circles CCW */
01457 {
01458   static char name[] SET_TO "arc_data_r";
01459   double abs_radius; /* absolute value of given radius */
01460   double half_length;
01461   double turn2;  /* absolute value of half of turn */
01462   double offset; /* distance from M to center */
01463   double theta;  /* angle of line from M to center */
01464   double mid_x;  /* first coordinate of M */
01465   double mid_y;  /* second coordinate of M */
01466 
01467   if ((end_x IS current_x) AND (end_y IS current_y))
01468     ERROR_MACRO(_interpreter_linetext, name, "Current point same as end point of arc");
01469   abs_radius SET_TO fabs(radius);
01470   mid_x SET_TO (end_x + current_x)/2.0;
01471   mid_y SET_TO (end_y + current_y)/2.0;
01472   half_length SET_TO hypot((mid_x - end_x), (mid_y - end_y));
01473   if ((half_length/abs_radius) > (1+TINY))
01474     ERROR_MACRO(_interpreter_linetext, name, "Arc radius too small to reach end point");
01475   else if ((half_length/abs_radius) > (1-TINY))
01476     half_length SET_TO abs_radius; /* allow a small error for semicircle */
01477                                    /* check needed before calling asin   */
01478   if (((move IS G_2) AND (radius > 0)) OR
01479       ((move IS G_3) AND (radius < 0)))
01480     theta SET_TO atan2((end_y - current_y), (end_x - current_x)) - PI2;
01481   else
01482     theta SET_TO atan2((end_y - current_y), (end_x - current_x)) + PI2;
01483 
01484   turn2 SET_TO asin (half_length/abs_radius);
01485   offset SET_TO abs_radius * cos(turn2);
01486   *center_x SET_TO mid_x + (offset * cos(theta));
01487   *center_y SET_TO mid_y + (offset * sin(theta));
01488   *turn SET_TO (move IS G_2) ? -1 : 1;
01489 
01490   return RS274NGC_OK;
01491 }
01492 
01493 /****************************************************************************/
01494 /* read_parameter
01495 
01496 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
01497    If any of the following errors occur, this returns RS274NGC_ERROR.
01498    Otherwise, this returns RS274NGC_OK.
01499    1. BUG - The first character read is not # .
01500    2. The characters following the # do not form an integer.
01501    3. The parameter number is out of bounds
01502 
01503 Side effects:
01504    The value of the given parameter is put into what double_ptr points at.
01505    The counter is reset to point to the first character after the
01506    characters which make up the value.
01507 
01508 Called by:
01509    read_real_value
01510 
01511 This attempts to read the value of a parameter out of the line,
01512 starting at the index given by the counter.
01513 
01514 According to the manual [NCMS, p. 62], the characters following
01515 # may be any "parameter expression". Thus, the following are legal
01516 and mean the same thing (the value of the parameter whose number is
01517 stored in parameter 2):
01518   ##2
01519   #[#2]
01520 
01521 */
01522 
01523 int read_parameter(   /* ARGUMENT VALUES                                */
01524  char * line,         /* string: line of RS274/NGC code being processed */
01525  int * counter,       /* pointer to a counter for position on the line  */
01526  double * double_ptr, /* pointer to double to be read                   */
01527  double * parameters) /* array of system parameters                     */
01528 {
01529   static char name[] SET_TO "read_parameter";
01530   int index;
01531 
01532   if (line[*counter] ISNT '#')
01533     BUG_MACRO(name, "Read_parameter should not have been called");
01534   *counter SET_TO (*counter + 1);
01535   if (read_integer_value(line, counter, &index, parameters) IS RS274NGC_ERROR)
01536     ERROR_MACRO_PASS(name);
01537   else if ((index < 1) OR (index >= RS274NGC_MAX_PARAMETERS))
01538     ERROR_MACRO(_interpreter_linetext, name, "Parameter number out of range");
01539   else
01540     {
01541       *double_ptr SET_TO parameters[index];
01542       return RS274NGC_OK;
01543     }
01544 }
01545 
01546 /****************************************************************************/
01547 
01548 /* read_real_expression
01549 
01550 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
01551    If any of the following errors occur, this returns RS274NGC_ERROR.
01552    Otherwise, it returns RS274NGC_OK.
01553    1. BUG - The first character is not [ .
01554    2. The call back to read_real_value does not return RS274NGC_OK.
01555    3. read_operation does not return RS274NGC_OK.
01556    4. execute_binary1 or execute_binary2 does not return RS274NGC_OK.
01557 
01558 Side effects:
01559    The number read from the line is put into what value_ptr points at.
01560    The counter is reset to point to the first character after the real
01561    expression.
01562 
01563 Called by: read_real_value.
01564 
01565 Example 1: [2 - 3 * 4 / 5] means [2 - [[3 * 4] / 5]] and equals -0.4.
01566 
01567 Segmenting Expressions -
01568 
01569 The manual [NCMS, section 3.5.1.1, page 50] provides for
01570 using square brackets to segment expressions.
01571 
01572 Binary Operations -
01573 
01574 The manual [NCMS section 3.5.1.1] discusses expression evaluation.
01575 The manual provides for eight binary operations: the four basic
01576 mathematical operations (addition, subtraction, multiplication,
01577 division), three logical operations (non-exclusive OR, exclusive OR,
01578 and AND2) and the modulus operation. The manual does not explicitly call
01579 these "binary" operations, but implicitly recognizes that they are
01580 binary. We have added the "power" operation of raising the number
01581 on the left of the operation to the power on the right; this is
01582 needed for many basic machining calculations.
01583 
01584 There are two groups of binary operations given in the manual. If
01585 operations are strung together as shown in Example 1, operations in
01586 the first group are to be performed before operations in the second
01587 group. If an expression contains more than one operation from the same
01588 group (such as * and / in Example 1), the operation on the left is
01589 performed first. The first group is: multiplication (*), division (/),
01590 modulus (MOD), and power (**). We added power. The second group is:
01591 addition(+), subtraction (-), logical non-exclusive OR (OR), logical
01592 exclusive OR (XOR), and logical AND (AND2).
01593 
01594 The logical operations and modulus are apparently to be performed on
01595 any real numbers, not just on integers or on some other data type.
01596 
01597 Unary Operations -
01598 
01599 The manual [NCMS, section 3.5.1.2] provides for fifteen unary
01600 mathematical operations. Two of these, BIN and BCD, are apparently for
01601 converting between decimal and hexadecimal number representation,
01602 although the text is not clear. These have not been implemented, since
01603 we are not using any hexadecimal numbers. The other thirteen unary
01604 operations have been implemented: absolute_value, arc_cosine, arc_sine,
01605 arc_tangent, cosine, e_raised_to, fix_down, fix_up, natural_log_of,
01606 round, sine, square_root, tangent .
01607 
01608 The manual [NCMS, section 3.5.1.2, page 51] requires the argument to
01609 all unary operations (except atan) to be in square brackets.  Thus,
01610 for example "sin[90]" is allowed in the interpreter, but "sin 90" is
01611 not. The atan operation must be in the format "atan[..]/[..]".
01612 
01613 Production Rule Definitions in Terms of Tokens -
01614 
01615 The following is a production rule definition of what this RS274NGC
01616 interpreter recognizes as valid combinations of symbols which form a
01617 recognized real_value (the top of this production hierarchy).
01618 
01619 The notion of "integer_value" is used in the interpreter. Below it is
01620 defined as a synonym for real_value, but in fact a constraint is added
01621 which cannot be readily written in a production language.  An
01622 integer_value is a real_value which is very close to an integer.
01623 Integer_values are needed for array and table indices and (when
01624 divided by 10) for the values of M codes and G codes. All numbers
01625 (including integers) are read as real numbers and stored as doubles.
01626 If an integer_value is required in some situation, a test for being
01627 close to an integer is applied to the number after it is read.
01628 
01629 
01630 arc_tangent_combo = arc_tangent expression divided_by expression .
01631 
01632 binary_operation1 = divided_by | modulo | power | times .
01633 
01634 binary_operation2 = and | exclusive_or | minus |  non_exclusive_or | plus .
01635 
01636 combo1 = real_value { binary_operation1 real_value } .
01637 
01638 digit = zero | one | two | three | four | five | six | seven |eight | nine .
01639 
01640 expression =
01641    left_bracket
01642    (unary_combo | (combo1 { binary_operation2 combo1 }))
01643    right_bracket .
01644 
01645 integer_value = real_value .
01646 
01647 ordinary_unary_combo =  ordinary_unary_operation expression .
01648 
01649 ordinary_unary_operation =
01650    absolute_value | arc_cosine | arc_sine | cosine | e_raised_to |
01651    fix_down | fix_up | natural_log_of | round | sine | square_root | tangent .
01652 
01653 parameter_index = integer_value .
01654 
01655 parameter_value = parameter_sign  parameter_index .
01656 
01657 real_number =
01658    [ plus | minus ]
01659    (( digit { digit } decimal_point {digit}) | ( decimal_point digit {digit})).
01660 
01661 real_value =
01662    real_number | expression | parameter_value | unary_combo.
01663 
01664 unary_combo = ordinary_unary_combo | arc_tangent_combo .
01665 
01666 
01667 Production Tokens in Terms of Characters -
01668 
01669 absolute_value   = 'abs'
01670 and              = 'and'
01671 arc_cosine       = 'acos'
01672 arc_sine         = 'asin'
01673 arc_tangent      = 'atan'
01674 cosine           = 'cos'
01675 decimal_point    = '.'
01676 divided_by       = '/'
01677 eight            = '8'
01678 exclusive_or     = 'xor'
01679 e_raised_to      = 'exp'
01680 five             = '5'
01681 fix_down         = 'fix'
01682 fix_up           = 'fup'
01683 four             = '4'
01684 left_bracket     = '['
01685 minus            = '-'
01686 modulo           = 'mod'
01687 natural_log_of   = 'ln'
01688 nine             = '9'
01689 non_exclusive_or = 'or'
01690 one              = '1'
01691 parameter_sign   = '#'
01692 plus             = '+'
01693 power            = '**'
01694 right_bracket    = ']'
01695 round            = 'round'
01696 seven            = '7'
01697 sine             = 'sin'
01698 six              = '6'
01699 square_root      = 'sqrt'
01700 tangent          = 'tan'
01701 three            = '3'
01702 times            = '*'
01703 two              = '2'
01704 zero             = '0'
01705 
01706 How the Function Works -
01707 
01708 When this function is called, the counter should be set at a left
01709 bracket. The function reads up to and including the right bracket
01710 which closes the expression.
01711 
01712 In this explanation we will use "bop1" to mean "binary_operation1"
01713 as defined above, and "bop2" to mean "binary_operation2". "bop"
01714 will mean an operation that is either a bop1 or a bop2.
01715 
01716 The basic form of an expression is: [v1 bop v2 bop ... vn], where
01717 the vi are real_values. Because bop1's are to be evaluated before
01718 bop2's, it is useful to rewrite the general form collecting any
01719 subsequences of bop1's. For example, suppose the expression is:
01720 [9+8*7/6+5-4*3*2+1]. It may be rewritten as: [9+(8*7/6)+5-(4*3*2)+1].
01721 
01722 The manual provides that operations of the same type should be processed
01723 left to right. The algorithm implemented here works through the
01724 expression left to right performing bop2's one at time and collecting
01725 the results in a buffer named hold2. When a subsequence of the
01726 expression containing bop1's is encountered, the bop1's in the
01727 subsequence are performed one at a time and the results are collected
01728 in a buffer named hold1, until the subsequence ends, at which point
01729 the hold1 buffer is combined with the hold2 buffer using the bop1
01730 that immediately preceeded the subsequence.
01731 
01732 An expression may be processed left to right by using a loop that
01733 reads one more real_value and one more bop each time around the loop.
01734 It is necessary for the reading to be ahead of the execution of bop's
01735 by one step in order to tell when subsequences of bop1's start and end.
01736 This algorithm implements such a loop. It keeps track of the following
01737 information.
01738 
01739 1. the value accumulated so far in hold2 (initialized to 0).
01740 2. the bop2 waiting to be executed (called waiting_operation
01741    and initialized to PLUS).
01742 3. the value accumulated so far in hold1.
01743 4. the bop which was read the last time around the loop
01744    (called last_operation and initialized to PLUS).
01745 5. the value read this time around the loop (called value).
01746 6. the bop read this time around the loop (called next_operation).
01747 
01748 The function terminates when the next_operation is found to be a right
01749 bracket and not a bop. When the function is ready to return, hold2
01750 has been set to the value of the expression, and RS274NGC_OK is returned.
01751 
01752 Without the initializations, special steps would have to be taken
01753 the first time around the loop, and special steps would have to
01754 be taken if the expression were simply a number in brackets (e.g. [5]).
01755 
01756 The algorithm (ignoring all the error detection) is:
01757 
01758 A. Initialize.
01759 B. Loop
01760   B1. Read the next operation into next_operation and the next
01761       real_value into value.
01762   B2. Check for one of three possibilities regarding next_operation:
01763       B2a. If next_operation is a right bracket:
01764           B2a(i). If last_operation was a bop1, a subsequence
01765                   of bop1's has just ended, so set hold1
01766                   to (hold1 last_operation value) and then
01767                   set hold2 to (hold2 waiting_operation hold1).
01768           B2a(ii). Otherwise, last_operation must have been a bop2,
01769                   so set hold2 to (hold2 waiting_operation value).
01770           B2a(iii). Return RS274NGC_OK.
01771       B2b. Otherwise, if next_operation is a bop1:
01772           B2b(i). If last_operation was a bop2, a subsequence of
01773                   bop1's is just starting, so reinitialize hold1
01774                   by setting it to value.
01775           B2b(ii). Otherwise, last operation must have been a bop1,
01776                   implying a subsequence of bop1's has already been
01777                   started, so set hold1 to (hold1 last_operation value).
01778       B2c. Otherwise, next_operation must have been a bop2, so:
01779           B2c(i). If last_operation was a bop1, a subsequence of
01780                   bop1's has just ended, so set hold1 to
01781                   (hold1 last_operation value), set hold2 to
01782                   (hold2 waiting_operation hold1), and set
01783                   waiting_operation to next_operation.
01784           B2c(ii). Otherwise, last_operation was also a bop2, so
01785                   set hold2 to (hold2 last_operation value), and
01786                   set waiting_operation to next_operation.
01787   B3. Set last_operation to next_operation, and go back to the top of
01788       the loop.
01789 
01790 The expression [2 - 3 * 4 / 5] given in Example 1 is processed as
01791 follows by this procedure:
01792 
01793 - initialize hold2 to 0.0, last_operation to PLUS, and
01794   waiting_operation to PLUS.
01795 
01796 LOOP1
01797 - read 2 into value and MINUS into next_operation.
01798 - since next_operation is a bop2, and last_operation is a bop2,
01799   set hold2 to the result of applying the waiting_operation
01800   to hold2 and value (i.e. set hold2 to 0+2 = 2)
01801 - reset last_operation to MINUS and waiting_operation to MINUS.
01802 
01803 LOOP2
01804 - read 3 into value and TIMES into next_operation.
01805 - since next_operation is a bop1 and last_operation is a bop2,
01806   set hold1 to 3.
01807 - set last_operation to TIMES (but do not change waiting_operation).
01808 
01809 LOOP3
01810 - read 4 into value and DIVIDED_BY into next_operation.
01811 - since next_operation is a bop1 and last operation is a bop1,
01812   set hold1 to the result of applying last operation to
01813   hold1 and value (i.e. set hold1 to 3*4 = 12)
01814 - reset last operation to DIVIDED_BY
01815 
01816 LOOP4
01817 - read 5 into value and RIGHT_BRACKET into next_operation
01818 - since next_operation is RIGHT_BRACKET and last operation was a bop1,
01819   o set hold1 to the result of applying the last_operation
01820     to hold1 and value (i.e. set hold1 to 12/5 = 2.4)
01821   o set hold2 to the result of applying the waiting_operation
01822     to hold2 and hold1 (i.e. set hold2 to 2-2.4 = -0.4)
01823   o return RS274NGC_OK
01824 
01825 The following table shows the settings of the variables at the end
01826 of initialization and the end of each loop.
01827 
01828        hold2  hold1  value  waiting_     last_     next_      unread part
01829                             operation  operation  operation  of expression
01830       _____________________________________________________________________
01831 
01832 INIT    0       ?      ?     PLUS       PLUS         ?       2 - 3 * 4 / 5]
01833 
01834 LOOP1   2.0     ?      2.0   MINUS      MINUS      MINUS         3 * 4 / 5]
01835 
01836 LOOP2   2.0     3.0    3.0   MINUS      TIMES      TIMES             4 / 5]
01837 
01838 LOOP3   2.0    12.0    4.0   MINUS    DIVIDED_BY  DIVIDED_BY             5]
01839 
01840 LOOP4  -0.4     2.4    5.0   MINUS    DIVIDED_BY  RIGHT_BRACKET
01841 
01842 
01843 A reasonable alternative to the algorithm here would be to have
01844 read_real_expression deal with only bop2's and have it call a separate
01845 function to handle any subsequences of bop1's that might be found.
01846 (or the two functions could be written to call each other
01847 recursively). In either case, the read-ahead would still be necessary,
01848 so the functions would have either to pass the read-ahead information
01849 back and forth (OK) or to read things twice (bad).
01850 
01851 */
01852 
01853 int read_real_expression( /* ARGUMENT VALUES                                */
01854  char * line,             /* string: line of RS274/NGC code being processed */
01855  int * counter,           /* pointer to a counter for position on the line  */
01856  double * hold2,          /* pointer to double to be read                   */
01857  double * parameters)     /* array of system parameters                     */
01858 {
01859   static char name[] SET_TO "read_real_expression";
01860   double value;
01861   double hold1;
01862   int waiting_operation;  /* always a bop2 */
01863   int last_operation;
01864   int next_operation;
01865 
01866   if (line[*counter] ISNT '[')
01867     BUG_MACRO(name, "Read_real_expression should not have been called");
01868   *counter SET_TO (*counter + 1);
01869   waiting_operation SET_TO PLUS;
01870   *hold2 SET_TO 0.0;
01871   last_operation SET_TO PLUS;
01872 
01873   for (;;)
01874     {
01875       if (read_real_value(line, counter, &value, parameters) ISNT RS274NGC_OK)
01876         ERROR_MACRO_PASS(name);
01877       if (read_operation(line, counter, &next_operation) ISNT RS274NGC_OK)
01878         ERROR_MACRO_PASS(name);
01879       if (next_operation IS RIGHT_BRACKET)
01880         {
01881           if (last_operation < AND2)
01882             {
01883               if (execute_binary1(&hold1,last_operation, &value)
01884                   ISNT RS274NGC_OK)
01885                 ERROR_MACRO_PASS(name);
01886               if (execute_binary2(hold2, waiting_operation, &hold1)
01887                   ISNT RS274NGC_OK)
01888                 ERROR_MACRO_PASS(name);
01889             }
01890           else if (execute_binary2(hold2, waiting_operation, &value)
01891                    ISNT RS274NGC_OK)
01892             ERROR_MACRO_PASS(name);
01893           else {}
01894           return RS274NGC_OK;
01895         }
01896       else if (next_operation < AND2)  /* next operation is a bop1 */
01897         {
01898           if (last_operation >= AND2)
01899             {
01900               hold1 SET_TO value;
01901             }
01902           else if (execute_binary1(&hold1, last_operation, &value)
01903                    ISNT RS274NGC_OK)
01904             ERROR_MACRO_PASS(name);
01905         }
01906       else                            /* next operation is a bop2 */
01907         {
01908           if (last_operation < AND2)
01909             {
01910               if (execute_binary1(&hold1, last_operation, &value)
01911                   ISNT RS274NGC_OK)
01912                 ERROR_MACRO_PASS(name);
01913               if (execute_binary2(hold2, waiting_operation, &hold1)
01914                   ISNT RS274NGC_OK)
01915                 ERROR_MACRO_PASS(name);
01916             }
01917           else
01918             {
01919               if (execute_binary2(hold2, waiting_operation, &value)
01920                   ISNT RS274NGC_OK)
01921                 ERROR_MACRO_PASS(name);
01922             }
01923           waiting_operation SET_TO next_operation;
01924         }
01925       last_operation SET_TO next_operation;
01926     }
01927 }
01928 
01929 /****************************************************************************/
01930 
01931 /* read_real_number
01932 
01933 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
01934    If any of the following errors occur, this returns RS274NGC_ERROR.
01935    Otherwise, it returns RS274NGC_OK.
01936    1. The first character is not "+", "-", "." or a digit.
01937    2. No digits are found after the first character and before the
01938       end of the line or the next character that cannot be part of a real.
01939    3. BUG - sscanf fails.
01940 
01941 Side effects:
01942    The number read from the line is put into what double_ptr points at.
01943    The counter is reset to point to the first character after the real.
01944 
01945 Called by:
01946    read_real_value
01947 
01948 This attempts to read a number out of the line, starting at the index
01949 given by the counter. It stops when the first character that cannot
01950 be part of the number is found.
01951 
01952 The first character may be a digit, "+", "-", or "."
01953 Every following character must be a digit or "." up to anything
01954 that is not a digit or "." (a second "." terminates reading).
01955 
01956 This function is not called if the first character is NULL, so it is
01957 not necessary to check that.
01958 
01959 The temporary insertion of a NULL character on the line is to avoid
01960 making a format string like "%3lf" which the LynxOS compiler cannot
01961 handle.
01962 
01963 */
01964 
01965 int read_real_number( /* ARGUMENT VALUES                                */
01966  char * line,         /* string: line of RS274/NGC code being processed */
01967  int * counter,       /* pointer to a counter for position on the line  */
01968  double * double_ptr) /* pointer to double to be read                   */
01969 {
01970   static char name[] SET_TO "read_real_number";
01971   int n;
01972   char c;
01973   int flag_point;  /* set to ON if decimal point found */
01974   int flag_digit; /* set to ON if digit found */
01975 
01976   n SET_TO *counter;
01977   flag_point SET_TO OFF;
01978   flag_digit SET_TO OFF;
01979 
01980 /* check first character */
01981   c SET_TO line[n];
01982   if (c IS '+')
01983     {
01984       *counter SET_TO (*counter + 1); /* skip plus sign */
01985       n++;
01986     }
01987   else if (c IS '-')
01988     {
01989       n++;
01990     }
01991   else if ((c ISNT '.') AND ((c < 48) OR (c > 57)))
01992     ERROR_MACRO(_interpreter_linetext, name, "Bad number format");
01993 
01994 /* check out rest of characters (must be digit or decimal point) */
01995   for (; (c SET_TO line[n]) ISNT (char) NULL; n++)
01996     {
01997       if (( 47 < c) AND ( c < 58))
01998         {
01999           flag_digit SET_TO ON;
02000         }
02001       else if (c IS '.')
02002         {
02003           if (flag_point IS OFF)
02004             {
02005               flag_point SET_TO ON;
02006             }
02007           else
02008             break;
02009         }
02010       else
02011         break;
02012     }
02013 
02014   if (flag_digit IS OFF)
02015     ERROR_MACRO(_interpreter_linetext, name, "No digits found where real number should be");
02016 
02017   line[n] SET_TO (char) NULL; /* temporary string termination for sscanf */
02018   if (sscanf(line + *counter, "%lf", double_ptr) IS 0)
02019     {
02020       line[n] SET_TO c;
02021       BUG_MACRO(name, "Sscanf failure in read_real_number");
02022     }
02023   else
02024     {
02025       line[n] SET_TO c;
02026       *counter SET_TO n;
02027       return RS274NGC_OK;
02028     }
02029 }
02030 
02031 /****************************************************************************/
02032 
02033 /* read_unary
02034 
02035 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
02036    If any of the following errors occur, this returns RS274NGC_ERROR.
02037    Otherwise, it returns RS274NGC_OK.
02038    1. read_operation_unary returns RS274NGC_ERROR,
02039    2. the name of the unary operation is not followed by a left bracket,
02040    3. read_real_expression returns RS274NGC_ERROR,
02041    4. read_atan or execute_unary returns RS274NGC_ERROR,
02042 
02043 Side effects:
02044    The value read from the line is put into what double_ptr points at.
02045    The counter is reset to point to the first character after the
02046    characters which make up the value.
02047 
02048 Called by:
02049    read_real_value
02050 
02051 This attempts to read the value of a unary operation out of the line,
02052 starting at the index given by the counter. The atan operation is
02053 handled specially because it is followed by two arguments.
02054 
02055 */
02056 
02057 int read_unary(       /* ARGUMENT VALUES                                */
02058  char * line,         /* string: line of RS274/NGC code being processed */
02059  int * counter,       /* pointer to a counter for position on the line  */
02060  double * double_ptr, /* pointer to double to be read                   */
02061  double * parameters) /* array of system parameters                     */
02062 {
02063   static char name[] SET_TO "read_unary";
02064   int operation;
02065 
02066   if (read_operation_unary (line, counter, &operation) IS RS274NGC_ERROR)
02067     ERROR_MACRO_PASS(name);
02068   if (line[*counter] ISNT '[')
02069     ERROR_MACRO(_interpreter_linetext, name, "Left bracket missing after unary operation name");
02070   if (read_real_expression (line, counter, double_ptr, parameters)
02071       IS RS274NGC_ERROR)
02072     ERROR_MACRO_PASS(name);
02073 
02074   if (operation IS ATAN)
02075     return read_atan(line, counter, double_ptr, parameters);
02076   else
02077     return execute_unary (double_ptr, operation);
02078 }
02079 
02080 /****************************************************************************/
02081 
02082 /* utility_find_arc_length
02083 
02084 Returned Value: double (length of path between start and end points)
02085 
02086 Side effects: none
02087 
02088 Called by:
02089    convert_arc_xy
02090    convert_arc_yz
02091    convert_arc_zx
02092 
02093 This calculates the length of the path that will be made relative to
02094 the XYZ axes for a motion in which the X,Y,Z, motion is a circular or
02095 helical arc with its axis parallel to the Z-axis. If tool length
02096 compensation is on, this is the path of the tool tip; if off, the
02097 length of the path of the spindle tip.
02098 
02099 If the arc is helical, it is coincident with the hypotenuse of a right
02100 triangle wrapped around a cylinder. If the triangle is unwrapped, its
02101 base is [the radius of the cylinder times the number of radians in the
02102 helix] and its height is [z2 - z1], and the path length can be found
02103 by the Pythagorean theorem.
02104 
02105 This is written as though it is only for arcs whose axis is parallel to
02106 the Z-axis, but it will serve also for arcs whose axis is parallel
02107 to the X-axis or Y-axis, with suitable permutation of the arguments.
02108 
02109 */
02110 
02111 double utility_find_arc_length( /* ARGUMENT VALUES      */
02112  double x1,       /* X-coordinate of start point        */
02113  double y1,       /* Y-coordinate of start point        */
02114  double z1,       /* Z-coordinate of start point        */
02115  double center_x, /* X-coordinate of arc center         */
02116  double center_y, /* Y-coordinate of arc center         */
02117  int turn,        /* no. of full or partial circles CCW */
02118  double x2,       /* X-coordinate of end point          */
02119  double y2,       /* Y-coordinate of end point          */
02120  double z2)       /* Z-coordinate of end point          */
02121 {
02122   double radius;
02123   double theta;  /* amount of turn of arc */
02124 
02125   if (turn IS 0)
02126     return 0.0;
02127   radius SET_TO hypot((center_x - x1), (center_y - y1));
02128   theta SET_TO utility_find_turn(x1, y1, center_x, center_y, turn, x2, y2);
02129   if (z2 IS z1)
02130     return (radius * fabs(theta));
02131   else
02132     return hypot((radius * theta), (z2 - z1));
02133 }
02134 
02135 /****************************************************************************/
02136 
02137 /* utility_find_straight_length
02138 
02139 Returned Value: double (length of path between start and end points)
02140 
02141 Side effects: none
02142 
02143 Called by:
02144    convert_straight
02145 
02146 This calculates a number to use in feed rate calculations when inverse
02147 time feed mode is used, for a motion in which X,Y, and Z, each change
02148 linearly or not at all from their initial value to their end value.
02149 
02150 This is used when the feed_reference mode is CANON_XYZ, which is
02151 always in rs274kt.
02152 
02153 This is the length of the path relative to the XYZ axes from the first
02154 point to the second. The length is the simple Euclidean distance.
02155 
02156 */
02157 
02158 double utility_find_straight_length( /* ARGUMENT VALUES  */
02159  double x2,    /* X-coordinate of end point              */
02160  double y2,    /* Y-coordinate of end point              */
02161  double z2,    /* Z-coordinate of end point              */
02162  double x1,    /* X-coordinate of start point            */
02163  double y1,    /* Y-coordinate of start point            */
02164  double z1)    /* Z-coordinate of start point            */
02165 {
02166     return sqrt(pow((x2 - x1),2) + pow((y2 - y1),2) + pow((z2 - z1),2));
02167 }
02168 
02169 /****************************************************************************/
02170 
02171 /* convert_arc_comp1
02172 
02173 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
02174    If any of the following errors occur, this returns RS274NGC_ERROR.
02175    Otherwise, it returns RS274NGC_OK.
02176    1. arc_data_comp_ijk or arc_data_comp_r returns RS274NGC_ERROR.
02177    2. The tool radius is not positive.
02178 
02179 Side effects:
02180    This executes an arc command at
02181    feed rate. It also updates the setting of the position of
02182    the tool point to the end point of the move.
02183 
02184 Called by: convert_arc.
02185 
02186 This function converts a helical or circular arc, generating only one
02187 arc. The axis must be parallel to the z-axis. This is called when
02188 cutter radius compensation is on and this is the first cut after the
02189 turning on.
02190 
02191 The arc which is generated is derived from second arc which passes
02192 through the programmed end point and is tangent to the cutter at its
02193 current location. The generated arc moves the tool so that it stays
02194 tangent to the second arc throughout the move.
02195 
02196 */
02197 
02198 int convert_arc_comp1(   /* ARGUMENT VALUES                              */
02199  int move,               /* either G_2 (cw arc) or G_3 (ccw arc)         */
02200  block_pointer block,    /* pointer to a block of RS274/NGC instructions */
02201  setup_pointer settings, /* pointer to machine settings                  */
02202  double end_x,           /* x-value at end of arc                        */
02203  double end_y,           /* y-value at end of arc                        */
02204  double end_z)           /* z-value at end of arc                        */
02205 {
02206   static char name[] SET_TO "convert_arc_comp1";
02207   double center_x;
02208   double center_y;
02209   int turn;     /* 1 for counterclockwise, -1 for clockwise */
02210   double gamma; /* direction of perpendicular to arc at end */
02211   double tolerance;
02212   double tool_radius;
02213   int side;
02214   int status;
02215 
02216   side SET_TO settings->cutter_radius_compensation;
02217   tool_radius SET_TO
02218     (settings->tool_table[settings->tool_table_index].diameter)/2.0;
02219   if (tool_radius <= 0.0)
02220     ERROR_MACRO(_interpreter_linetext, name, "Bad tool radius value with cutter radius comp");
02221   tolerance SET_TO (settings->length_units IS CANON_UNITS_INCHES) ?
02222     TOLERANCE_INCH : TOLERANCE_MM;
02223 
02224   if (block->r_flag)
02225     {
02226       status SET_TO
02227         arc_data_comp_r(move, side, tool_radius, settings->current_x,
02228                         settings->current_y, end_x, end_y, block->r_number,
02229                         &center_x, &center_y, &turn);
02230     }
02231   else
02232     {
02233       status SET_TO
02234         arc_data_comp_ijk(move, side, tool_radius, settings->current_x,
02235                           settings->current_y, end_x, end_y,
02236                           block->i_number, block->j_number,
02237                           &center_x, &center_y, &turn, tolerance);
02238     }
02239 
02240   if (status IS RS274NGC_ERROR)
02241    ERROR_MACRO_PASS(name);
02242 
02243   gamma SET_TO
02244     (((side IS LEFT) AND (move IS G_3)) OR
02245      ((side IS RIGHT) AND (move IS G_2))) ?
02246        atan2 ((center_y - end_y), (center_x - end_x)) :
02247        atan2 ((end_y - center_y), (end_x - center_x));
02248 
02249   settings->program_x SET_TO end_x;
02250   settings->program_y SET_TO end_y;
02251   end_x SET_TO (end_x + (tool_radius * cos(gamma)));
02252   end_y SET_TO (end_y + (tool_radius * sin(gamma)));
02253   ARC_FEED(end_x, end_y, center_x, center_y, turn, end_z);
02254   settings->current_x SET_TO end_x;
02255   settings->current_y SET_TO end_y;
02256   settings->current_z SET_TO end_z;
02257 
02258   return RS274NGC_OK;
02259 }
02260 
02261 /****************************************************************************/
02262 
02263 /* convert_arc_comp2
02264 
02265 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
02266    If any of the following errors occurs, this returns RS274NGC_ERROR.
02267    Otherwise, it returns RS274NGC_OK.
02268    1. arc_data_ijk or arc_data_r returns RS274NGC_ERROR.
02269    2. A concave corner is found.
02270    3. The tool will not fit inside an arc.
02271    4. The tool radius is zero or negative
02272 
02273 Side effects:
02274    This executes an arc command feed rate. If needed, at also generates
02275    an arc to go around a convex corner. It also updates the setting of
02276    the position of the tool point to the end point of the move.
02277 
02278 Called by: convert_arc.
02279 
02280 This function converts a helical or circular arc. The axis must be
02281 parallel to the z-axis. This is called when cutter radius compensation
02282 is on and this is not the first cut after the turning on.
02283 
02284 If the Z-axis is moved in this block and an extra arc is required to
02285 go around a sharp corner, all the Z-axis motion occurs on the main arc
02286 and none on the extra arc.  An alternative might be to distribute the
02287 Z-axis motion over the extra arc and the main arc in proportion to
02288 their lengths.
02289 
02290 */
02291 
02292 int convert_arc_comp2(   /* ARGUMENT VALUES                                */
02293  int move,               /* either G_2 (cw arc) or G_3 (ccw arc)           */
02294  block_pointer block,    /* pointer to a block of RS274/NGC instructions   */
02295  setup_pointer settings, /* pointer to machine settings                    */
02296  double end_x,           /* x-value at end of programmed (then actual) arc */
02297  double end_y,           /* y-value at end of programmed (then actual) arc */
02298  double end_z)           /* z-value at end of arc                          */
02299 {
02300   static char name[] SET_TO "convert_arc_comp2";
02301   double center_x; /* center of arc */
02302   double center_y;
02303   double start_x;
02304   double start_y;
02305   int turn;     /* number of full or partial circles CCW */
02306   double theta; /* direction of tangent to last cut */
02307   double delta; /* direction of radius from start of arc to center of arc */
02308   double alpha; /* direction of tangent to start of arc */
02309   double beta;  /* angle between two tangents above */
02310   double gamma; /* direction of perpendicular to arc at end */
02311   double arc_radius;
02312   double tool_radius;
02313   /* small angle in radians for testing corners */
02314   double small SET_TO TOLERANCE_CONCAVE_CORNER;
02315   int side;
02316   int status;
02317   double tolerance;
02318 
02319 /* find basic arc data: center_x, center_y, and turn */
02320 
02321   start_x SET_TO settings->program_x;
02322   start_y SET_TO settings->program_y;
02323   tolerance SET_TO (settings->length_units IS CANON_UNITS_INCHES) ?
02324     TOLERANCE_INCH : TOLERANCE_MM;
02325 
02326   if (block->r_flag)
02327     {
02328       status SET_TO arc_data_r(move, start_x, start_y, end_x, end_y,
02329                                block->r_number, &center_x, &center_y, &turn);
02330     }
02331   else
02332     {
02333       status SET_TO
02334         arc_data_ijk(move, start_x, start_y, end_x, end_y,
02335                      block->i_number, block->j_number,
02336                      &center_x, &center_y, &turn, tolerance);
02337     }
02338 
02339   if (status IS RS274NGC_ERROR)
02340     ERROR_MACRO_PASS(name);
02341 
02342 /* compute other data */
02343   side SET_TO settings->cutter_radius_compensation;
02344   tool_radius SET_TO
02345     (settings->tool_table[settings->tool_table_index].diameter)/2.0;
02346   if (tool_radius <= 0.0)
02347     ERROR_MACRO(_interpreter_linetext, name, "Bad tool radius value with cutter radius comp");
02348   arc_radius SET_TO hypot((center_x - end_x), (center_y - end_y));
02349   theta SET_TO
02350     atan2(settings->current_y - start_y, settings->current_x - start_x);
02351   theta SET_TO (side IS LEFT) ? (theta - PI2) : (theta + PI2);
02352   delta SET_TO atan2(center_y - start_y, center_x - start_x);
02353   alpha SET_TO (move IS G_3) ? (delta - PI2) : (delta + PI2);
02354   beta SET_TO (side IS LEFT) ? (theta - alpha) : (alpha - theta);
02355   beta SET_TO (beta > (1.5 * PI))  ? (beta - TWO_PI) :
02356               (beta < -PI2) ? (beta + TWO_PI) : beta;
02357 
02358   if (((side IS LEFT)  AND (move IS G_3)) OR
02359       ((side IS RIGHT) AND (move IS G_2)))
02360     {
02361       gamma SET_TO atan2 ((center_y - end_y), (center_x - end_x));
02362       if (arc_radius <= tool_radius)
02363         ERROR_MACRO(_interpreter_linetext, name,
02364            "Tool radius not less than arc radius with cutter radius comp");
02365     }
02366   else
02367     {
02368       gamma SET_TO atan2 ((end_y - center_y), (end_x - center_x));
02369       delta SET_TO (delta + PI);
02370     }
02371 
02372   settings->program_x SET_TO end_x;
02373   settings->program_y SET_TO end_y;
02374 
02375 /* check if extra arc needed and insert if so */
02376 
02377   if ((beta < -small) OR (beta > (PI + small)))
02378     ERROR_MACRO(_interpreter_linetext, name, "Concave corner with cutter radius comp");
02379   else if (beta > small) /* TWO ARCS NEEDED */
02380     ARC_FEED((start_x + (tool_radius * cos(delta))),
02381              (start_y + (tool_radius * sin(delta))),
02382              start_x, start_y, (side IS LEFT) ? -1 : 1,
02383              settings->current_z);
02384 
02385   end_x SET_TO (end_x + (tool_radius * cos(gamma))); /* end_x reset actual */
02386   end_y SET_TO (end_y + (tool_radius * sin(gamma))); /* end_y reset actual */
02387 
02388 /* insert main arc */
02389   ARC_FEED(end_x, end_y, center_x, center_y, turn, end_z);
02390 
02391   settings->current_x SET_TO end_x;
02392   settings->current_y SET_TO end_y;
02393   settings->current_z SET_TO end_z;
02394 
02395   return RS274NGC_OK;
02396 }
02397 
02398 /****************************************************************************/
02399 
02400 /* convert_arc_xy
02401 
02402 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
02403    If arc_data_ijk or arc_data_r returns RS274NGC_ERROR, this
02404    returns RS274NGC_ERROR. Otherwise, it returns RS274NGC_OK.
02405 
02406 Side effects:
02407    This executes an arc command at feed rate. It also updates the
02408    setting of the position of the tool point to the end point of the move.
02409    If inverse time feed rate is in effect, it also resets the feed rate.
02410 
02411 Called by: convert_arc.
02412 
02413 This converts a helical or circular arc. The axis must be parallel to
02414 the z-axis.
02415 
02416 */
02417 
02418 int convert_arc_xy(      /* ARGUMENT VALUES                          */
02419  int move,               /* either G_2 (cw arc) or G_3 (ccw arc)     */
02420  block_pointer block,    /* pointer to a block of RS274 instructions */
02421  setup_pointer settings, /* pointer to machine settings              */
02422  double end_x,           /* x-value at end of arc                    */
02423  double end_y,           /* y-value at end of arc                    */
02424  double end_z)           /* z-value at end of arc                    */
02425 {
02426   static char name[] SET_TO "convert_arc_xy";
02427   double center_x;
02428   double center_y;
02429   double length;
02430   double rate;
02431   int turn;
02432   int status;
02433   double tolerance;
02434 
02435   tolerance SET_TO (settings->length_units IS CANON_UNITS_INCHES) ?
02436     TOLERANCE_INCH : TOLERANCE_MM;
02437 
02438   if (block->r_flag)
02439     {
02440       status SET_TO
02441         arc_data_r(move, settings->current_x, settings->current_y, end_x,
02442                    end_y, block->r_number, &center_x, &center_y, &turn);
02443     }
02444   else
02445     {
02446       status SET_TO
02447         arc_data_ijk(move, settings->current_x, settings->current_y,
02448                      end_x, end_y, block->i_number, block->j_number,
02449                      &center_x, &center_y, &turn, tolerance);
02450     }
02451 
02452   if (status IS RS274NGC_ERROR)
02453     ERROR_MACRO_PASS(name);
02454 
02455   if (settings->feed_mode IS INVERSE_TIME)
02456     {
02457       length SET_TO utility_find_arc_length
02458         (settings->current_x, settings->current_y, settings->current_z,
02459          center_x, center_y, turn, end_x, end_y, end_z);
02460       rate SET_TO MAX(0.1, (length * block->f_number));
02461       SET_FEED_RATE (rate);
02462       settings->feed_rate SET_TO rate;
02463     }
02464 
02465   ARC_FEED(end_x, end_y, center_x, center_y, turn, end_z);
02466   settings->current_x SET_TO end_x;
02467   settings->current_y SET_TO end_y;
02468   settings->current_z SET_TO end_z;
02469   return RS274NGC_OK;
02470 }
02471 
02472 /****************************************************************************/
02473 
02474 /* convert_arc_yz
02475 
02476 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
02477    If arc_data_ijk or arc_data_r returns RS274NGC_ERROR, this
02478    returns RS274NGC_ERROR. Otherwise, it returns RS274NGC_OK.
02479 
02480 Side effects:
02481    This executes an arc command at feed rate. It also updates the
02482    setting of the position of the tool point to the end point of the move.
02483    If inverse time feed rate is in effect, it also resets the feed rate.
02484 
02485 Called by: convert_arc.
02486 
02487 This converts a helical or circular arc. The axis must be parallel to
02488 the x-axis.
02489 
02490 */
02491 
02492 int convert_arc_yz(      /* ARGUMENT VALUES                          */
02493  int move,               /* either G_2 (cw arc) or G_3 (ccw arc)     */
02494  block_pointer block,    /* pointer to a block of RS274 instructions */
02495  setup_pointer settings, /* pointer to machine settings              */
02496  double end_y,           /* y-value at end of arc                    */
02497  double end_z,           /* z-value at end of arc                    */
02498  double end_x)           /* x-value at end of arc                    */
02499 {
02500   static char name[] SET_TO "convert_arc_yz";
02501   double center_y;
02502   double center_z;
02503   double length;
02504   double rate;
02505   int turn;
02506   int status;
02507   double tolerance;
02508 
02509   tolerance SET_TO (settings->length_units IS CANON_UNITS_INCHES) ?
02510     TOLERANCE_INCH : TOLERANCE_MM;
02511 
02512   if (block->r_flag)
02513     {
02514       status SET_TO
02515         arc_data_r(move, settings->current_y, settings->current_z, end_y,
02516                    end_z, block->r_number, &center_y, &center_z, &turn);
02517     }
02518   else
02519     {
02520       status SET_TO
02521         arc_data_ijk(move, settings->current_y, settings->current_z,
02522                      end_y, end_z, block->j_number, block->k_number,
02523                      &center_y, &center_z, &turn, tolerance);
02524     }
02525 
02526   if (status IS RS274NGC_ERROR)
02527     ERROR_MACRO_PASS(name);
02528 
02529   if (settings->feed_mode IS INVERSE_TIME)
02530     {
02531       length SET_TO utility_find_arc_length
02532         (settings->current_y, settings->current_z, settings->current_x,
02533          center_y, center_z, turn, end_y, end_z, end_x);
02534       rate SET_TO MAX(0.1, (length * block->f_number));
02535       SET_FEED_RATE (rate);
02536       settings->feed_rate SET_TO rate;
02537     }
02538 
02539   ARC_FEED(end_y, end_z, center_y, center_z, turn, end_x);
02540   settings->current_y SET_TO end_y;
02541   settings->current_z SET_TO end_z;
02542   settings->current_x SET_TO end_x;
02543 
02544   return RS274NGC_OK;
02545 }
02546 
02547 /****************************************************************************/
02548 
02549 /* convert_arc_zx
02550 
02551 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
02552    If arc_data_ijk or arc_data_r returns RS274NGC_ERROR, this
02553    returns RS274NGC_ERROR. Otherwise, it returns RS274NGC_OK.
02554 
02555 Side effects:
02556    This executes an arc command at feed rate. It also updates the
02557    setting of the position of the tool point to the end point of the move.
02558    If inverse time feed rate is in effect, it also resets the feed rate.
02559 
02560 Called by: convert_arc.
02561 
02562 This converts a helical or circular arc. The axis must be parallel to
02563 the y-axis.
02564 
02565 */
02566 
02567 int convert_arc_zx(      /* ARGUMENT VALUES                          */
02568  int move,               /* either G_2 (cw arc) or G_3 (ccw arc)     */
02569  block_pointer block,    /* pointer to a block of RS274 instructions */
02570  setup_pointer settings, /* pointer to machine settings              */
02571  double end_z,           /* z-value at end of arc                    */
02572  double end_x,           /* x-value at end of arc                    */
02573  double end_y)           /* y-value at end of arc                    */
02574 {
02575   static char name[] SET_TO "convert_arc_zx";
02576   double center_z;
02577   double center_x;
02578   double length;
02579   double rate;
02580   int turn;
02581   int status;
02582   double tolerance;
02583 
02584   tolerance SET_TO (settings->length_units IS CANON_UNITS_INCHES) ?
02585     TOLERANCE_INCH : TOLERANCE_MM;
02586 
02587   if (block->r_flag)
02588     {
02589       status SET_TO
02590         arc_data_r(move, settings->current_z, settings->current_x, end_z,
02591                    end_x, block->r_number, &center_z, &center_x, &turn);
02592     }
02593   else
02594     {
02595       status SET_TO
02596         arc_data_ijk(move, settings->current_z, settings->current_x,
02597                      end_z, end_x, block->k_number, block->i_number,
02598                      &center_z, &center_x, &turn, tolerance);
02599     }
02600 
02601   if (status IS RS274NGC_ERROR)
02602     ERROR_MACRO_PASS(name);
02603 
02604   if (settings->feed_mode IS INVERSE_TIME)
02605     {
02606       length SET_TO utility_find_arc_length
02607         (settings->current_z, settings->current_x, settings->current_y,
02608          center_z, center_x, turn, end_z, end_x, end_y);
02609       rate SET_TO MAX(0.1, (length * block->f_number));
02610       SET_FEED_RATE (rate);
02611       settings->feed_rate SET_TO rate;
02612     }
02613 
02614   ARC_FEED(end_z, end_x, center_z, center_x, turn, end_y);
02615   settings->current_z SET_TO end_z;
02616   settings->current_x SET_TO end_x;
02617   settings->current_y SET_TO end_y;
02618 
02619   return RS274NGC_OK;
02620 }
02621 
02622 /****************************************************************************/
02623 
02624 /* convert_cycle_g81
02625 
02626 Returned Value: int (RS274NGC_OK)
02627 
02628 Side effects:
02629    A number of moves are made as described below.
02630 
02631 Called by: convert_cycle
02632 
02633 This implements the following cycle, which is usually drilling:
02634 1. Move the z-axis only at the current feed rate to the specified bottom_z.
02635 2. Retract the z-axis at traverse rate to clear_z.
02636 
02637 See [NCMS, page 99].
02638 
02639 convert_cycle has positioned the tool at (x, y, r) when this starts.
02640 
02641 */
02642 
02643 int convert_cycle_g81( /* ARGUMENT VALUES                  */
02644  double x,             /* x-value where cycle is executed  */
02645  double y,             /* y-value where cycle is executed  */
02646  double clear_z,       /* z-value of clearance plane       */
02647  double bottom_z)      /* value of z at bottom of cycle    */
02648 {
02649   static char name[] SET_TO "convert_cycle_g81";
02650 
02651   STRAIGHT_FEED(x, y, bottom_z);
02652   STRAIGHT_TRAVERSE(x, y, clear_z);
02653 
02654   return RS274NGC_OK;
02655 }
02656 
02657 /****************************************************************************/
02658 
02659 /* convert_cycle_g82
02660 
02661 Returned Value: int (RS274NGC_OK)
02662 
02663 Side effects:
02664    A number of moves are made as described below.
02665 
02666 Called by: convert_cycle
02667 
02668 This implements the following cycle, which is usually drilling:
02669 1. Move the z_axis only at the current feed rate to the specified z-value.
02670 2. Dwell for the given number of seconds.
02671 3. Retract the z-axis at traverse rate to the clear_z.
02672 
02673 convert_cycle has positioned the tool at (x, y, r) when this starts.
02674 
02675 */
02676 
02677 int convert_cycle_g82( /* ARGUMENT VALUES                  */
02678  double x,             /* x-value where cycle is executed  */
02679  double y,             /* y-value where cycle is executed  */
02680  double clear_z,       /* z-value of clearance plane       */
02681  double bottom_z,      /* value of z at bottom of cycle    */
02682  double dwell)         /* dwell time                       */
02683 {
02684   static char name[] SET_TO "convert_cycle_g82";
02685 
02686   STRAIGHT_FEED(x, y, bottom_z);
02687   DWELL(dwell);
02688   STRAIGHT_TRAVERSE(x, y, clear_z);
02689 
02690   return RS274NGC_OK;
02691 }
02692 
02693 /****************************************************************************/
02694 
02695 /* convert_cycle_g83
02696 
02697 Returned Value: int (RS274NGC_OK)
02698 
02699 Side effects:
02700    A number of moves are made as described below.
02701 
02702 Called by: convert_cycle
02703 
02704 This implements the following cycle, which is usually
02705 deep drilling or milling with chipbreaking:
02706 1. Move the z-axis only at the current feed rate downward by delta or
02707    to the specified bottom_z, whichever is less deep.
02708 2. Dwell for 0.25 second.
02709 3. Rapid back out to the clear_z.
02710 4. Rapid back down to the current hole bottom, backed off a bit.
02711 5. Repeat steps 1 and 2 until the specified bottom_z is reached.
02712 6. Retract the z-axis at traverse rate to clear_z.
02713 
02714 convert_cycle has positioned the tool at (x, y, r) when this starts.
02715 
02716 The dwell causes any long stringers (which are common when drilling
02717 in aluminum) to be cut off, and pulls out the chips.
02718 
02719 */
02720 
02721 #define G83_RAPID_DELTA 0.010   /* how far above hole bottom for rapid
02722                                    return, in inches */
02723 int convert_cycle_g83( /* ARGUMENT VALUES                  */
02724  double x,             /* x-value where cycle is executed  */
02725  double y,             /* y-value where cycle is executed  */
02726  double r,             /* initial z-value                  */
02727  double clear_z,       /* z-value of clearance plane       */
02728  double bottom_z,      /* value of z at bottom of cycle    */
02729  double delta)         /* size of z-axis feed increment    */
02730 {
02731   static char name[] SET_TO "convert_cycle_g83";
02732   double current_depth;
02733   double rapid_delta;
02734 
02735   rapid_delta SET_TO G83_RAPID_DELTA;
02736   if (_interpreter_settings.length_units IS CANON_UNITS_MM)
02737     rapid_delta *= 25.4;
02738 
02739   for (current_depth SET_TO (r - delta);
02740        current_depth > bottom_z;
02741        current_depth SET_TO (current_depth - delta))
02742     {
02743       STRAIGHT_FEED(x, y, current_depth);
02744       DWELL(0.25);
02745       STRAIGHT_TRAVERSE(x, y, clear_z);
02746       STRAIGHT_TRAVERSE(x, y, current_depth + rapid_delta);
02747     }
02748   STRAIGHT_FEED(x, y, bottom_z);
02749   DWELL(0.25);
02750   STRAIGHT_TRAVERSE(x, y, clear_z);
02751 
02752   return RS274NGC_OK;
02753 }
02754 
02755 /****************************************************************************/
02756 
02757 /* convert_cycle_g84
02758 
02759 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
02760    If the spindle is not turning clockwise, this returns RS274NGC_ERROR.
02761    Otherwise, it returns RS274NGC_OK.
02762 
02763 Side effects:
02764    A number of moves are made as described below. This is for right-hand
02765    tapping.
02766 
02767 Called by: convert_cycle
02768 
02769 This implements the following cycle, which is right-hand tapping:
02770 1. Start speed-feed synchronization.
02771 2. Move the z-axis only at the current feed rate to the specified bottom_z.
02772 3. Stop the spindle.
02773 4. Start the spindle counterclockwise.
02774 5. Retract the z-axis at current feed rate to clear_z.
02775 6. If speed-feed synch was not on before the cycle started, stop it.
02776 7. Stop the spindle.
02777 8. Start the spindle clockwise.
02778 
02779 convert_cycle has positioned the tool at (x, y, r) when this starts.
02780 The direction argument must be clockwise.
02781 
02782 */
02783 
02784 int convert_cycle_g84(       /* ARGUMENT VALUES                     */
02785  double x,                   /* x-value where cycle is executed     */
02786  double y,                   /* y-value where cycle is executed     */
02787  double clear_z,             /* z-value of clearance plane          */
02788  double bottom_z,            /* value of z at bottom of cycle       */
02789  CANON_DIRECTION direction,  /* direction spindle turning at outset */
02790  CANON_SPEED_FEED_MODE mode) /* the speed-feed mode at outset       */
02791 {
02792   static char name[] SET_TO "convert_cycle_g84";
02793 
02794   if (direction ISNT CANON_CLOCKWISE)
02795     ERROR_MACRO(_interpreter_linetext, name, "Spindle not turning clockwise in G84 canned cycle");
02796   START_SPEED_FEED_SYNCH();
02797   STRAIGHT_FEED(x, y, bottom_z);
02798   STOP_SPINDLE_TURNING();
02799   START_SPINDLE_COUNTERCLOCKWISE();
02800   STRAIGHT_FEED(x, y, clear_z);
02801   if (mode ISNT CANON_SYNCHED)
02802     STOP_SPEED_FEED_SYNCH();
02803   STOP_SPINDLE_TURNING();
02804   START_SPINDLE_CLOCKWISE();
02805 
02806   return RS274NGC_OK;
02807 }
02808 
02809 /****************************************************************************/
02810 
02811 /* convert_cycle_g85
02812 
02813 Returned Value: int (RS274NGC_OK)
02814 
02815 Side effects:
02816    A number of moves are made as described below.
02817 
02818 Called by: convert_cycle
02819 
02820 This implements the following cycle, which is usually boring or reaming:
02821 1. Move the z-axis only at the current feed rate to the specified z-value.
02822 2. Retract the z-axis at the current feed rate to clear_z.
02823 
02824 convert_cycle has positioned the tool at (x, y, r) when this starts.
02825 
02826 */
02827 
02828 int convert_cycle_g85( /* ARGUMENT VALUES                  */
02829  double x,             /* x-value where cycle is executed  */
02830  double y,             /* y-value where cycle is executed  */
02831  double clear_z,       /* z-value of clearance plane       */
02832  double bottom_z)      /* value of z at bottom of cycle    */
02833 {
02834   static char name[] SET_TO "convert_cycle_g85";
02835 
02836   STRAIGHT_FEED(x, y, bottom_z);
02837   STRAIGHT_FEED(x, y, clear_z);
02838 
02839   return RS274NGC_OK;
02840 }
02841 
02842 /****************************************************************************/
02843 
02844 /* convert_cycle_g86
02845 
02846 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
02847    If the spindle is not turning clockwise or counterclockwise,
02848    this returns RS274NGC_ERROR.
02849    Otherwise, it returns RS274NGC_OK.
02850 
02851 Side effects:
02852    A number of moves are made as described below.
02853 
02854 Called by: convert_cycle
02855 
02856 This implements the following cycle, which is usually boring:
02857 1. Move the z-axis only at the current feed rate to bottom_z.
02858 2. Dwell for the given number of seconds.
02859 3. Stop the spindle turning.
02860 4. Retract the z-axis at traverse rate to clear_z.
02861 5. Restart the spindle in the direction it was going.
02862 
02863 convert_cycle has positioned the tool at (x, y, r) when this starts.
02864 
02865 */
02866 
02867 int convert_cycle_g86(      /* ARGUMENT VALUES                     */
02868  double x,                  /* x-value where cycle is executed     */
02869  double y,                  /* y-value where cycle is executed     */
02870  double clear_z,            /* z-value of clearance plane          */
02871  double bottom_z,           /* value of z at bottom of cycle       */
02872  double dwell,              /* dwell time                          */
02873  CANON_DIRECTION direction) /* direction spindle turning at outset */
02874 {
02875   static char name[] SET_TO "convert_cycle_g86";
02876 
02877   if ((direction ISNT CANON_CLOCKWISE) AND
02878       (direction ISNT CANON_COUNTERCLOCKWISE))
02879     ERROR_MACRO(_interpreter_linetext, name, "Spindle not turning in G86 canned cycle");
02880 
02881   STRAIGHT_FEED(x, y, bottom_z);
02882   DWELL(dwell);
02883   STOP_SPINDLE_TURNING();
02884   STRAIGHT_TRAVERSE(x, y, clear_z);
02885   if (direction IS CANON_CLOCKWISE)
02886     START_SPINDLE_CLOCKWISE();
02887   else
02888     START_SPINDLE_COUNTERCLOCKWISE();
02889 
02890   return RS274NGC_OK;
02891 }
02892 
02893 /****************************************************************************/
02894 
02895 /* convert_cycle_g87
02896 
02897 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
02898    If the spindle is not turning clockwise or counterclockwise,
02899    this returns RS274NGC_ERROR.
02900    Otherwise, it returns RS274NGC_OK.
02901 
02902 Side effects:
02903    A number of moves are made as described below. This cycle is a
02904    modified version of [Monarch, page 5-24] since [NCMS, pages 98 - 100]
02905    gives no clue as to what the cycle is supposed to do.
02906 
02907 Called by: convert_cycle
02908 
02909 This implements the following cycle, which is usually back
02910 boring.  The situation is that you have a through hole and you want to
02911 counterbore the bottom of hole. To to this you put an L-shaped tool in
02912 the spindle with a cutting surface on the UPPER side of its base. You
02913 stick it carefully through the hole when it is not spinning and is
02914 oriented so it fits through the hole, then you move it so the stem of
02915 the L is on the axis of the hole, start the spindle, and feed the tool
02916 upward to make the counterbore. Then you get the tool out of the hole.
02917 
02918 1. Move at traverse rate parallel to the XY-plane to the point
02919    with x-value offset_x and y-value offset_y.
02920 2. Stop the spindle in a specific orientation.
02921 3. Move the z-axis only at traverse rate downward to the bottom_z.
02922 4. Move at traverse rate parallel to the XY-plane to the x,y location.
02923 5. Start the spindle in the direction it was going before.
02924 6. Move the z-axis only at the given feed rate upward to the middle_z.
02925 7. Move the z-axis only at the given feed rate back down to bottom_z.
02926 8. Stop the spindle in the same orientation as before.
02927 9. Move at traverse rate parallel to the XY-plane to the point
02928    with x-value offset_x and y-value offset_y.
02929 10. Move the z-axis only at traverse rate to the clear z value.
02930 11. Move at traverse rate parallel to the XY-plane to the specified x,y
02931     location.
02932 12. Restart the spindle in the direction it was going before.
02933 
02934 convert_cycle has positioned the tool at (x, y, r) before this starts.
02935 
02936 It might be useful to add a check that clear_z > middle_z > bottom_z.
02937 Without the check, however, this can be used to counterbore a hole in
02938 material that can only be accessed through a hole in material above it.
02939 
02940 */
02941 
02942 int convert_cycle_g87(       /* ARGUMENT VALUES                     */
02943  double x,                   /* x-value where cycle is executed     */
02944  double offset_x,            /* x-axis offset position              */
02945  double y,                   /* y-value where cycle is executed     */
02946  double offset_y,            /* y-axis offset position              */
02947  double r,                   /* z_value of r_plane                  */
02948  double clear_z,             /* z-value of clearance plane          */
02949  double middle_z,            /* z-value of top of back bore         */
02950  double bottom_z,            /* value of z at bottom of cycle       */
02951  CANON_DIRECTION direction)  /* direction spindle turning at outset */
02952 {
02953   static char name[] SET_TO "convert_cycle_g87";
02954 
02955   if ((direction ISNT CANON_CLOCKWISE) AND
02956       (direction ISNT CANON_COUNTERCLOCKWISE))
02957     ERROR_MACRO(_interpreter_linetext, name, "Spindle not turning in G87 canned cycle");
02958 
02959   STRAIGHT_TRAVERSE(offset_x, offset_y, r);
02960   STOP_SPINDLE_TURNING();
02961   ORIENT_SPINDLE(0.0, direction);
02962   STRAIGHT_TRAVERSE(offset_x, offset_y, bottom_z);
02963   STRAIGHT_TRAVERSE(x, y, bottom_z);
02964   if (direction IS CANON_CLOCKWISE)
02965     START_SPINDLE_CLOCKWISE();
02966   else
02967     START_SPINDLE_COUNTERCLOCKWISE();
02968   STRAIGHT_FEED(x, y, middle_z);
02969   STRAIGHT_FEED(x, y, bottom_z);
02970   STOP_SPINDLE_TURNING();
02971   ORIENT_SPINDLE(0.0, direction);
02972   STRAIGHT_TRAVERSE(offset_x, offset_y, bottom_z);
02973   STRAIGHT_TRAVERSE(offset_x, offset_y, clear_z);
02974   STRAIGHT_TRAVERSE(x, y, clear_z);
02975   if (direction IS CANON_CLOCKWISE)
02976     START_SPINDLE_CLOCKWISE();
02977   else
02978     START_SPINDLE_COUNTERCLOCKWISE();
02979 
02980   return RS274NGC_OK;
02981 }
02982 
02983 /****************************************************************************/
02984 
02985 /* convert_cycle_g88
02986 
02987 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
02988    If the spindle is not turning clockwise or counterclockwise, this
02989    returns RS274NGC_ERROR.
02990    Otherwise, it returns RS274NGC_OK.
02991 
02992 Side effects:
02993    A number of moves are made as described below.
02994 
02995 Called by: convert_cycle
02996 
02997 This implements the following cycle, which is usually boring:
02998 1. Move the z-axis only at the current feed rate to the specified z-value.
02999 2. Dwell for the given number of seconds.
03000 3. Stop the spindle turning.
03001 4. Stop the program so the operator can retract the spindle manually.
03002 5. Restart the spindle.
03003 
03004 convert_cycle has positioned the tool at (x, y, r) when this starts.
03005 
03006 */
03007 
03008 int convert_cycle_g88(      /* ARGUMENT VALUES                     */
03009  double x,                  /* x-value where cycle is executed     */
03010  double y,                  /* y-value where cycle is executed     */
03011  double bottom_z,           /* value of z at bottom of cycle       */
03012  double dwell,              /* dwell time                          */
03013  CANON_DIRECTION direction) /* direction spindle turning at outset */
03014 {
03015   static char name[] SET_TO "convert_cycle_g88";
03016 
03017   if ((direction ISNT CANON_CLOCKWISE) AND
03018       (direction ISNT CANON_COUNTERCLOCKWISE))
03019     ERROR_MACRO(_interpreter_linetext, name, "Spindle not turning in G88 canned cycle");
03020 
03021   STRAIGHT_FEED(x, y, bottom_z);
03022   DWELL(dwell);
03023   STOP_SPINDLE_TURNING();
03024   PROGRAM_STOP(); /* operator retracts the spindle here */
03025   if (direction IS CANON_CLOCKWISE)
03026     START_SPINDLE_CLOCKWISE();
03027   else
03028     START_SPINDLE_COUNTERCLOCKWISE();
03029 
03030   return RS274NGC_OK;
03031 }
03032 
03033 /****************************************************************************/
03034 
03035 /* convert_cycle_g89
03036 
03037 Returned Value: int (RS274NGC_OK)
03038 
03039 Side effects:
03040    A number of moves are made as described below.
03041 
03042 Called by: convert_cycle
03043 
03044 This implements the following cycle, which is intended for boring:
03045 1. Move the z-axis only at the current feed rate to the specified z-value.
03046 2. Dwell for the given number of seconds.
03047 3. Retract the z-axis at the current feed rate to clear_z.
03048 
03049 */
03050 
03051 int convert_cycle_g89(      /* ARGUMENT VALUES                     */
03052  double x,                  /* x-value where cycle is executed     */
03053  double y,                  /* y-value where cycle is executed     */
03054  double clear_z,            /* z-value of clearance plane          */
03055  double bottom_z,           /* value of z at bottom of cycle       */
03056  double dwell)              /* dwell time                          */
03057 {
03058   static char name[] SET_TO "convert_cycle_g89";
03059 
03060   STRAIGHT_FEED(x, y, bottom_z);
03061   DWELL(dwell);
03062   STRAIGHT_FEED(x, y, clear_z);
03063 
03064   return RS274NGC_OK;
03065 }
03066 
03067 /****************************************************************************/
03068 
03069 /* convert_straight_comp1
03070 
03071 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
03072    If any of the following errors occur, this returns RS274NGC_ERROR.
03073    Otherwise, it returns RS274NGC_OK.
03074    1. BUG - The side is not RIGHT or LEFT.
03075    2. The destination tangent point is not more than a tool radius
03076       away (indicating gouging).
03077    3. The tool radius is less than or equal to zero.
03078 
03079 Side effects:
03080    This executes a straight move command at cutting feed rate.
03081    It also updates the setting of the position of the tool point
03082    to the end point of the move and updates the programmed point.
03083 
03084 Called by: convert_straight.
03085 
03086 This is called if cutter radius compensation is on and
03087 settings->program_x is UNKNOWN, indicating that this is the first move
03088 after cutter radius compensation is turned on.
03089 
03090 The algorithm used here for determining the path is to draw a straight
03091 line from the destination point which is tangent to a circle whose
03092 center is at the current point and whose radius is the radius of the
03093 cutter. The destination point of the cutter tip is then found as the
03094 center of a circle of the same radius tangent to the tangent line at
03095 the destination point.
03096 
03097 */
03098 
03099 int convert_straight_comp1( /* ARGUMENT VALUES                         */
03100  setup_pointer settings,    /* pointer to machine settings             */
03101  double px,                 /* X coordinate of end point               */
03102  double py,                 /* Y coordinate of end point               */
03103  double end_z)              /* Z coordinate of end point               */
03104 {
03105   static char name[] SET_TO "convert_straight_comp1";
03106   double radius;
03107   double cx, cy;
03108   double distance;
03109   double theta;
03110   double alpha;
03111   int side;
03112 
03113   side SET_TO settings->cutter_radius_compensation;
03114   cx SET_TO settings->current_x;
03115   cy SET_TO settings->current_y;
03116 
03117   radius SET_TO
03118     (settings->tool_table[settings->tool_table_index].diameter)/2.0;
03119   if (radius <= 0.0)
03120     ERROR_MACRO(_interpreter_linetext, name, "Bad tool radius value with cutter radius comp");
03121   distance SET_TO hypot((px - cx), (py -cy));
03122 
03123   if ((side ISNT LEFT) AND (side ISNT RIGHT))
03124     BUG_MACRO(name,
03125               "Side fails to be right or left in convert_straight_comp1");
03126   else if (distance <= radius)
03127     ERROR_MACRO(_interpreter_linetext, name, "Cutter gouging with cutter radius comp");
03128 
03129   theta SET_TO acos(radius/distance);
03130   alpha SET_TO (side IS LEFT) ? (atan2((cy - py), (cx - px)) - theta) :
03131                                 (atan2((cy - py), (cx - px)) + theta);
03132   cx SET_TO (px + (radius * cos(alpha))); /* reset to end location */
03133   cy SET_TO (py + (radius * sin(alpha)));
03134   STRAIGHT_FEED (cx, cy, end_z);
03135   settings->current_x SET_TO cx;
03136   settings->current_y SET_TO cy;
03137   settings->program_x SET_TO px;
03138   settings->program_y SET_TO py;
03139   return RS274NGC_OK;
03140 }
03141 
03142 /****************************************************************************/
03143 
03144 /* convert_straight_comp2
03145 
03146 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
03147    If any of the following errors occur, this returns RS274NGC_ERROR.
03148    Otherwise, it returns RS274NGC_OK.
03149    1. BUG - The compensation side is not RIGHT or LEFT.
03150    2. A concave corner is found.
03151    3. The tool radius is less than or equal to zero.
03152 
03153 Side effects:
03154    This executes a straight move command at cutting feed rate.
03155    It also generates an arc cut to go around a corner, if necessary.
03156    It also updates the setting of the position of the tool point to
03157    the end point of the move and updates the programmed point.
03158 
03159 Called by: convert_straight.
03160 
03161 This is called if cutter radius compensation is on and
03162 settings->program_x is not UNKNOWN, indicating that this is not the
03163 first move after cutter radius compensation is turned on.
03164 
03165 The algorithm used here is:
03166 1. Determine the direction of the last motion. This is done by finding
03167    the direction of the line from the last programmed point to the
03168    current tool tip location. This line is a radius of the tool and is
03169    perpendicular to the direction of motion since the cutter is tangent
03170    to that direction.
03171 2. Determine the direction of the programmed motion.
03172 3. If there is a convex corner, insert an arc to go around the corner.
03173 4. Find the destination point for the tool tip. The tool will be
03174    tangent to the line from the last programmed point to the present
03175    programmed point at the present programmed point.
03176 5. Go in a straight line from the current tool tip location to the
03177    destination tool tip location.
03178 
03179 This uses an angle tolerance of 0.001 radian to determine if:
03180 1) an illegal concave corner exists (tool will not fit into corner),
03181 2) no arc is required to go around the corner (i.e. the current line
03182    is in the same direction as the end of the previous move), or
03183 3) an arc is required to go around a convex corner and start off in
03184    a new direction.
03185 
03186 If the B-axis is moved in this block and an extra arc is required to go
03187 around a sharp corner, all the B-axis motion occurs on the arc.
03188 An alternative might be to distribute the B-axis motion over the arc
03189 and the straight move in proportion to their lengths.
03190 
03191 */
03192 
03193 int convert_straight_comp2( /* ARGUMENT VALUES                         */
03194  setup_pointer settings,    /* pointer to machine settings             */
03195  double program_end_x,      /* X coordinate of programmed end point    */
03196  double program_end_y,      /* Y coordinate of programmed end point    */
03197  double end_z)              /* Z coordinate of end point               */
03198 {
03199   static char name[] SET_TO "convert_straight_comp2";
03200   double radius;
03201   double cx, cy; /* actual end point */
03202   double dx, dy; /* end of added arc, if needed */
03203   double start_x, start_y; /* programmed beginning point */
03204   double theta;
03205   double alpha;
03206   double beta;
03207   double gamma;
03208   /* small angle in radians for testing corners */
03209   double small SET_TO TOLERANCE_CONCAVE_CORNER;
03210   int side;
03211 
03212   side SET_TO settings->cutter_radius_compensation;
03213   start_x SET_TO settings->program_x;
03214   start_y SET_TO settings->program_y;
03215   if ((start_x IS program_end_x) AND (start_y IS program_end_y))
03216     {
03217       STRAIGHT_FEED(settings->current_x, settings->current_y, end_z);
03218       return RS274NGC_OK;
03219     }
03220   radius SET_TO
03221     (settings->tool_table[settings->tool_table_index].diameter)/2.0;
03222   if (radius <= 0.0)
03223     ERROR_MACRO(_interpreter_linetext, name, "Bad tool radius value with cutter radius comp");
03224   theta SET_TO atan2(settings->current_y - start_y,
03225                      settings->current_x - start_x);
03226   alpha SET_TO
03227     atan2(program_end_y - start_y, program_end_x - start_x);
03228 
03229   if (side IS LEFT)
03230     {
03231       if (theta < alpha)
03232         theta SET_TO (theta + TWO_PI);
03233       beta SET_TO ((theta - alpha) - PI2);
03234       gamma SET_TO PI2;
03235     }
03236   else if (side IS RIGHT)
03237     {
03238       if (alpha < theta)
03239         alpha SET_TO (alpha + TWO_PI);
03240       beta SET_TO ((alpha - theta) - PI2);
03241       gamma SET_TO -PI2;
03242     }
03243   else
03244     BUG_MACRO(name,
03245               "Side fails to be right or left in convert_straight_comp2");
03246   cx SET_TO (program_end_x + (radius * cos(alpha + gamma)));
03247   cy SET_TO (program_end_y + (radius * sin(alpha + gamma)));
03248   dx SET_TO (start_x + (radius * cos(alpha + gamma)));
03249   dy SET_TO (start_y + (radius * sin(alpha + gamma)));
03250 
03251   if ((beta < -small) OR (beta > (PI + small)))
03252     ERROR_MACRO(_interpreter_linetext, name, "Cannot have concave corner with cutter radius comp");
03253   else if (beta > small) /* ARC NEEDED */
03254     {
03255       ARC_FEED(dx, dy, start_x, start_y, (side IS LEFT) ? -1 : 1, end_z);
03256       STRAIGHT_FEED (cx, cy, end_z);
03257     }
03258   else STRAIGHT_FEED (cx, cy, end_z);
03259 
03260   settings->current_x SET_TO cx;
03261   settings->current_y SET_TO cy;
03262   settings->program_x SET_TO program_end_x;
03263   settings->program_y SET_TO program_end_y;
03264   return RS274NGC_OK;
03265 }
03266 
03267 /****************************************************************************/
03268 
03269 /* read_integer_unsigned
03270 
03271 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
03272    If any of the following errors occur, RS274NGC_ERROR is returned.
03273    Otherwise, RS274NGC_OK is returned.
03274    1. The first character is not a digit.
03275    2. BUG - sscanf fails.
03276 
03277 Side effects:
03278    The number read from the line is put into what integer_ptr points at.
03279 
03280 Called by:
03281    read_line_number
03282 
03283 This reads an explicit unsigned (positive) integer from a string,
03284 starting from the position given by *counter. It expects to find one
03285 or more digits. Any character other than a digit terminates reading
03286 the integer. Note that if the first character is a sign (+ or -),
03287 an error will be reported (since a sign is not a digit).
03288 
03289 */
03290 
03291 int read_integer_unsigned(   /* ARGUMENT VALUES                        */
03292  char * line,         /* string: line of RS274 code being processed    */
03293  int * counter,       /* pointer to a counter for position on the line */
03294  int * integer_ptr)   /* pointer to the value being read               */
03295 {
03296   static char name[] SET_TO "read_integer_unsigned";
03297   int n;
03298   char c;
03299 
03300   for (n SET_TO *counter; ; n++)
03301     {
03302       c SET_TO line[n];
03303       if ((c < 48) OR (c > 57))
03304         break;
03305     }
03306   if (n IS *counter)
03307     ERROR_MACRO(_interpreter_linetext, name, "Bad format unsigned integer");
03308   if (sscanf(line + *counter, "%d", integer_ptr) IS 0)
03309     BUG_MACRO(name, "Sscanf failure in read_integer_unsigned");
03310   else
03311     {
03312       *counter SET_TO n;
03313       return RS274NGC_OK;
03314     }
03315 }
03316 
03317 /****************************************************************************/
03318 
03319 /* read_real_value
03320 
03321 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
03322    If no characters are found before the end of the line or
03323    if a call to read_real_expression, read_parameter, read_unary,
03324    or read_real_number returns RS274NGC_ERROR, this returns RS274NGC_ERROR.
03325    Otherwise, this returns RS274NGC_OK.
03326 
03327 Side effects:
03328    The value read from the line is put into what double_ptr points at.
03329    The counter is reset to point to the first character after the
03330    characters which make up the value.
03331 
03332 Called by:
03333    read_f
03334    read_g
03335    read_i
03336    read_integer_value
03337    read_j
03338    read_k
03339    read_parameter
03340    read_parameter_setting
03341    read_q
03342    read_r
03343    read_real_expression
03344    read_spindle_speed
03345    read_x
03346    read_y
03347    read_z
03348 
03349 This attempts to read a real value out of the line, starting at the
03350 index given by the counter. The value may be a number, a parameter
03351 value, a unary function, or an expression. It calls one of four
03352 other readers, depending upon the first character.
03353 
03354 
03355 */
03356 
03357 int read_real_value(  /* ARGUMENT VALUES                                */
03358  char * line,         /* string: line of RS274/NGC code being processed */
03359  int * counter,       /* pointer to a counter for position on the line  */
03360  double * double_ptr, /* pointer to double to be read                   */
03361  double * parameters) /* array of system parameters                     */
03362 {
03363   static char name[] SET_TO "read_real_value";
03364   char c;
03365 
03366   c SET_TO line[*counter];
03367   if (c IS 0)
03368     ERROR_MACRO(_interpreter_linetext, name, "No characters found in reading real value");
03369   else if (c IS '[')
03370     return read_real_expression (line, counter, double_ptr, parameters);
03371   else if (c IS '#')
03372     return read_parameter(line, counter, double_ptr, parameters);
03373   else if ((c >= 'a') AND (c <= 'z'))
03374     return read_unary(line, counter, double_ptr, parameters);
03375   else
03376     return read_real_number(line, counter, double_ptr);
03377 }
03378 
03379 /****************************************************************************/
03380 
03381 /* utility_find_ends
03382 
03383 Returned Value: int (RS274NGC_OK)
03384 
03385 Side effects:
03386    The values of px, py, and pz are set.
03387 
03388 Called by:
03389    convert_arc
03390    convert_straight
03391 
03392 This finds the end point of a straight line or arc.
03393 
03394 */
03395 
03396 int utility_find_ends(   /* ARGUMENT VALUES                              */
03397  block_pointer block,    /* pointer to a block of RS274/NGC instructions */
03398  setup_pointer settings, /* pointer to machine settings                  */
03399  double * px,            /* pointer to end_x                             */
03400  double * py,            /* pointer to end_y                             */
03401  double * pz)            /* pointer to end_z                             */
03402 {
03403   int mode;
03404   int middle;
03405   int comp;
03406 
03407   mode SET_TO settings->distance_mode;
03408   middle SET_TO (settings->program_x ISNT UNKNOWN);
03409   comp SET_TO (settings->cutter_radius_compensation ISNT OFF);
03410 
03411   if (block->g_modes[4] IS G_53) /* mode is absolute in this case */
03412     {
03413 #ifdef DEBUG_EMC
03414       COMMENT("interpreter: offsets temporarily suspended");
03415 #endif
03416       *px SET_TO (block->x_flag IS ON) ? (block->x_number -
03417                    (settings->origin_offset_x + settings->axis_offset_x)) :
03418                      settings->current_x;
03419       *py SET_TO (block->y_flag IS ON) ? (block->y_number -
03420                    (settings->origin_offset_y + settings->axis_offset_y)) :
03421                      settings->current_y;
03422       *pz SET_TO (block->z_flag IS ON) ? (block->z_number -
03423                    (settings->tool_length_offset + settings->origin_offset_z
03424                     + settings->axis_offset_z)) : settings->current_z;
03425     }
03426   else if (mode IS MODE_ABSOLUTE)
03427     {
03428       *px SET_TO (block->x_flag IS ON) ? block->x_number     :
03429                  (comp AND middle)     ? settings->program_x :
03430                                          settings->current_x ;
03431 
03432       *py SET_TO (block->y_flag IS ON) ? block->y_number     :
03433                  (comp AND middle)     ? settings->program_y :
03434                                          settings->current_y ;
03435 
03436       *pz SET_TO (block->z_flag IS ON) ? block->z_number     :
03437                                          settings->current_z ;
03438     }
03439   else
03440     {
03441       *px SET_TO (block->x_flag IS ON)
03442         ? ((comp AND middle) ? (block->x_number + settings->program_x)
03443                              : (block->x_number + settings->current_x))
03444         : ((comp AND middle) ? settings->program_x
03445                              : settings->current_x);
03446 
03447       *py SET_TO (block->y_flag IS ON)
03448         ? ((comp AND middle) ? (block->y_number + settings->program_y)
03449                              : (block->y_number + settings->current_y))
03450         : ((comp AND middle) ? settings->program_y
03451                              : settings->current_y);
03452 
03453       *pz SET_TO (block->z_flag IS ON)
03454         ? (settings->current_z + block->z_number)
03455         : settings->current_z;
03456     }
03457 
03458   return RS274NGC_OK;
03459 }
03460 
03461 /****************************************************************************/
03462 
03463 /* convert_arc
03464 
03465 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
03466    This returns RS274NGC_OK unless one of the following errors occurs,
03467    in which case it returns RS274NGC_ERROR.
03468    1. The block has neither an r value nor any i,j,k values.
03469    2. The block has both an r value and one or more i,j,k values.
03470    3. In the ijk format the XY-plane is selected and either:
03471       a. the block has a k value, or
03472       b. the block has no i value and no j value.
03473    4. In the ijk format the YZ-plane is selected and either:
03474       a. the block has an i value, or
03475       b. the block has no j value and no k value.
03476    5. In the ijk format the XZ-plane is selected and either:
03477       a. the block has a j value, or
03478       b. the block has no i value and no k value.
03479    6. In either format any of the following occurs.
03480       a. The XY-plane is selected and the block has no x or y value.
03481       b. The XY-plane is selected, cutter radius compensation is on,
03482          and inverse time feed is used.
03483       c. The YZ-plane is selected and the block has no y or z value.
03484       d. The ZX-plane is selected and the block has no z or x value.
03485    7. BUG - The selected plane is an unknown plane.
03486    8. One of the subordinate arc making functions is called and returns
03487       RS274NGC_ERROR.
03488    9. The feed rate is zero.
03489 
03490 Side effects:
03491    This generates and executes an arc command at feed rate
03492    (and, possibly a second arc command). It also updates the setting
03493    of the position of the tool point to the end point of the move.
03494 
03495 Called by: convert_motion.
03496 
03497 This converts a helical or circular arc.  The function calls one of
03498 five convert_arc_XXX functions (three for for the xy plane and one
03499 each for the yz and zx plane) according to the currently selected
03500 plane and whether cutter radius compensation is in effect.
03501 
03502 If the ijk format is used, at least one of the offsets in the current
03503 plane must be given in the block; it is common but not required to
03504 give both offsets. The offsets are always incremental [NCMS, page 21].
03505 
03506 */
03507 
03508 int convert_arc(         /* ARGUMENT VALUES                              */
03509  int move,               /* either G_2 (cw arc) or G_3 (ccw arc)         */
03510  block_pointer block,    /* pointer to a block of RS274/NGC instructions */
03511  setup_pointer settings) /* pointer to machine settings                  */
03512 {
03513   static char name[] SET_TO "convert_arc";
03514   int status;
03515   DISTANCE_MODE mode;
03516   int first;
03517   int ijk_flag;
03518   double end_x;
03519   double end_y;
03520   double end_z;
03521 
03522   mode SET_TO settings->distance_mode;
03523   ijk_flag SET_TO
03524     ((block->i_flag OR block->j_flag) OR block->k_flag) ? ON : OFF;
03525   first SET_TO (settings->program_x IS UNKNOWN);
03526 
03527   if ((block->r_flag ISNT ON) AND (ijk_flag ISNT ON))
03528     ERROR_MACRO(_interpreter_linetext, name, "R, I, J, and K words all missing for arc");
03529   else if ((block->r_flag IS ON) AND (ijk_flag IS ON))
03530     ERROR_MACRO(_interpreter_linetext, name, "Mixed radius-ijk format for arc");
03531   else if (settings->feed_rate IS 0.0)
03532     ERROR_MACRO(_interpreter_linetext, name, "Cannot make arc with zero feed rate");
03533   else if (ijk_flag)
03534     {
03535       if (settings->plane IS CANON_PLANE_XY)
03536         {
03537           if (block->k_flag)
03538             ERROR_MACRO(_interpreter_linetext, name, "K word given for arc in XY-plane");
03539           else if (block->i_flag IS OFF) /* i or j flag on to get here */
03540               block->i_number SET_TO 0.0;
03541           else if (block->j_flag IS OFF)
03542             block->j_number SET_TO 0.0;
03543         }
03544       else if (settings->plane IS CANON_PLANE_YZ)
03545         {
03546           if (block->i_flag)
03547             ERROR_MACRO(_interpreter_linetext, name, "I word given for arc in YZ-plane");
03548           else if (block->j_flag IS OFF) /* j or k flag on to get here */
03549               block->j_number SET_TO 0.0;
03550           else if (block->k_flag IS OFF)
03551             block->k_number SET_TO 0.0;
03552         }
03553       else if (settings->plane IS CANON_PLANE_XZ)
03554         {
03555           if (block->j_flag)
03556             ERROR_MACRO(_interpreter_linetext, name, "J word given for arc in XZ-plane");
03557           else if (block->i_flag IS OFF) /* i or k flag on to get here */
03558               block->i_number SET_TO 0.0;
03559           else if (block->k_flag IS OFF)
03560             block->k_number SET_TO 0.0;
03561         }
03562       else
03563         BUG_MACRO(name, "Plane is not XY, YZ, or XZ in convert_arc");
03564     }
03565   else; /* r format arc; no other checks needed specific to this format */
03566 
03567   if (settings->plane IS CANON_PLANE_XY) /* checks for both formats */
03568     {
03569       if ((block->x_flag IS OFF) AND (block->y_flag IS OFF))
03570         ERROR_MACRO(_interpreter_linetext, name, "X and Y words missing for arc in XY-plane");
03571     }
03572   else if (settings->plane IS CANON_PLANE_YZ)
03573     {
03574       if ((block->y_flag IS OFF) AND (block->z_flag IS OFF))
03575         ERROR_MACRO(_interpreter_linetext, name, "Y and Z words missing for arc in YZ-plane");
03576     }
03577   else if (settings->plane IS CANON_PLANE_XZ)
03578     {
03579       if ((block->x_flag IS OFF) AND (block->z_flag IS OFF))
03580         ERROR_MACRO(_interpreter_linetext, name, "X and Z words missing for arc in XZ-plane");
03581     }
03582 
03583   utility_find_ends(block, settings, &end_x, &end_y, &end_z);
03584 
03585   settings->motion_mode SET_TO move;
03586 
03587   if (settings->plane IS CANON_PLANE_XY)
03588     {
03589       if (settings->cutter_radius_compensation IS OFF)
03590         status SET_TO convert_arc_xy(move, block, settings, end_x, end_y,
03591                                      end_z);
03592       else if (settings->feed_mode IS INVERSE_TIME)
03593         ERROR_MACRO(_interpreter_linetext, name,
03594                      "Cannot use inverse time feed with cutter radius comp");
03595       else if (first)
03596         status SET_TO convert_arc_comp1
03597           (move, block, settings, end_x, end_y, end_z);
03598       else
03599         status SET_TO convert_arc_comp2
03600           (move, block, settings, end_x, end_y, end_z);
03601     }
03602   else if (settings->plane IS CANON_PLANE_XZ)
03603     status SET_TO convert_arc_zx (move, block, settings, end_z, end_x, end_y);
03604   else if (settings->plane IS CANON_PLANE_YZ)
03605     status SET_TO convert_arc_yz (move, block, settings, end_y, end_z, end_x);
03606   else
03607    BUG_MACRO(name, "Plane is not XY, YZ, or XZ in convert_arc");
03608 
03609   if (status ISNT RS274NGC_OK)
03610     ERROR_MACRO_PASS(name);
03611   else
03612     return RS274NGC_OK;
03613 }
03614 
03615 /****************************************************************************/
03616 
03617 /* convert_axis_offsets
03618 
03619 Returned Value: int
03620    If any of the following errors occur, this returns RS274NGC_ERROR.
03621    Otherwise, it returns RS274NGC_OK.
03622    1. The function is called when cutter radius compensation is on.
03623    2. BUG - g_code is not G_92 or G_92_2.
03624 
03625 Side effects:
03626    SET_PROGRAM_ORIGIN is called, and the coordinate
03627    values for the axis offsets are reset. The coordinates of the
03628    current point are reset.
03629 
03630 Called by: convert_modal_0.
03631 
03632 The action of G92 is described in [NCMS, pages 10 - 11] and {Fanuc,
03633 pages 61 - 63]. [NCMS] is ambiguous about the intent, but [Fanuc]
03634 is clear. When G92 is executed, an offset of the origin is calculated
03635 so that the coordinates of the current point with respect to the moved
03636 origin are as specified on the line containing the G92. If an axis
03637 is not mentioned on the line, the coordinates of the current point
03638 are not changed. The execution of G92 results in an axis offset being
03639 calculated and saved for each of the five axes, and the axis offsets
03640 are always used when motion is specified with respect to absolute
03641 distance mode using any of the nine coordinate systems (those designated
03642 by G54 - G59.3). Thus all nine coordinate systems are affected by G92.
03643 
03644 Being in incremental distance mode has no effect on the action of G92
03645 in this implementation. [NCMS] is not explicit about this, but it is
03646 implicit in the second sentence of [Fanuc, page 61].
03647 
03648 The offset is the amount the origin must be moved so that the
03649 coordinate of the controlled point has the specified value. For
03650 example if the current point is at X=4 in the currently specified
03651 coordinate system, then "G92 x7" causes the X-axis offset to be -3.
03652 
03653 Since a non-zero offset may be already be in effect when the G92 is
03654 called, that must be taken into account.
03655 
03656 The action of G92.2 is described in [NCMS, page 12]. G92.2 resets axis
03657 offsets to zero.
03658 
03659 These offset values are saved in parameters 5211-5213, which were not
03660 assigned in [NCMS] but look like reasonable places to put them.
03661 */
03662 
03663 int convert_axis_offsets( /* ARGUMENT VALUES                                */
03664  int g_code,              /* g_code being executed (must be G_92 or G_92_2) */
03665  block_pointer block,     /* pointer to a block of RS274/NGC instructions   */
03666  setup_pointer settings)  /* pointer to machine settings                    */
03667 {
03668   static char name[] SET_TO "convert_axis_offsets";
03669 
03670   if (settings->cutter_radius_compensation ISNT OFF) /* not "IS ON" */
03671     ERROR_MACRO(_interpreter_linetext, name, "Cannot change axis offsets with cutter radius comp");
03672   else if (g_code IS G_92)
03673     {
03674       if (block->x_flag IS ON)
03675         {
03676           settings->axis_offset_x SET_TO
03677             (settings->current_x + settings->axis_offset_x - block->x_number);
03678           settings->current_x SET_TO block->x_number;
03679         }
03680 
03681       if (block->y_flag IS ON)
03682         {
03683           settings->axis_offset_y SET_TO
03684             (settings->current_y + settings->axis_offset_y - block->y_number);
03685           settings->current_y SET_TO block->y_number;
03686         }
03687 
03688       if (block->z_flag IS ON)
03689         {
03690           settings->axis_offset_z SET_TO
03691             (settings->current_z + settings->axis_offset_z - block->z_number);
03692           settings->current_z SET_TO block->z_number;
03693         }
03694 
03695       SET_ORIGIN_OFFSETS(settings->origin_offset_x + settings->axis_offset_x,
03696                          settings->origin_offset_y + settings->axis_offset_y,
03697                          settings->origin_offset_z + settings->axis_offset_z);
03698 
03699       settings->parameters[5211] SET_TO settings->axis_offset_x;
03700       settings->parameters[5212] SET_TO settings->axis_offset_y;
03701       settings->parameters[5213] SET_TO settings->axis_offset_z;
03702     }
03703   else if (g_code IS G_92_2)
03704     {
03705       settings->current_x SET_TO
03706         settings->current_x + settings->axis_offset_x;
03707       settings->current_y SET_TO
03708         settings->current_y + settings->axis_offset_y;
03709       settings->current_z SET_TO
03710         settings->current_z + settings->axis_offset_z;
03711       SET_ORIGIN_OFFSETS(settings->origin_offset_x,
03712                          settings->origin_offset_y,
03713                          settings->origin_offset_z);
03714       settings->axis_offset_x SET_TO 0.0;
03715       settings->axis_offset_y SET_TO 0.0;
03716       settings->axis_offset_z SET_TO 0.0;
03717 
03718       settings->parameters[5211] SET_TO 0.0;
03719       settings->parameters[5212] SET_TO 0.0;
03720       settings->parameters[5213] SET_TO 0.0;
03721     }
03722   else
03723     BUG_MACRO(name, "Code is not G92 or G92.2 in convert_axis_offsets");
03724 
03725   return RS274NGC_OK;
03726 }
03727 
03728 /****************************************************************************/
03729 
03730 /* convert_cutter_compensation_off
03731 
03732 Returned Value: int (RS274NGC_OK)
03733 
03734 Side effects:
03735    A comment is made that cutter radius compensation is turned off.
03736    The machine model of the cutter radius compensation mode is set to OFF.
03737    The value of program_x in the machine model is set to UNKNOWN.
03738      This serves as a flag when cutter radius compensation is
03739      turned on again.
03740 
03741 Called by: convert_cutter_compensation
03742 
03743 */
03744 
03745 int convert_cutter_compensation_off( /* ARGUMENT VALUES             */
03746  setup_pointer settings)             /* pointer to machine settings */
03747 {
03748 #ifdef DEBUG_EMC
03749   COMMENT("interpreter: cutter radius compensation off");
03750 #endif
03751   settings->cutter_radius_compensation SET_TO OFF;
03752   settings->program_x SET_TO UNKNOWN;
03753   return RS274NGC_OK;
03754 }
03755 
03756 /****************************************************************************/
03757 
03758 /* convert_cutter_compensation_on
03759 
03760 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
03761    If any of the following errors occur, this returns RS274NGC_ERROR.
03762    Otherwise, it returns RS274NGC_OK.
03763    1. The selected plane is not the XY plane.
03764    2. There is no D word in the block.
03765    3. Cutter radius compensation is already on.
03766 
03767 Side effects:
03768    A COMMENT function call is made (conditionally) saying that the
03769    interpreter is switching mode so that cutter radius compensation is on.
03770    The value of cutter_radius_compensation in the machine model mode is
03771    set to RIGHT or LEFT. The currently active tool table index in
03772    the machine model is updated.
03773 
03774 Called by: convert_cutter_compensation
03775 
03776 check_other_codes checks that a d word occurs only in a block with g41 or g42.
03777 
03778 Cutter radius compensation is carried out in the interpreter, so no
03779 call is made to a canonical function (although there is a canonical
03780 function, START_CUTTER_RADIUS_COMPENSATION, that could be called if
03781 the primitive level could execute it).
03782 
03783 */
03784 
03785 int convert_cutter_compensation_on( /* ARGUMENT VALUES                */
03786  int side,               /* side of path cutter is on (LEFT or RIGHT) */
03787  block_pointer block,    /* pointer to a block of RS274 instructions  */
03788  setup_pointer settings) /* pointer to machine settings               */
03789 {
03790   static char name[] SET_TO "convert_cutter_compensation_on";
03791 
03792   if (settings->plane ISNT CANON_PLANE_XY)
03793     ERROR_MACRO(_interpreter_linetext, name, "Cannot turn cutter radius comp on out of XY-plane");
03794   if (block->d_number IS -1)
03795     ERROR_MACRO(_interpreter_linetext, name, "D word missing with cutter radius comp on");
03796   if (settings->cutter_radius_compensation ISNT OFF)
03797     ERROR_MACRO(_interpreter_linetext, name, "Cannot turn cutter radius comp on when already on");
03798 
03799 #ifdef DEBUG_EMC
03800   if (side IS RIGHT)
03801     COMMENT("interpreter: cutter radius compensation on right");
03802   else
03803     COMMENT("interpreter: cutter radius compensation on left");
03804 #endif
03805 
03806   /* radius is (settings->tool_table[block->d_number].diameter)/2.0) */
03807   settings->tool_table_index SET_TO block->d_number;
03808   settings->cutter_radius_compensation SET_TO side;
03809   return RS274NGC_OK;
03810 }
03811 
03812 /****************************************************************************/
03813 /* convert_cycle
03814 
03815 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
03816    If any of the following errors occur, this returns RS274NGC_ERROR.
03817    Otherwise, it returns RS274NGC_OK.
03818    1. The r-value is not given the first time this code is called after
03819       some other motion mode has been in effect.
03820    2. The z-value is not given the first time this code is called after
03821       some other motion mode has been in effect.
03822    3. The r clearance plane is below the bottom_z.
03823    4. BUG - the distance mode is neither absolute or incremental.
03824    5. The l number is zero.
03825    6. G82, G86, G88, or G89 is called when it is not already in effect,
03826       and no p number is in the block.
03827    7. G83 is called when it is not already in effect,
03828       and no q number is in the block.
03829    8. G87 is called when it is not already in effect,
03830       and any of the i number, j number, or k number is missing.
03831    9. The distance mode setting is neither incremental nor absolute.
03832   10. BUG - the G code is not between G_81 and G_89.
03833   11. One of the specific cycle functions called returns RS274NGC_ERROR.
03834 
03835 Side effects:
03836    A number of moves are made to execute the g-code
03837 
03838 Called by: convert_motion
03839 
03840 The function does not require that any of x,y,z, or r be specified in
03841 the block, except that if the last motion mode command executed was
03842 not the same as this one, the r-value and z-value must be specified.
03843 
03844 This function is handling the repeat feature, wherein
03845 the L word represents the number of repeats [NCMS, page 99]. We are
03846 not allowing L=0, contrary to the manual. We are allowing L > 1
03847 in absolute distance mode to mean "do the same thing in the same
03848 place several times", as provided in the manual, although this seems
03849 abnormal.
03850 
03851 In incremental distance mode, x, y, and r values are treated as
03852 increments to the current position and z as an increment from r.  In
03853 absolute distance mode, x, y, r, and z are absolute. In g87, i and j
03854 will always be increments, regardless of the distance mode setting, as
03855 implied in [NCMS, page 98], but k (z-value of top of counterbore) will
03856 be an absolute z-value in absolute distance mode, and an increment
03857 (from bottom z) in incremental distance mode.
03858 
03859 If the r position of a cycle is above the current_z position, this
03860 retracts the z-axis to the r position before moving parallel to the
03861 XY plane.
03862 
03863 In the code for this function, there is a nearly identical "for" loop
03864 in every case of the switch. The loop is the done with a compiler
03865 macro, "CYCLE_MACRO" so that the code is easy to read, automatically
03866 kept identical from case to case and, and much shorter than it would
03867 be without the macro. The loop could be put outside the switch, but
03868 then the switch would run every time around the loop, not just once,
03869 as it does here. The loop could also be placed in the called
03870 functions, but then it would not be clear that all the loops are the
03871 same, and it would be hard to keep them the same when the code is
03872 modified.  The macro would be very awkward as a regular function
03873 because it would have to be passed all of the arguments used by any of
03874 the specific cycles, and, if another switch in the function is to be
03875 avoided, it would have to passed a function pointer, but the different
03876 cycle functions have different arguments so the type of the pointer
03877 could not be declared unless the cycle functions were re-written to
03878 take the same arguments (in which case most of them would have several
03879 unused arguments).
03880 
03881 The motions within the CYCLE_MACRO (but outside a specific cycle) are
03882 a straight traverse parallel to the XY-plane to the given xy-position
03883 and a straight traverse of the z-axis only (if needed) to the r
03884 position.
03885 
03886 The height of the retract move at the end of each repeat of a cycle is
03887 determined by the setting of the retract_mode: either to the r
03888 position (if the retract_mode is R_PLANE) or to the original
03889 z-position (if that is above the r position and the retract_mode is
03890 not R_PLANE). This is a slight departure from [NCMS, page 98], which
03891 does not require checking that the original z-position is above r.
03892 
03893 */
03894 
03895 #define CYCLE_MACRO(call)                       \
03896 for (repeat SET_TO block->l_number;             \
03897      repeat > 0;                                \
03898      repeat--)                                  \
03899 {                                               \
03900   x SET_TO (x + x_increment);                   \
03901   y SET_TO (y + y_increment);                   \
03902   STRAIGHT_TRAVERSE(x, y, old_z);               \
03903   if (old_z ISNT r)                             \
03904     STRAIGHT_TRAVERSE(x, y, r);                 \
03905   if (call IS RS274NGC_ERROR)                   \
03906     ERROR_MACRO_PASS(name);                     \
03907   old_z SET_TO clear_z;                         \
03908 }
03909 
03910 int convert_cycle(       /* ARGUMENT VALUES                                */
03911  int motion,             /* a g-code between G_81 and G_89, a canned cycle */
03912  block_pointer block,    /* pointer to a block of RS274/NGC instructions   */
03913  setup_pointer settings) /* pointer to machine settings                    */
03914 {
03915   static char name[] SET_TO "convert_cycle";
03916   double x_increment;
03917   double y_increment;
03918   double i;
03919   double j;
03920   double k;
03921   double x;
03922   double y;
03923   double clear_z;
03924   double old_z;
03925   double z;
03926   double r;
03927   int repeat;
03928   CANON_MOTION_MODE save_mode;
03929 
03930   if (settings->motion_mode ISNT motion)
03931     {
03932       if (block->r_flag IS OFF)
03933         ERROR_MACRO(_interpreter_linetext, name, "R clearance plane unspecified in canned cycle");
03934       if (block->z_flag IS OFF)
03935         ERROR_MACRO(_interpreter_linetext, name, "Z value unspecified in canned cycle");
03936     }
03937 
03938   block->r_number SET_TO
03939     block->r_flag IS ON ? block->r_number : settings->cycle_r;
03940   block->z_number SET_TO
03941     block->z_flag IS ON ? block->z_number : settings->cycle_z;
03942   old_z SET_TO settings->current_z;
03943 
03944   if (settings->distance_mode IS MODE_ABSOLUTE)
03945     {
03946       x_increment SET_TO 0.0;
03947       y_increment SET_TO 0.0;
03948       r SET_TO block->r_number;
03949       z SET_TO block->z_number;
03950       x SET_TO block->x_flag IS ON ? block->x_number : settings->current_x;
03951       y SET_TO block->y_flag IS ON ? block->y_number : settings->current_y;
03952     }
03953   else if (settings->distance_mode IS MODE_INCREMENTAL)
03954     {
03955       x_increment SET_TO block->x_number;
03956       y_increment SET_TO block->y_number;
03957       r SET_TO (block->r_number + old_z);
03958       z SET_TO (r + block->z_number); /* [NCMS, page 98] */
03959       x SET_TO settings->current_x;
03960       y SET_TO settings->current_y;
03961     }
03962   else
03963     BUG_MACRO(name,
03964         "Distance mode is neither absolute nor incremental in convert_cycle");
03965 
03966   if (r < z)
03967     ERROR_MACRO(_interpreter_linetext, name, "R value less than Z value in canned cycle");
03968 
03969   if (block->l_number IS -1)
03970     block->l_number SET_TO 1;
03971   else if (block->l_number IS 0)
03972     ERROR_MACRO(_interpreter_linetext, name, "Cannot do zero repeats of cycle");
03973 
03974   if (old_z < r)
03975     {
03976       STRAIGHT_TRAVERSE(settings->current_x, settings->current_y, r);
03977       old_z SET_TO r;
03978     }
03979   clear_z SET_TO (settings->retract_mode IS R_PLANE) ? r : old_z;
03980 
03981   save_mode = GET_MOTION_CONTROL_MODE();
03982   if (save_mode ISNT CANON_EXACT_PATH)
03983     SET_MOTION_CONTROL_MODE(CANON_EXACT_PATH);
03984 
03985   switch(motion)
03986     {
03987     case G_81:
03988       CYCLE_MACRO(convert_cycle_g81(x, y, clear_z, z))
03989       break;
03990     case G_82:
03991       if ((settings->motion_mode ISNT G_82) AND (block->p_number IS -1.0))
03992         ERROR_MACRO(_interpreter_linetext, name, "P word (dwell time) missing with G82");
03993       block->p_number SET_TO
03994         block->p_number IS -1.0 ? settings->cycle_p : block->p_number;
03995       CYCLE_MACRO(convert_cycle_g82 (x, y, clear_z, z, block->p_number))
03996       settings->cycle_p SET_TO block->p_number;
03997       break;
03998     case G_83:
03999       if ((settings->motion_mode ISNT G_83) AND (block->q_number IS -1.0))
04000         ERROR_MACRO(_interpreter_linetext, name, "Q word (depth increment) missing with G83");
04001       block->q_number SET_TO
04002         block->q_number IS -1.0 ? settings->cycle_q : block->q_number;
04003       CYCLE_MACRO(convert_cycle_g83 (x, y, r, clear_z, z, block->q_number))
04004       settings->cycle_q SET_TO block->q_number;
04005       break;
04006     case G_84:
04007       CYCLE_MACRO(convert_cycle_g84 (x, y, clear_z, z,
04008                       settings->spindle_turning, settings->speed_feed_mode))
04009       break;
04010     case G_85:
04011       CYCLE_MACRO(convert_cycle_g85 (x, y, clear_z, z))
04012       break;
04013     case G_86:
04014       if ((settings->motion_mode ISNT G_86) AND (block->p_number IS -1.0))
04015         ERROR_MACRO(_interpreter_linetext, name, "P word (dwell time) missing with G86");
04016       block->p_number SET_TO
04017         block->p_number IS -1.0 ? settings->cycle_p : block->p_number;
04018       CYCLE_MACRO(convert_cycle_g86 (x, y, clear_z, z, block->p_number,
04019                                  settings->spindle_turning))
04020       settings->cycle_p SET_TO block->p_number;
04021       break;
04022     case G_87:
04023       if (settings->motion_mode ISNT G_87)
04024         {
04025           if (block->i_flag IS OFF)
04026             ERROR_MACRO(_interpreter_linetext, name, "I word missing with G87");
04027           if (block->j_flag IS OFF)
04028             ERROR_MACRO(_interpreter_linetext, name, "J word missing with G87");
04029           if (block->k_flag IS OFF)
04030             ERROR_MACRO(_interpreter_linetext, name, "K word missing with G87");
04031         }
04032       i SET_TO block->i_flag IS ON ? block->i_number : settings->cycle_i;
04033       j SET_TO block->j_flag IS ON ? block->j_number : settings->cycle_j;
04034       k SET_TO block->k_flag IS ON ? block->k_number : settings->cycle_k;
04035       settings->cycle_i SET_TO i;
04036       settings->cycle_j SET_TO j;
04037       settings->cycle_k SET_TO k;
04038       if (settings->distance_mode IS MODE_INCREMENTAL)
04039         {
04040           k SET_TO (z + k); /* k always absolute in function call below */
04041         }
04042       CYCLE_MACRO(convert_cycle_g87 (x, (x + i), y, (y + j), r, clear_z, k,
04043                                  z, settings->spindle_turning))
04044       break;
04045     case G_88:
04046       if ((settings->motion_mode ISNT G_88) AND (block->p_number IS -1.0))
04047         ERROR_MACRO(_interpreter_linetext, name, "P word (dwell time) missing with G88");
04048       block->p_number SET_TO
04049         block->p_number IS -1.0 ? settings->cycle_p : block->p_number;
04050       CYCLE_MACRO(convert_cycle_g88 (x, y, z, block->p_number,
04051                                  settings->spindle_turning))
04052       settings->cycle_p SET_TO block->p_number;
04053       break;
04054     case G_89:
04055       if ((settings->motion_mode ISNT G_89) AND (block->p_number IS -1.0))
04056         ERROR_MACRO(_interpreter_linetext, name, "P word (dwell time) missing with G89");
04057       block->p_number SET_TO
04058         block->p_number IS -1.0 ? settings->cycle_p : block->p_number;
04059       CYCLE_MACRO(convert_cycle_g89 (x, y, clear_z, z, block->p_number))
04060       settings->cycle_p SET_TO block->p_number;
04061       break;
04062     default:
04063       BUG_MACRO(name, "Convert_cycle should not have been called");
04064     }
04065   settings->current_x SET_TO x;
04066   settings->current_y SET_TO y;
04067   settings->current_z SET_TO clear_z;
04068   settings->cycle_l SET_TO block->l_number;
04069   settings->cycle_r SET_TO block->r_number;
04070   settings->cycle_z SET_TO block->z_number;
04071   settings->motion_mode SET_TO motion;
04072 
04073   if (save_mode ISNT CANON_EXACT_PATH)
04074     SET_MOTION_CONTROL_MODE(save_mode);
04075 
04076   return RS274NGC_OK;
04077 }
04078 
04079 /****************************************************************************/
04080 
04081 /* convert_probe
04082 
04083 Returned Value: int
04084    If any of the following errors occur, this returns RS274NGC_ERROR.
04085    Otherwise, it returns RS274NGC_OK.
04086    1. No value is given in the block for any of X, Y, or Z.
04087 
04088 Side effects:
04089    This executes a straight_probe command.
04090    The probe_flag in the settings is set to ON.
04091    The motion mode in the settings is set to G_38_2.
04092 
04093 Called by: convert_motion.
04094 
04095 The approach to operating in incremental distance mode (g91) is to
04096 put the the absolute position values into the block before using the
04097 block to generate a move.
04098 
04099 After probing is performed, the location of the probe cannot be
04100 predicted.  This differs from every other command, all of which have
04101 predictable results. The next call to the interpreter (with either
04102 rs274ngc_read or rs274ngc_execute) will result in updating the
04103 current position by a call to get_position. When running stand-alone
04104 the get_position function just returns the current position. To
04105 provide a reasonable value for get_position to return, in stand-alone
04106 mode (only) this function sets the current position nine_tenths of the
04107 way from the old current position to the programmed x, y, z point.
04108 
04109 */
04110 
04111 int convert_probe(       /* ARGUMENT VALUES                          */
04112  block_pointer block,    /* pointer to a block of RS274 instructions */
04113  setup_pointer settings) /* pointer to machine settings              */
04114 {
04115   static char name[] SET_TO "convert_probe";
04116   double end_x;
04117   double end_y;
04118   double end_z;
04119 
04120   if (((block->x_flag IS OFF) AND (block->y_flag IS OFF)) AND
04121       (block->z_flag IS OFF))
04122     ERROR_MACRO(_interpreter_linetext, name, "X, Y, and Z words all missing with G38.2");
04123   utility_find_ends(block, settings, &end_x, &end_y, &end_z);
04124   TURN_PROBE_ON();
04125   STRAIGHT_PROBE (end_x, end_y, end_z);
04126   TURN_PROBE_OFF();
04127   settings->motion_mode SET_TO G_38_2;
04128   settings->probe_flag SET_TO ON;
04129 #ifdef STAND_ALONE_INTERP
04130   settings->current_x SET_TO ((0.9 * end_x) + (0.1 * settings->current_x));
04131   settings->current_y SET_TO ((0.9 * end_y) + (0.1 * settings->current_y));
04132   settings->current_z SET_TO ((0.9 * end_z) + (0.1 * settings->current_z));
04133 #endif
04134   return RS274NGC_OK;
04135 }
04136 
04137 /****************************************************************************/
04138 
04139 /* convert_setup
04140 
04141 Returned Value: int (RS274NGC_OK)
04142 
04143 Side effects:
04144    SET_ORIGIN_OFFSETS is called, and the coordinate values for the origin
04145    offsets in the settings are reset.
04146    If the coordinate system being changed is currently in use, the values
04147    of the the coordinates of the current point are updated.
04148 
04149 Called by: convert_g.
04150 
04151 This is called only if g10 is called. g10 may be used to alter the
04152 second program coordinate system as described in [NCMS, pages 9 - 10].
04153 All nine coordinate systems are implemented here.
04154 
04155 Being in incremental distance mode has no effect on the action of G10
04156 in this implementation. The manual is not explicit about what is
04157 intended.
04158 
04159 See documentation of convert_coordinate_systems for more information.
04160 
04161 */
04162 
04163 int convert_setup(        /* ARGUMENT VALUES                              */
04164  block_pointer block,     /* pointer to a block of RS274/NGC instructions */
04165  setup_pointer settings)  /* pointer to machine settings                  */
04166 {
04167   static char name[] SET_TO "convert_setup";
04168   double x;
04169   double y;
04170   double z;
04171   double * parameters;
04172   int p_int;
04173 
04174   parameters SET_TO settings->parameters;
04175   p_int SET_TO (int)(block->p_number + 0.0001); /* p_number is a double */
04176 
04177   if (block->x_flag IS ON)
04178     {
04179       x SET_TO block->x_number;
04180       parameters[5201 + (p_int * 20)] SET_TO x;
04181     }
04182   else
04183     x SET_TO parameters[5201 + (p_int * 20)];
04184 
04185   if (block->y_flag IS ON)
04186     {
04187       y SET_TO block->y_number;
04188       parameters[5202 + (p_int * 20)] SET_TO y;
04189     }
04190   else
04191     y SET_TO parameters[5202 + (p_int * 20)];
04192 
04193   if (block->z_flag IS ON)
04194     {
04195       z SET_TO block->z_number;
04196       parameters[5203 + (p_int * 20)] SET_TO z;
04197     }
04198   else
04199    z SET_TO parameters[5203 + (p_int * 20)];
04200 
04201 /* axis offsets could be included in the following calculations but
04202    do not need to be because the results do not change */
04203   if (p_int IS settings->origin_ngc) /* system is currently used */
04204     {
04205       settings->current_x SET_TO
04206         (settings->current_x + settings->origin_offset_x);
04207       settings->current_y SET_TO
04208         (settings->current_y + settings->origin_offset_y);
04209       settings->current_z SET_TO
04210         (settings->current_z + settings->origin_offset_z);
04211 
04212       settings->origin_offset_x SET_TO x;
04213       settings->origin_offset_y SET_TO y;
04214       settings->origin_offset_z SET_TO z;
04215 
04216       SET_ORIGIN_OFFSETS(x + settings->axis_offset_x,
04217                          y + settings->axis_offset_y,
04218                          z + settings->axis_offset_z);
04219       settings->current_x SET_TO (settings->current_x - x);
04220       settings->current_y SET_TO (settings->current_y - y);
04221       settings->current_z SET_TO (settings->current_z - z);
04222     }
04223 #ifdef DEBUG_EMC
04224   else
04225     COMMENT("interpreter: setting coordinate system origin");
04226 #endif
04227   return RS274NGC_OK;
04228 }
04229 
04230 /****************************************************************************/
04231 
04232 /* convert_straight
04233 
04234 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
04235    If any of the following errors occur, this returns RS274NGC_ERROR.
04236    Otherwise, it returns RS274NGC_OK.
04237    1. x, y, and z are all missing from the block.
04238    2. convert_straight_comp1 or convert_straight_comp2 is called
04239       and returns RS274NGC_ERROR.
04240    3. A traverse (g0) move is called with cutter radius compensation on.
04241    4. BUG - The value of move is not G_0 or G_1.
04242    5. A straight feed (g1) move is called with feed rate set to 0.
04243    6. A straight feed (g1) move is called with cutter radius compensation
04244       on while inverse time feed is in effect.
04245    7. A straight feed (g1) move is called with inverse time feed in effect
04246       but no f word (feed time) is provided.
04247    8. A G1 move is called with G53 and cutter radius compensation on.
04248 
04249 Side effects:
04250    This executes a straight move command at either traverse feed rate
04251    (if move is 0) or cutting feed rate (if move is 1). It also updates
04252    the setting of the position of the tool point to the end point of
04253    the move. If cutter radius compensation is on, it may also generate
04254    an arc before the straight move.
04255 
04256 Called by: convert_motion.
04257 
04258 The approach to operating in incremental distance mode (g91) is to
04259 put the the absolute position values into the block before using the
04260 block to generate a move.
04261 
04262 */
04263 
04264 int convert_straight(    /* ARGUMENT VALUES                          */
04265  int move,               /* either G_0 (for g0) or G_1 (for g1)      */
04266  block_pointer block,    /* pointer to a block of RS274 instructions */
04267  setup_pointer settings) /* pointer to machine settings              */
04268 {
04269   static char name[] SET_TO "convert_straight";
04270   double end_x = 0.0;
04271   double end_y = 0.0;
04272   double end_z = 0.0;
04273   double length;
04274   double rate;
04275 
04276   if (((block->x_flag IS OFF) AND (block->y_flag IS OFF)) AND
04277       (block->z_flag IS OFF))
04278     ERROR_MACRO(_interpreter_linetext, name, "X, Y, and Z words all missing with G0 or G1");
04279 
04280   settings->motion_mode SET_TO move;
04281   utility_find_ends(block, settings, &end_x, &end_y, &end_z);
04282 
04283   if (move IS G_0)
04284     {
04285       if (settings->cutter_radius_compensation ISNT OFF)
04286         ERROR_MACRO(_interpreter_linetext, name, "Cannot use G0 with cutter radius comp");
04287       STRAIGHT_TRAVERSE(end_x, end_y, end_z);
04288       settings->current_x SET_TO end_x;
04289       settings->current_y SET_TO end_y;
04290     }
04291   else if (move IS G_1)
04292     {
04293       if (settings->cutter_radius_compensation ISNT OFF) /* NOT "IS ON"! */
04294         {
04295           if (settings->feed_mode IS INVERSE_TIME)
04296             ERROR_MACRO(_interpreter_linetext, name,
04297                 "Cannot use inverse time feed with cutter radius comp");
04298           else if (block->g_modes[4] IS G_53)
04299             ERROR_MACRO(_interpreter_linetext, name, "Cannot use G53 with cutter radius comp");
04300           else if (settings->feed_rate IS 0.0)
04301             ERROR_MACRO(_interpreter_linetext, name, "Cannot do G1 with zero feed rate");
04302           else if (settings->program_x IS UNKNOWN)
04303             {
04304               if (convert_straight_comp1(settings, end_x, end_y, end_z)
04305                   IS RS274NGC_ERROR)
04306                 ERROR_MACRO_PASS(name);
04307             }
04308           else
04309             {
04310               if (convert_straight_comp2(settings, end_x, end_y, end_z)
04311                   IS RS274NGC_ERROR)
04312                 ERROR_MACRO_PASS(name);
04313             }
04314         }
04315       else
04316         {
04317           if (settings->feed_mode IS INVERSE_TIME)
04318             {
04319               if (block->f_number IS -1.0)
04320                 ERROR_MACRO(_interpreter_linetext, name,
04321                             "F word missing with inverse time G1 move");
04322               else
04323                 {
04324                   length SET_TO utility_find_straight_length
04325                     (end_x, end_y, end_z,
04326                      settings->current_x, settings->current_y,
04327                      settings->current_z);
04328                   rate SET_TO MAX(0.1, (length * block->f_number));
04329                   SET_FEED_RATE (rate);
04330                   settings->feed_rate SET_TO rate;
04331                 }
04332             }
04333           else if (settings->feed_rate IS 0.0)
04334             ERROR_MACRO(_interpreter_linetext, name, "Cannot do G1 with zero feed rate");
04335           STRAIGHT_FEED(end_x, end_y, end_z);
04336           settings->current_x SET_TO end_x;
04337           settings->current_y SET_TO end_y;
04338         }
04339     }
04340   else
04341     BUG_MACRO(name, "Code is not G0 or G1 in convert_straight");
04342 
04343   settings->current_z SET_TO end_z;
04344   return RS274NGC_OK;
04345 }
04346 
04347 /****************************************************************************/
04348 
04349 /* read_comment
04350 
04351 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
04352    BUG - If the first character read is not '(' , this returns RS274NGC_ERROR.
04353    Otherwise, it returns RS274NGC_OK.
04354 
04355 Side effects:
04356    The counter is reset to point to the character following the comment.
04357    The comment string, without parentheses, is copied into the comment
04358    area of the block.
04359 
04360 Called by: read_one_item
04361 
04362 When this function is called, counter is pointing at an item on the
04363 line that starts with the character '(', indicating a comment is
04364 beginning. The function reads characters of the comment, up to and
04365 including the comment closer ')'.
04366 
04367 It is expected that the format of a comment will have been checked (by
04368 read_text or read_keyboard_line) and bad format comments will
04369 have prevented the system from getting this far, so that this function
04370 can assume a close parenthesis will be found when an open parenthesis
04371 has been found, and that comments are not nested.
04372 
04373 The "parameters" argument is not used in this function. That argument is
04374 present only so that this will have the same argument list as the other
04375 "read_XXX" functions called using a function pointer by read_one_item.
04376 
04377 */
04378 
04379 int read_comment(     /* ARGUMENT VALUES                               */
04380  char * line,         /* string: line of RS274 code being processed    */
04381  int * counter,       /* pointer to a counter for position on the line */
04382  block_pointer block, /* pointer to a block being filled from the line */
04383  double * parameters) /* array of system parameters                    */
04384 {
04385   static char name[] SET_TO "read_comment";
04386   int n;
04387 
04388   if (line[*counter] ISNT '(')
04389     BUG_MACRO(name, "Read_comment should not have been called");
04390 
04391   (*counter)++;
04392   for (n SET_TO 0; line[*counter] ISNT ')' ; (*counter)++, n++)
04393     {
04394       block->comment[n] SET_TO line[*counter];
04395     }
04396   block->comment[n] SET_TO 0;
04397   (*counter)++;
04398   return RS274NGC_OK;
04399 }
04400 
04401 /****************************************************************************/
04402 
04403 /* read_d
04404 
04405 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
04406    If any of the following errors occur, this returns RS274NGC_ERROR.
04407    Otherwise, it returns RS274NGC_OK.
04408    1. BUG - The first character read is not d.
04409    2. A d_number has already been inserted in the block.
04410    3. The value cannot be read.
04411    4. The d_number is negative.
04412 
04413 Side effects:
04414    counter is reset to the character following the tool number.
04415    A d_number is inserted in the block.
04416 
04417 Called by: read_one_item
04418 
04419 When this function is called, counter is pointing at an item on the
04420 line that starts with the character 'd', indicating an index into a
04421 table of tool diameters.  The function reads characters which give the
04422 (positive integer) value of the index.
04423 
04424 read_integer_value allows a minus sign, so a check for a negative value
04425 is needed here, and the parameters argument is also needed.
04426 
04427 */
04428 
04429 int read_d(           /* ARGUMENT VALUES                               */
04430  char * line,         /* string: line of RS274 code being processed    */
04431  int * counter,       /* pointer to a counter for position on the line */
04432  block_pointer block, /* pointer to a block being filled from the line */
04433  double * parameters) /* array of system parameters                    */
04434 {
04435   static char name[] SET_TO "read_d";
04436   int value;
04437 
04438   if (line[*counter] ISNT 'd')
04439     BUG_MACRO(name, "Read_d should not have been called");
04440 
04441   *counter SET_TO (*counter + 1);
04442 
04443   if (block->d_number > -1)
04444     ERROR_MACRO(_interpreter_linetext, name, "Multiple D words on one line");
04445   else if (read_integer_value(line, counter, &value, parameters)
04446            IS RS274NGC_ERROR)
04447     ERROR_MACRO_PASS(name);
04448   else if (value < 0)
04449     ERROR_MACRO(_interpreter_linetext, name, "Negative D code used");
04450   else
04451     {
04452       block->d_number SET_TO value;
04453       return RS274NGC_OK;
04454     }
04455 }
04456 
04457 /****************************************************************************/
04458 
04459 /* read_f
04460 
04461 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
04462    If any of the following errors occur, this returns RS274NGC_ERROR.
04463    Otherwise, it returns RS274NGC_OK.
04464    1. BUG - The first character read is not f.
04465    2. An f_number has already been inserted in the block.
04466    3. The f_number cannot be read.
04467    4. The f_number is negative.
04468 
04469 Side effects:
04470    counter is reset to point to the first character following the f_number.
04471    The f_number is inserted in block.
04472 
04473 Called by: read_one_item
04474 
04475 When this function is called, counter is pointing at an item on the
04476 line that starts with the character 'f'. The function reads characters
04477 which tell how to set the f_number, up to the start of the next item
04478 or the end of the line. This information is inserted in the block.
04479 
04480 The value may be a real number or something that evaluates to a
04481 real number, so read_real_value is used to read it. Parameters
04482 may be involved, so an extra argument is required. The value is always
04483 a feed rate.
04484 
04485 */
04486 
04487 int read_f(           /* ARGUMENT VALUES                               */
04488  char * line,         /* string: line of RS274 code being processed    */
04489  int * counter,       /* pointer to a counter for position on the line */
04490  block_pointer block, /* pointer to a block being filled from the line */
04491  double * parameters) /* array of system parameters                    */
04492 {
04493   static char name[] SET_TO "read_f";
04494   double value;
04495 
04496   if (line[*counter] ISNT 'f')
04497     BUG_MACRO(name, "Read_f should not have been called");
04498 
04499   *counter SET_TO (*counter + 1);
04500 
04501   if (block->f_number > -1.0)
04502     ERROR_MACRO(_interpreter_linetext, name, "Multiple F words on one line");
04503   else if (read_real_value(line, counter, &value, parameters)
04504            IS RS274NGC_ERROR)
04505     ERROR_MACRO_PASS(name);
04506   else if (value < 0.0)
04507     ERROR_MACRO(_interpreter_linetext, name, "Negative F word found");
04508   else
04509     {
04510       block->f_number SET_TO value;
04511       return RS274NGC_OK;
04512     }
04513 }
04514 
04515 /****************************************************************************/
04516 
04517 /* read_g
04518 
04519 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
04520    If any of the following errors occur, this returns RS274NGC_ERROR.
04521    Otherwise, it returns RS274NGC_OK.
04522    1. BUG - The first character read is not g.
04523    2. The value cannot be read.
04524    3. The value is negative.
04525    4. The value differs from a number ending in an even tenth by more
04526       than 0.0001.
04527    5. Another g code from the same modal group has already been
04528       inserted in the block.
04529 
04530 Side effects:
04531    counter is reset to the character following the end of the g_code.
04532    A g code is inserted as the value of the appropriate mode in the
04533    g_modes array in the block.
04534    The g code counter in the block is increased by 1.
04535 
04536 Called by: read_one_item
04537 
04538 When this function is called, counter is pointing at an item on the
04539 line that starts with the character 'g', indicating a g_code.  The
04540 function reads characters which tell how to set the g_code.
04541 
04542 The manual [NCMS, page 51] allows g_codes to be represented
04543 by expressions and provide [NCMS, 71 - 73] that a g_code must evaluate
04544 to to a number of the form XX.X (59.1, for example). The manual does not
04545 say how close an expression must come to one of the allowed values for
04546 it to be legitimate. Is 59.099999 allowed to mean 59.1, for example?
04547 In the interpreter, we adopt the convention that the evaluated number
04548 for the g_code must be within 0.0001 of a value of the form XX.X
04549 
04550 To simplify the handling of g_codes, we convert them to integers by
04551 multiplying by 10 and rounding down or up if within 0.001 of an
04552 integer. Other functions that deal with g_codes handle them
04553 symbolically, however. The symbols are defined in rs274ngc.hh
04554 where G_1 is 10, G_83 is 830, etc.
04555 
04556 This allows any number g_codes on one line, provided that no two are
04557 in the same modal group. The check_g_codes function checks that no
04558 more than some maximum number of g_codes (currently 4) are on the same
04559 line.
04560 
04561 */
04562 
04563 int read_g(           /* ARGUMENT VALUES                                */
04564  char * line,         /* string: line of RS274/NGC code being processed */
04565  int * counter,       /* pointer to a counter for position on the line  */
04566  block_pointer block, /* pointer to a block being filled from the line  */
04567  double * parameters) /* array of system parameters                     */
04568 {
04569   static char name[] SET_TO "read_g";
04570   double value_read;
04571   int value;
04572   int mode;
04573 
04574   if (line[*counter] ISNT 'g')
04575     BUG_MACRO(name, "Read_g should not have been called");
04576 
04577   *counter SET_TO (*counter + 1);
04578   if (read_real_value(line, counter, &value_read, parameters)
04579       IS RS274NGC_ERROR)
04580     ERROR_MACRO_PASS(name);
04581   value_read SET_TO (10.0 * value_read);
04582   value SET_TO (int)floor(value_read);
04583 
04584   if ((value_read - value) > 0.999)
04585     value SET_TO (int)ceil(value_read);
04586   else if ((value_read - value) > 0.001)
04587     ERROR_MACRO(_interpreter_linetext, name,"G code out of range");
04588 
04589   if (value > 999)
04590     ERROR_MACRO(_interpreter_linetext, name, "G code out of range");
04591   else if (value < 0)
04592     ERROR_MACRO(_interpreter_linetext, name, "Negative G code used");
04593 
04594   mode SET_TO gees[value];
04595 
04596   if (mode IS -1)
04597     ERROR_MACRO(_interpreter_linetext, name, "Unknown G code used");
04598   else if (block->g_modes[mode] ISNT -1)
04599     ERROR_MACRO(_interpreter_linetext, name, "Two G codes used from same modal group");
04600   else
04601     {
04602       block->g_modes[mode] SET_TO value;
04603       block->g_count++;
04604       return RS274NGC_OK;
04605     }
04606 }
04607 
04608 /****************************************************************************/
04609 
04610 /* read_i
04611 
04612 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
04613    If any of the following errors occur, this returns RS274NGC_ERROR.
04614    Otherwise, it returns RS274NGC_OK.
04615    1. BUG - The first character read is not i.
04616    2. A i_coordinate has already been inserted in the block.
04617    3. The value cannot be read.
04618 
04619 Side effects:
04620    counter is reset.
04621    The i_flag in the block is turned on.
04622    A i_coordinate setting is inserted in the block.
04623 
04624 Called by: read_one_item
04625 
04626 When this function is called, counter is pointing at an item on the
04627 line that starts with the character 'i', indicating a i_coordinate
04628 setting. The function reads characters which tell how to set the
04629 coordinate, up to the start of the next item or the end of the line.
04630 This information is inserted in the block. The counter is then set to
04631 point to the character following.
04632 
04633 The value may be a real number or something that evaluates to a
04634 real number, so read_real_value is used to read it. Parameters
04635 may be involved.
04636 
04637 */
04638 
04639 int read_i(           /* ARGUMENT VALUES                                */
04640  char * line,         /* string: line of RS274 code being processed     */
04641  int * counter,       /* pointer to a counter for position on the line  */
04642  block_pointer block, /* pointer to a block being filled from the line  */
04643  double * parameters) /* array of system parameters                     */
04644 {
04645   static char name[] SET_TO "read_i";
04646   double value;
04647 
04648   if (line[*counter] ISNT 'i')
04649     BUG_MACRO(name, "Read_i should not have been called");
04650 
04651   *counter SET_TO (*counter + 1);
04652 
04653   if (block->i_flag ISNT OFF)
04654     ERROR_MACRO(_interpreter_linetext, name, "Multiple I words on one line");
04655   else if (read_real_value(line, counter, &value, parameters)
04656            IS RS274NGC_ERROR)
04657     ERROR_MACRO_PASS(name);
04658   else
04659     {
04660       block->i_flag SET_TO ON;
04661       block->i_number SET_TO value;
04662       return RS274NGC_OK;
04663     }
04664 }
04665 
04666 /****************************************************************************/
04667 
04668 /* read_j
04669 
04670 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
04671    If any of the following errors occur, this returns RS274NGC_ERROR.
04672    Otherwise, it returns RS274NGC_OK.
04673    1. BUG - The first character read is not j.
04674    2. A j_coordinate has already been inserted in the block.
04675    3. The value cannot be read.
04676 
04677 Side effects:
04678    counter is reset.
04679    The j_flag in the block is turned on.
04680    A j_coordinate setting is inserted in the block.
04681 
04682 Called by: read_one_item
04683 
04684 When this function is called, counter is pointing at an item on the
04685 line that starts with the character 'j', indicating a j_coordinate
04686 setting. The function reads characters which tell how to set the
04687 coordinate, up to the start of the next item or the end of the line.
04688 This information is inserted in the block. The counter is then set to
04689 point to the character following.
04690 
04691 The value may be a real number or something that evaluates to a
04692 real number, so read_real_value is used to read it. Parameters
04693 may be involved.
04694 
04695 */
04696 
04697 int read_j(           /* ARGUMENT VALUES                                */
04698  char * line,         /* string: line of RS274 code being processed     */
04699  int * counter,       /* pointer to a counter for position on the line  */
04700  block_pointer block, /* pointer to a block being filled from the line  */
04701  double * parameters) /* array of system parameters                     */
04702 {
04703   static char name[] SET_TO "read_j";
04704   double value;
04705 
04706   if (line[*counter] ISNT 'j')
04707     BUG_MACRO(name, "Read_j should not have been called");
04708 
04709   *counter SET_TO (*counter + 1);
04710 
04711   if (block->j_flag ISNT OFF)
04712     ERROR_MACRO(_interpreter_linetext, name, "Multiple J words on one line");
04713   else if (read_real_value(line, counter, &value, parameters)
04714            IS RS274NGC_ERROR)
04715     ERROR_MACRO_PASS(name);
04716   else
04717     {
04718       block->j_flag SET_TO ON;
04719       block->j_number SET_TO value;
04720       return RS274NGC_OK;
04721     }
04722 }
04723 
04724 /****************************************************************************/
04725 
04726 /* read_k
04727 
04728 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
04729    If any of the following errors occur, this returns RS274NGC_ERROR.
04730    Otherwise, it returns RS274NGC_OK.
04731    1. BUG - The first character read is not k.
04732    2. A k_coordinate has already been inserted in the block.
04733    3. The value cannot be read.
04734 
04735 Side effects:
04736    counter is reset.
04737    The k_flag in the block is turned on.
04738    A k_coordinate setting is inserted in the block.
04739 
04740 Called by: read_one_item
04741 
04742 When this function is called, counter is pointing at an item on the
04743 line that starts with the character 'k', indicating a k_coordinate
04744 setting. The function reads characters which tell how to set the
04745 coordinate, up to the start of the next item or the end of the line.
04746 This information is inserted in the block. The counter is then set to
04747 point to the character following.
04748 
04749 The value may be a real number or something that evaluates to a
04750 real number, so read_real_value is used to read it. Parameters
04751 may be involved.
04752 
04753 */
04754 
04755 int read_k(           /* ARGUMENT VALUES                                */
04756  char * line,         /* string: line of RS274 code being processed     */
04757  int * counter,       /* pointer to a counter for position on the line  */
04758  block_pointer block, /* pointer to a block being filled from the line  */
04759  double * parameters) /* array of system parameters                     */
04760 {
04761   static char name[] SET_TO "read_k";
04762   double value;
04763 
04764   if (line[*counter] ISNT 'k')
04765     BUG_MACRO(name, "Read_k should not have been called");
04766 
04767   *counter SET_TO (*counter + 1);
04768 
04769   if (block->k_flag ISNT OFF)
04770     ERROR_MACRO(_interpreter_linetext, name, "Multiple K words on one line");
04771   else if (read_real_value(line, counter, &value, parameters)
04772            IS RS274NGC_ERROR)
04773     ERROR_MACRO_PASS(name);
04774   else
04775     {
04776       block->k_flag SET_TO ON;
04777       block->k_number SET_TO value;
04778       return RS274NGC_OK;
04779     }
04780 }
04781 
04782 /****************************************************************************/
04783 
04784 /* read_l
04785 
04786 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
04787    If any of the following errors occur, this returns RS274NGC_ERROR.
04788    Otherwise, it returns RS274NGC_OK.
04789    1. BUG - the first character read is not l
04790    2. the value cannot be read
04791    3. the value is negative
04792    4. there is already an l code in the block
04793 
04794 Side effects:
04795    counter is reset to the character following the l number.
04796    An l code is inserted in the block as the value of l.
04797 
04798 Called by: read_one_item
04799 
04800 When this function is called, counter is pointing at an item on the
04801 line that starts with the character 'l', indicating an L code.
04802 The function reads characters which give the (integer) value of the
04803 L code.
04804 
04805 L codes are used for:
04806 1. the number of times a canned cycle should be repeated.
04807 2. a key with G10.
04808 
04809 */
04810 
04811 int read_l(           /* ARGUMENT VALUES                                */
04812  char * line,         /* string: line of RS274/NGC code being processed */
04813  int * counter,       /* pointer to a counter for position on the line  */
04814  block_pointer block, /* pointer to a block being filled from the line  */
04815  double * parameters) /* array of system parameters                     */
04816 {
04817   static char name[] SET_TO "read_l";
04818   int value;
04819 
04820   if (line[*counter] ISNT 'l')
04821     BUG_MACRO(name, "Read_l should not have been called");
04822 
04823   *counter SET_TO (*counter + 1);
04824 
04825   if (block->l_number > -1)
04826     ERROR_MACRO(_interpreter_linetext, name, "Multiple L words on one line");
04827   else if (read_integer_value(line, counter, &value, parameters)
04828            IS RS274NGC_ERROR)
04829     ERROR_MACRO_PASS(name);
04830   else if (value < 0)
04831     ERROR_MACRO(_interpreter_linetext, name, "Negative L word used");
04832   else
04833     {
04834       block->l_number SET_TO value;
04835       return RS274NGC_OK;
04836     }
04837 }
04838 
04839 /****************************************************************************/
04840 
04841 /* read_m
04842 
04843 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
04844    If any of the following errors occur, this returns RS274NGC_ERROR.
04845    Otherwise, it returns RS274NGC_OK.
04846    1. BUG - The first character read is not m.
04847    2. The value cannot be read.
04848    3. The value is negative.
04849    4. The value is greater than 99.
04850    5. The m code is not known to the system.
04851    6. Another m code in the same modal group has already been read.
04852 
04853 Side effects:
04854    counter is reset to the character following the m number.
04855    An m code is inserted as the value of the appropriate mode in the
04856    m_modes array in the block.
04857    The m code counter in the block is increased by 1.
04858 
04859 Called by: read_one_item
04860 
04861 When this function is called, counter is pointing at an item on the
04862 line that starts with the character 'm', indicating an m code.
04863 The function reads characters which give the (integer) value of the
04864 m code.
04865 
04866 read_integer_value allows a minus sign, so a check for a negative value
04867 is needed here, and the parameters argument is also needed.
04868 
04869 */
04870 
04871 int read_m(           /* ARGUMENT VALUES                               */
04872  char * line,         /* string: line of RS274 code being processed    */
04873  int * counter,       /* pointer to a counter for position on the line */
04874  block_pointer block, /* pointer to a block being filled from the line */
04875  double * parameters) /* array of system parameters                    */
04876 {
04877   static char name[] SET_TO "read_m";
04878   int value;
04879   int mode;
04880 
04881   if (line[*counter] ISNT 'm')
04882     BUG_MACRO(name, "Read_m should not have been called");
04883 
04884   *counter SET_TO (*counter + 1);
04885 
04886   if (read_integer_value(line, counter, &value, parameters) IS RS274NGC_ERROR)
04887     ERROR_MACRO_PASS(name);
04888   else if (value < 0)
04889     ERROR_MACRO(_interpreter_linetext, name, "Negative M code used");
04890   else if (value > 99)
04891     ERROR_MACRO(_interpreter_linetext, name, "M code greater than 99");
04892 
04893   mode SET_TO ems[value];
04894 
04895   if (mode IS -1)
04896     ERROR_MACRO(_interpreter_linetext, name, "Unknown M code used");
04897   else if (block->m_modes[mode] ISNT -1)
04898     ERROR_MACRO(_interpreter_linetext, name, "Two M codes used from same modal group");
04899   else
04900     {
04901       block->m_modes[mode] SET_TO value;
04902       block->m_count++;
04903       return RS274NGC_OK;
04904     }
04905 }
04906 
04907 /****************************************************************************/
04908 
04909 /* read_p
04910 
04911 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
04912    If any of the following errors occur, this returns RS274NGC_ERROR.
04913    Otherwise, it returns RS274NGC_OK.
04914    1. BUG - The first character read is not p.
04915    2. A p value has already been inserted in the block.
04916    3. The p value cannot be read.
04917    4. The p value is negative.
04918 
04919 Side effects:
04920    counter is reset to point to the first character following the p value.
04921    The p value setting is inserted in block.
04922 
04923 Called by: read_one_item
04924 
04925 When this function is called, counter is pointing at an item on the
04926 line that starts with the character 'p', indicating a p value
04927 setting. The function reads characters which tell how to set the p
04928 value, up to the start of the next item or the end of the line. This
04929 information is inserted in the block.
04930 
04931 P codes are used for:
04932 1. Dwell time with G4 dwell [NCMS, page 23].
04933 2. Dwell time in canned cycles g82, G86, G88, G89 [NCMS pages 98 - 100].
04934 3. A key with G10 [NCMS, pages 9, 10].
04935 
04936 */
04937 
04938 int read_p(           /* ARGUMENT VALUES                                */
04939  char * line,         /* string: line of RS274/NGC code being processed */
04940  int * counter,       /* pointer to a counter for position on the line  */
04941  block_pointer block, /* pointer to a block being filled from the line  */
04942  double * parameters) /* array of system parameters                     */
04943 {
04944   static char name[] SET_TO "read_p";
04945   double value;
04946 
04947   if (line[*counter] ISNT 'p')
04948     BUG_MACRO(name, "Read_p should not have been called");
04949 
04950   *counter SET_TO (*counter + 1);
04951   if (block->p_number > -1.0)
04952     ERROR_MACRO(_interpreter_linetext, name, "Multiple P words on one line");
04953   else if (read_real_value(line, counter, &value, parameters)
04954            IS RS274NGC_ERROR)
04955     ERROR_MACRO_PASS(name);
04956   else if (value < 0.0)
04957     ERROR_MACRO(_interpreter_linetext, name, "Negative P value used");
04958   else
04959     {
04960       block->p_number SET_TO value;
04961       return RS274NGC_OK;
04962     }
04963 }
04964 
04965 /****************************************************************************/
04966 
04967 /* read_parameter_setting
04968 
04969 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
04970    If any of the following errors occur, this returns RS274NGC_ERROR.
04971    Otherwise, it returns RS274NGC_OK.
04972    1. BUG - The first character read is not # .
04973    2. The characters immediately following do not evaluate to an integer.
04974    3. The parameter index is out of range.
04975    4. An equal sign does not follow the parameter expression.
04976    5. The value on the right side of the equal sign cannot be read.
04977 
04978 Side effects:
04979    counter is reset to the character following the end of the parameter
04980    setting. The parameter whose index follows "#" is set to the
04981    real value following "=".
04982 
04983 Called by: read_one_item
04984 
04985 When this function is called, counter is pointing at an item on the
04986 line that starts with the character '#', indicating a parameter
04987 setting.  The function reads characters which tell how to set the
04988 parameter.
04989 
04990 Any number of parameters may be set on a line, and parameters set
04991 early on the line may be used in expressions later on the line.
04992 
04993 Parameter setting is not clearly described in [NCMS, pp. 51 - 62]: it is
04994 not clear if more than one parameter setting per line is allowed (any
04995 number is OK in this implementation). The characters immediately following
04996 the "#" must constitute a "parameter expression", but it is not clear
04997 what that is. Here we allow any expression as long as it evaluates to
04998 an integer.
04999 
05000 Parameters are handled in the interpreter by having a parameter table as
05001 part of the machine settings. The parameter table is passed to the
05002 reading functions which need it. Reading functions may set parameter
05003 values or use them.
05004 
05005 The syntax recognized by this this function is # followed by an
05006 integer expression (explicit integer or expression evaluating to an
05007 integer) followed by = followed by a real value (number or
05008 expression).
05009 
05010 Note that # also starts a bunch of characters which represent a parameter
05011 to be evaluated. That situation is handled by read_parameter.
05012 
05013 */
05014 
05015 int read_parameter_setting(  /* ARGUMENT VALUES                         */
05016  char * line,         /* string: line of RS274/NGC code being processed */
05017  int * counter,       /* pointer to a counter for position on the line  */
05018  block_pointer block, /* pointer to a block being filled from the line  */
05019  double * parameters) /* array of system parameters                     */
05020 {
05021   static char name[] SET_TO "read_parameter_setting";
05022   int index;
05023   double value;
05024 
05025   if (line[*counter] ISNT '#')
05026     BUG_MACRO(name, "Read_parameter_setting should not have been called");
05027   *counter SET_TO (*counter + 1);
05028   if (read_integer_value(line, counter, &index, parameters) IS RS274NGC_ERROR)
05029     ERROR_MACRO_PASS(name);
05030   else if ((index < 1) OR (index >= RS274NGC_MAX_PARAMETERS))
05031     ERROR_MACRO(_interpreter_linetext, name, "Parameter number out of range");
05032   else if (line[*counter] ISNT '=')
05033     ERROR_MACRO(_interpreter_linetext, name, "Equal sign missing in parameter setting");
05034   *counter SET_TO (*counter + 1);
05035   if (read_real_value(line, counter, &value, parameters) IS RS274NGC_ERROR)
05036     ERROR_MACRO_PASS(name);
05037   else
05038     {
05039       parameters[index] SET_TO value;
05040       return RS274NGC_OK;
05041     }
05042 }
05043 
05044 /****************************************************************************/
05045 
05046 /* read_q
05047 
05048 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
05049    If any of the following errors occur, this returns RS274NGC_ERROR.
05050    Otherwise, it returns RS274NGC_OK.
05051    1. BUG - The first character read is not q.
05052    2. A q value has already been inserted in the block.
05053    3. The q value cannot be read.
05054    4. The q value is negative.
05055 
05056 Side effects:
05057    counter is reset to point to the first character following the q value.
05058    The q value setting is inserted in block.
05059 
05060 Called by: read_one_item
05061 
05062 When this function is called, counter is pointing at an item on the
05063 line that starts with the character 'q', indicating a q value
05064 setting. The function reads characters which tell how to set the q
05065 value, up to the start of the next item or the end of the line. This
05066 information is inserted in the block.
05067 
05068 Q is used in the G87 canned cycle [NCMS, page 98].
05069 
05070 */
05071 
05072 int read_q(           /* ARGUMENT VALUES                                */
05073  char * line,         /* string: line of RS274/NGC code being processed */
05074  int * counter,       /* pointer to a counter for position on the line  */
05075  block_pointer block, /* pointer to a block being filled from the line  */
05076  double * parameters) /* array of system parameters                     */
05077 {
05078   static char name[] SET_TO "read_q";
05079   double value;
05080 
05081   if (line[*counter] ISNT 'q')
05082     BUG_MACRO(name, "Read_q should not have been called");
05083 
05084   *counter SET_TO (*counter + 1);
05085   if (block->q_number > -1.0)
05086     ERROR_MACRO(_interpreter_linetext, name, "Multiple Q words on one line");
05087   else if (read_real_value(line, counter, &value, parameters)
05088            IS RS274NGC_ERROR)
05089     ERROR_MACRO_PASS(name);
05090   else if (value < 0.0)
05091     ERROR_MACRO(_interpreter_linetext, name, "Negative Q value used");
05092   else
05093     {
05094       block->q_number SET_TO value;
05095       return RS274NGC_OK;
05096     }
05097 }
05098 
05099 /****************************************************************************/
05100 
05101 /* read_r
05102 
05103 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
05104    If any of the following errors occur, this returns RS274NGC_ERROR.
05105    Otherwise, it returns RS274NGC_OK.
05106    1. BUG - The first character read is not r.
05107    2. An r_number has already been inserted in the block.
05108    3. The value cannot be read.
05109 
05110 Side effects:
05111    counter is reset.
05112    The r_flag in the block is turned on.
05113    The r_number is inserted in the block.
05114 
05115 Called by: read_one_item
05116 
05117 When this function is called, counter is pointing at an item on the
05118 line that starts with the character 'r'. The function reads characters
05119 which tell how to set the coordinate, up to the start of the next item
05120 or the end of the line. This information is inserted in the block. The
05121 counter is then set to point to the character following.
05122 
05123 An r number indicates the clearance plane in canned cycles.
05124 
05125 An r number may also be the radius of an arc. The parameters argument
05126 is needed.
05127 
05128 */
05129 
05130 int read_r(           /* ARGUMENT VALUES                               */
05131  char * line,         /* string: line of RS274 code being processed    */
05132  int * counter,       /* pointer to a counter for position on the line */
05133  block_pointer block, /* pointer to a block being filled from the line */
05134  double * parameters) /* array of system parameters                    */
05135 {
05136   static char name[] SET_TO "read_r";
05137   double value;
05138 
05139   if (line[*counter] ISNT 'r')
05140     BUG_MACRO(name, "Read_r should not have been called");
05141 
05142   *counter SET_TO (*counter + 1);
05143 
05144   if (block->r_flag ISNT OFF)
05145     ERROR_MACRO(_interpreter_linetext, name, "Multiple R words on one line");
05146   else if (read_real_value(line, counter, &value, parameters)
05147            IS RS274NGC_ERROR)
05148     ERROR_MACRO_PASS(name);
05149   else
05150     {
05151       block->r_flag SET_TO ON;
05152       block->r_number SET_TO value;
05153       return RS274NGC_OK;
05154     }
05155 }
05156 
05157 /****************************************************************************/
05158 
05159 /* read_spindle_speed
05160 
05161 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
05162    If any of the following errors occur, this returns RS274NGC_ERROR.
05163    Otherwise, it returns RS274NGC_OK.
05164    1. BUG - The first character read is not s.
05165    2. A spindle speed has already been inserted in the block.
05166    3. The spindle speed cannot be read.
05167    4. The spindle speed is negative.
05168 
05169 Side effects:
05170    counter is reset to the character following the spindle speed.
05171    A spindle speed setting is inserted in the block.
05172 
05173 Called by: read_one_item
05174 
05175 When this function is called, counter is pointing at an item on the
05176 line that starts with the character 's', indicating a spindle speed
05177 setting. The function reads characters which tell how to set the spindle
05178 speed.
05179 
05180 The value may be a real number or something that evaluates to a
05181 real number, so read_real_value is used to read it. Parameters
05182 may be involved.
05183 
05184 */
05185 
05186 int read_spindle_speed( /* ARGUMENT VALUES                               */
05187  char * line,           /* string: line of RS274 code being processed    */
05188  int * counter,         /* pointer to a counter for position on the line */
05189  block_pointer block,   /* pointer to a block being filled from the line */
05190  double * parameters)   /* array of system parameters                    */
05191 {
05192   static char name[] SET_TO "read_spindle_speed";
05193   double speed;
05194 
05195   if (line[*counter] ISNT 's')
05196     BUG_MACRO(name, "Read_spindle_speed should not have been called");
05197 
05198   *counter SET_TO (*counter + 1);
05199 
05200   if (block->s_number > -1.0)
05201     ERROR_MACRO(_interpreter_linetext, name, "Multiple S word spindle speed settings on one line");
05202   else if (read_real_value(line, counter, &speed, parameters)
05203            IS RS274NGC_ERROR)
05204     ERROR_MACRO_PASS(name);
05205   else if (speed < 0.0)
05206     ERROR_MACRO(_interpreter_linetext, name, "Negative spindle speed found");
05207   else
05208     {
05209       block->s_number SET_TO speed;
05210       return RS274NGC_OK;
05211     }
05212 }
05213 
05214 /****************************************************************************/
05215 
05216 /* read_tool
05217 
05218 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
05219    If any of the following errors occur, this returns RS274NGC_ERROR.
05220    Otherwise, it returns RS274NGC_OK.
05221    1. BUG - The first character read is not t.
05222    2. A t_number has already been inserted in the block.
05223    3. The value cannot be read.
05224    4. The tool number is negative.
05225 
05226 Side effects:
05227    counter is reset to the character following the tool number.
05228    A t_number is inserted in the block.
05229 
05230 Called by: read_one_item
05231 
05232 When this function is called, counter is pointing at an item on the
05233 line that starts with the character 't', indicating a tool.
05234 The function reads characters which give the (integer) value of the
05235 tool code.
05236 
05237 */
05238 
05239 int read_tool(        /* ARGUMENT VALUES                                */
05240  char * line,         /* string: line of RS274/NGC code being processed */
05241  int * counter,       /* pointer to a counter for position on the line  */
05242  block_pointer block, /* pointer to a block being filled from the line  */
05243  double * parameters) /* array of system parameters                     */
05244 {
05245   static char name[] SET_TO "read_tool";
05246   int value;
05247 
05248   if (line[*counter] ISNT 't')
05249     BUG_MACRO(name, "Read_tool should not have been called");
05250 
05251   *counter SET_TO (*counter + 1);
05252   if (block->t_number > -1)
05253     ERROR_MACRO(_interpreter_linetext, name, "Multiple T words (tool ids) on one line");
05254   if (read_integer_value(line, counter, &value, parameters) IS RS274NGC_ERROR)
05255     ERROR_MACRO_PASS(name);
05256   else if (value < 0)
05257     ERROR_MACRO(_interpreter_linetext, name, "Negative tool id used");
05258   else
05259     {
05260       block->t_number SET_TO value;
05261       return RS274NGC_OK;
05262     }
05263 }
05264 
05265 /****************************************************************************/
05266 
05267 /* read_tool_length_offset
05268 
05269 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
05270    If any of the following errors occur, this returns RS274NGC_ERROR.
05271    Otherwise, it returns RS274NGC_OK.
05272    1. BUG - The first character read is not h.
05273    2. An h_number has already been inserted in the block.
05274    3. The value cannot be read.
05275    4. The value is negative.
05276 
05277 Side effects:
05278    counter is reset to the character following the tool length offset number.
05279    An h_number is inserted in the block.
05280 
05281 Called by: read_one_item
05282 
05283 When this function is called, counter is pointing at an item on the
05284 line that starts with the character 'h', indicating a tool length
05285 offset.  The function reads characters which give the (integer) value
05286 of the tool length offset code. This code is a reference to the
05287 location of an offset in a table, not the actual distance of the
05288 offset.
05289 
05290 */
05291 
05292 int read_tool_length_offset(  /* ARGUMENT VALUES                        */
05293  char * line,         /* string: line of RS274/NGC code being processed */
05294  int * counter,       /* pointer to a counter for position on the line  */
05295  block_pointer block, /* pointer to a block being filled from the line  */
05296  double * parameters) /* array of system parameters                     */
05297 {
05298   static char name[] SET_TO "read_tool_length_offset";
05299   int value;
05300 
05301   if (line[*counter] ISNT 'h')
05302     BUG_MACRO(name, "Read_tool_length_offset should not have been called");
05303 
05304   *counter SET_TO (*counter + 1);
05305   if (block->h_number > -1)
05306     ERROR_MACRO(_interpreter_linetext, name, "Multiple tool length offsets on one line");
05307   if (read_integer_value(line, counter, &value, parameters) IS RS274NGC_ERROR)
05308     ERROR_MACRO_PASS(name);
05309   else if (value < 0)
05310     ERROR_MACRO(_interpreter_linetext, name, "Negative tool length offset used");
05311   else
05312     {
05313       block->h_number SET_TO value;
05314       return RS274NGC_OK;
05315     }
05316 }
05317 
05318 /****************************************************************************/
05319 
05320 /* read_x
05321 
05322 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
05323    If any of the following errors occur, this returns RS274NGC_ERROR.
05324    Otherwise, it returns RS274NGC_OK.
05325    1. BUG - The first character read is not x.
05326    2. A x_coordinate has already been inserted in the block.
05327    3. The value cannot be read.
05328 
05329 Side effects:
05330    counter is reset.
05331    The x_flag in the block is turned on.
05332    A x_coordinate setting is inserted in the block.
05333 
05334 Called by: read_one_item
05335 
05336 When this function is called, counter is pointing at an item on the
05337 line that starts with the character 'x', indicating a x_coordinate
05338 setting. The function reads characters which tell how to set the
05339 coordinate, up to the start of the next item or the end of the line.
05340 This information is inserted in the block. The counter is then set to
05341 point to the character following.
05342 
05343 The value may be a real number or something that evaluates to a
05344 real number, so read_real_value is used to read it. Parameters
05345 may be involved.
05346 
05347 */
05348 
05349 int read_x(           /* ARGUMENT VALUES                                */
05350  char * line,         /* string: line of RS274 code being processed     */
05351  int * counter,       /* pointer to a counter for position on the line  */
05352  block_pointer block, /* pointer to a block being filled from the line  */
05353  double * parameters) /* array of system parameters                     */
05354 {
05355   static char name[] SET_TO "read_x";
05356   double value;
05357 
05358   if (line[*counter] ISNT 'x')
05359     BUG_MACRO(name, "Read_x should not have been called");
05360 
05361   *counter SET_TO (*counter + 1);
05362 
05363   if (block->x_flag ISNT OFF)
05364     ERROR_MACRO(_interpreter_linetext, name, "Multiple X words on one line");
05365   else if (read_real_value(line, counter, &value, parameters)
05366            IS RS274NGC_ERROR)
05367     ERROR_MACRO_PASS(name);
05368   else
05369     {
05370       block->x_flag SET_TO ON;
05371       block->x_number SET_TO value;
05372       return RS274NGC_OK;
05373     }
05374 }
05375 
05376 /****************************************************************************/
05377 
05378 /* read_y
05379 
05380 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
05381    If any of the following errors occur, this returns RS274NGC_ERROR.
05382    Otherwise, it returns RS274NGC_OK.
05383    1. BUG - The first character read is not y.
05384    2. A y_coordinate has already been inserted in the block.
05385    3. The value cannot be read.
05386 
05387 Side effects:
05388    counter is reset.
05389    The y_flag in the block is turned on.
05390    A y_coordinate setting is inserted in the block.
05391 
05392 Called by: read_one_item
05393 
05394 When this function is called, counter is pointing at an item on the
05395 line that starts with the character 'y', indicating a y_coordinate
05396 setting. The function reads characters which tell how to set the
05397 coordinate, up to the start of the next item or the end of the line.
05398 This information is inserted in the block. The counter is then set to
05399 point to the character following.
05400 
05401 The value may be a real number or something that evaluates to a
05402 real number, so read_real_value is used to read it. Parameters
05403 may be involved.
05404 
05405 */
05406 
05407 int read_y(           /* ARGUMENT VALUES                                */
05408  char * line,         /* string: line of RS274 code being processed     */
05409  int * counter,       /* pointer to a counter for position on the line  */
05410  block_pointer block, /* pointer to a block being filled from the line  */
05411  double * parameters) /* array of system parameters                     */
05412 {
05413   static char name[] SET_TO "read_y";
05414   double value;
05415 
05416   if (line[*counter] ISNT 'y')
05417     BUG_MACRO(name, "Read_y should not have been called");
05418 
05419   *counter SET_TO (*counter + 1);
05420 
05421   if (block->y_flag ISNT OFF)
05422     ERROR_MACRO(_interpreter_linetext, name, "Multiple Y words on one line");
05423   else if (read_real_value(line, counter, &value, parameters)
05424            IS RS274NGC_ERROR)
05425     ERROR_MACRO_PASS(name);
05426   else
05427     {
05428       block->y_flag SET_TO ON;
05429       block->y_number SET_TO value;
05430       return RS274NGC_OK;
05431     }
05432 }
05433 
05434 /****************************************************************************/
05435 
05436 /* read_z
05437 
05438 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
05439    If any of the following errors occur, this returns RS274NGC_ERROR.
05440    Otherwise, it returns RS274NGC_OK.
05441    1. BUG - The first character read is not z.
05442    2. A z_coordinate has already been inserted in the block.
05443    3. The value cannot be read.
05444 
05445 Side effects:
05446    counter is reset.
05447    The z_flag in the block is turned on.
05448    A z_coordinate setting is inserted in the block.
05449 
05450 Called by: read_one_item
05451 
05452 When this function is called, counter is pointing at an item on the
05453 line that starts with the character 'z', indicating a z_coordinate
05454 setting. The function reads characters which tell how to set the
05455 coordinate, up to the start of the next item or the end of the line.
05456 This information is inserted in the block. The counter is then set to
05457 point to the character following.
05458 
05459 The value may be a real number or something that evaluates to a
05460 real number, so read_real_value is used to read it. Parameters
05461 may be involved.
05462 
05463 */
05464 
05465 int read_z(           /* ARGUMENT VALUES                                */
05466  char * line,         /* string: line of RS274 code being processed     */
05467  int * counter,       /* pointer to a counter for position on the line  */
05468  block_pointer block, /* pointer to a block being filled from the line  */
05469  double * parameters) /* array of system parameters                     */
05470 {
05471   static char name[] SET_TO "read_z";
05472   double value;
05473 
05474   if (line[*counter] ISNT 'z')
05475     BUG_MACRO(name, "Read_z should not have been called");
05476 
05477   *counter SET_TO (*counter + 1);
05478 
05479   if (block->z_flag ISNT OFF)
05480     ERROR_MACRO(_interpreter_linetext, name, "Multiple Z words on one line");
05481   else if (read_real_value(line, counter, &value, parameters)
05482            IS RS274NGC_ERROR)
05483     ERROR_MACRO_PASS(name);
05484   else
05485     {
05486       block->z_flag SET_TO ON;
05487       block->z_number SET_TO value;
05488       return RS274NGC_OK;
05489     }
05490 }
05491 
05492 /****************************************************************************/
05493 
05494 /* check_g_codes
05495 
05496 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
05497    If any of the following errors occur, this returns RS274NGC_ERROR.
05498    Otherwise, it returns RS274NGC_OK.
05499    1. A g4 (dwell) is used with an axis word.
05500    2. A g4 is used without a P (dwell time) word.
05501    3. A g4 is used and a motion g_code (other than g80) is on same line.
05502    4. There are too many g codes in the block.
05503    5. g10 (which uses axis values) is used and a motion g_code (other
05504       than g80) is on the same line.
05505    6. A g10 is used and L2 is not in the block.
05506    7. A g10 is used and P is not close to an integer.
05507    8. A g10 is used and P is less than 1 or more than 9.
05508    9. A g53 is used with motion other than g0 or g1.
05509   10. A g53 is used in incremental distance mode.
05510   11. A g80 is used while g10 isn't used, and an axis value is given.
05511   12. All axis values are missing with g92.
05512   13. g92 (which uses axis values) is used and a motion g_code (other
05513       than g80) is on the same line.
05514   14. BUG - g_modes[0] is set to an unknown value.
05515 
05516 Side effects: none
05517 
05518 Called by: check_items
05519 
05520 This runs checks on g_codes from a block of RS274/NGC instructions.
05521 
05522 The read_g function checks for errors which would foul up the
05523 reading. This function checks for additional logical errors in g_codes.
05524 
05525 [NCMS] does not give any maximum for how many g_codes may be put on
05526 the same line. The value of MAX_GEES (set in rs274ngc.hh) is currently
05527 4.
05528 
05529 We are suspending any implicit motion g_code when g4 or g10 is used.
05530 The implicit motion g_code takes effect again automatically after the
05531 line on which the g4 or g10 occurs.  It is not clear what the intent
05532 of [NCMS] is in this regard. The alternative is to require that any
05533 implicit motion be explicitly cancelled.
05534 
05535 Not all checks on g_codes are included here. Those checks that are
05536 sensitive to whether other g_codes on the same line have been executed
05537 yet are made by the functions called by convert_g.
05538 
05539 */
05540 
05541 int check_g_codes(       /* ARGUMENT VALUES                  */
05542  block_pointer block,    /* pointer to a block to be checked */
05543  setup_pointer settings) /* pointer to machine settings      */
05544 {
05545   static char name[] SET_TO "check_g_codes";
05546   ON_OFF axis_flag;
05547   int explicit_motion;
05548   int mode0;
05549   int p_int;
05550 
05551   axis_flag SET_TO
05552     ((block->x_flag) OR (block->y_flag) OR (block->z_flag)) ? ON : OFF;
05553   explicit_motion SET_TO block->g_modes[1];
05554   mode0 SET_TO block->g_modes[0];
05555 
05556   if(block->g_count > MAX_GEES)
05557     ERROR_MACRO(_interpreter_linetext, name, "Too many G codes on line");
05558 
05559   if (mode0 IS -1)
05560     {
05561       if (block->g_modes[4] IS -1)
05562         {
05563           if ((block->motion_to_be IS G_80) AND (axis_flag IS ON))
05564             ERROR_MACRO(_interpreter_linetext, name,  "Cannot use axis commands with G80");
05565         }
05566       else if (block->g_modes[4] IS G_4)
05567         {
05568           if (((block->motion_to_be IS -1) OR
05569                (block->motion_to_be IS G_80)) AND
05570               (axis_flag IS ON))
05571             ERROR_MACRO(_interpreter_linetext, name, "Cannot use axis commands with G4");
05572           if(block->p_number IS -1.0)
05573             ERROR_MACRO(_interpreter_linetext, name, "Dwell time missing with G4");
05574         }
05575       else if (block->g_modes[4] IS G_53)
05576         {
05577           if ((block->motion_to_be ISNT G_0) AND
05578               (block->motion_to_be ISNT G_1))
05579             ERROR_MACRO(_interpreter_linetext, name, "Must use G0 or G1 with G53");
05580           if((block->g_modes[3] IS G_91) OR
05581              ((block->g_modes[3] ISNT G_90) AND
05582               (settings->distance_mode IS MODE_INCREMENTAL)))
05583             ERROR_MACRO(_interpreter_linetext, name, "Cannot use G53 in incremental distance mode");
05584         }
05585       else
05586         BUG_MACRO(name, "Bad setting of g_mode in check_g_codes");
05587     }
05588   else if (block->g_modes[4] ISNT -1)
05589     ERROR_MACRO(_interpreter_linetext, name, "Cannot use two G codes from group 0");
05590   else if (mode0 IS G_10)
05591     {
05592       p_int SET_TO (int)(block->p_number + 0.0001);
05593       if ((explicit_motion ISNT -1) AND (explicit_motion ISNT G_80))
05594         ERROR_MACRO(_interpreter_linetext, name, "Cannot use a G code for motion with G10");
05595       if (block->l_number ISNT 2)
05596         ERROR_MACRO(_interpreter_linetext, name, "Line with G10 does not have L2");
05597       if (((block->p_number + 0.0001) - p_int) > 0.0002)
05598         ERROR_MACRO(_interpreter_linetext, name, "P value not an integer with G10 L2");
05599       if ((p_int < 1) OR (p_int > 9))
05600         ERROR_MACRO(_interpreter_linetext, name, "P value out of range with G10 L2");
05601     }
05602   else if (mode0 IS G_92)
05603     {
05604       if ((explicit_motion ISNT -1) AND (explicit_motion ISNT G_80))
05605         ERROR_MACRO(_interpreter_linetext, name, "Cannot use a G motion with G92");
05606       if (axis_flag IS OFF)
05607         ERROR_MACRO(_interpreter_linetext, name, "All axes missing with G92");
05608     }
05609   else if (mode0 IS G_92_2)
05610     {}
05611   else
05612     BUG_MACRO(name, "Bad setting of g_mode in check_g_codes");
05613 
05614   return RS274NGC_OK;
05615 }
05616 
05617 /****************************************************************************/
05618 
05619 /* check_m_codes
05620 
05621 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
05622    If any of the following errors occur, this returns RS274NGC_ERROR.
05623    Otherwise, it returns RS274NGC_OK.
05624    1. There are too many m codes in the block.
05625 
05626 Side effects: none
05627 
05628 Called by: check_items
05629 
05630 This runs checks on m_codes from a block of RS274/NGC instructions.
05631 
05632 The read_m function checks for errors which would foul up the
05633 reading. This function checks for additional errors in m_codes.
05634 
05635 */
05636 
05637 int check_m_codes(       /* ARGUMENT VALUES                  */
05638  block_pointer block,    /* pointer to a block to be checked */
05639  setup_pointer settings) /* pointer to machine settings      */
05640 {
05641   static char name[] SET_TO "check_m_codes";
05642 
05643   if(block->m_count > MAX_EMS)
05644     ERROR_MACRO(_interpreter_linetext, name, "Too many M codes on line");
05645 
05646   return RS274NGC_OK;
05647 }
05648 
05649 /****************************************************************************/
05650 
05651 /* check_other_codes
05652 
05653 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
05654    If any of the following errors occur, this returns RS274NGC_ERROR.
05655    Otherwise, it returns RS274NGC_OK.
05656    1. A d word is in a block with no cutter_radius_compensation_on command.
05657    2. An h_number is in a block with no tool length offset setting.
05658    3. An i_number is in a block with no G code that uses it.
05659    4. A j_number is in a block with no G code that uses it.
05660    5. A k_number is in a block with no G code that uses it.
05661    6. A l_number is in a block with no G code that uses it.
05662    7. A p_number is in a block with no G code that uses it.
05663    8. A q_number is in a block with no G code that uses it.
05664    9. An r_number is in a block with no G code that uses it.
05665 
05666 Side effects: none
05667 
05668 Called by: check_items
05669 
05670 This runs checks on codes from a block of RS274/NGC code which are
05671 not m or g codes.
05672 
05673 The functions named read_XXXX check for errors which would foul up the
05674 reading. This function checks for additional logical errors in codes.
05675 
05676 */
05677 
05678 int check_other_codes(   /* ARGUMENT VALUES                              */
05679  block_pointer block,    /* pointer to a block of RS274/NGC instructions */
05680  setup_pointer settings) /* pointer to machine settings                  */
05681 {
05682   static char name[] SET_TO "check_other_codes";
05683   int motion;
05684 
05685   motion SET_TO block->motion_to_be;
05686   if (block->d_number ISNT -1)
05687     {
05688       if ((block->g_modes[7] ISNT G_41) AND (block->g_modes[7] ISNT G_42))
05689         ERROR_MACRO(_interpreter_linetext, name,
05690            "D word on line with no cutter comp on (G41 or G42) command");
05691     }
05692   if (block->h_number ISNT -1)
05693     {
05694       if (block->g_modes[8] ISNT G_43)
05695         ERROR_MACRO(_interpreter_linetext, name,
05696           "H word on line with no tool length comp (G43) command");
05697     }
05698 
05699   if (block->i_flag IS ON) /* could still be useless if yz_plane arc */
05700     {
05701       if (((motion ISNT G_2) AND (motion ISNT G_3)) AND (motion ISNT G_87))
05702         ERROR_MACRO(_interpreter_linetext, name,
05703           "I word on line with no G code (G2, G3, G87) that uses it");
05704     }
05705 
05706 
05707   if (block->j_flag IS ON) /* could still be useless if xz_plane arc */
05708     {
05709       if (((motion ISNT G_2) AND (motion ISNT G_3)) AND (motion ISNT G_87))
05710         ERROR_MACRO(_interpreter_linetext, name,
05711           "J word on line with no G code (G2, G3, G87) that uses it");
05712     }
05713 
05714   if (block->k_flag IS ON) /* could still be useless if xy_plane arc */
05715     {
05716       if (((motion ISNT G_2) AND (motion ISNT G_3)) AND (motion ISNT G_87))
05717         ERROR_MACRO(_interpreter_linetext, name,
05718           "K word on line with no G code (G2, G3, G87) that uses it");
05719     }
05720 
05721   if (block->l_number ISNT -1)
05722     {
05723       if (((motion < G_81) OR (motion > G_89)) AND
05724           (block->g_modes[0] ISNT G_10))
05725         ERROR_MACRO(_interpreter_linetext, name,
05726                     "L word on line with no canned cycle or G10 to use it");
05727     }
05728 
05729   if (block->p_number ISNT -1.0)
05730     {
05731       if (((block->g_modes[0] ISNT G_10) AND
05732            (block->g_modes[4] ISNT G_4)) AND
05733           (((motion ISNT G_82) AND (motion ISNT G_86)) AND
05734            ((motion ISNT G_88) AND (motion ISNT G_89))))
05735         ERROR_MACRO(_interpreter_linetext, name,
05736       "P word on line with no G code (G4 G10 G82 G86 G88 G89) that uses it");
05737     }
05738 
05739   if (block->q_number ISNT -1.0)
05740     {
05741       if (motion ISNT G_83)
05742         ERROR_MACRO(_interpreter_linetext, name, "Q word on line with no G83 cycle that uses it");
05743     }
05744 
05745   if (block->r_flag IS ON)
05746     {
05747       if (((motion ISNT G_2) AND (motion ISNT G_3)) AND
05748           ((motion < G_81) OR (motion > G_89)))
05749         ERROR_MACRO(_interpreter_linetext, name,
05750           "R word on line with no G code (arc or cycle) that uses it");
05751     }
05752 
05753   return RS274NGC_OK;
05754 }
05755 
05756 /****************************************************************************/
05757 
05758 /* convert_control_mode
05759 
05760 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
05761   BUG - If g_code isn't G_61 or G_64, this returns RS274NGC_ERROR.
05762   Otherwise, it returns RS274NGC_OK.
05763 
05764 Side effects:
05765   The interpreter switches the machine settings to indicate the current
05766   control mode (CANON_EXACT_PATH or CANON_CONTINUOUS).
05767 
05768   The call SET_MOTION_CONTROL_MODE(CANON_CONTINUOUS) is made if
05769   the G code is G_64. The call SET_MOTION_CONTROL_MODE(CANON_EXACT_PATH)
05770   is made if the G code is G_61.
05771 
05772   We could, alternatively, set the control mode to CANON_EXACT_STOP
05773   on G_61, and that would correspond more closely to the meaning as
05774   given in [NCMS, page 40], but CANON_EXACT_PATH has the advantage
05775   that the tool does not stop if it does not have to, and no evident
05776   disadvantage compared to CANON_EXACT_STOP, so it is being used.
05777 
05778 Called by: convert_g.
05779 
05780 It is OK to call G_61 when G_61 is already in force, and similarly for
05781 G_64.
05782 
05783 */
05784 
05785 int convert_control_mode( /* ARGUMENT VALUES                               */
05786  int g_code,               /* g_code being executed (must be G_61 or G_64) */
05787  block_pointer block,      /* pointer to a block of RS274 instructions     */
05788  setup_pointer settings)   /* pointer to machine settings                  */
05789 {
05790   static char name[] SET_TO "convert_control_mode";
05791   if (g_code IS G_61)
05792     {
05793       SET_MOTION_CONTROL_MODE(CANON_EXACT_PATH);
05794       settings->control_mode SET_TO CANON_EXACT_PATH;
05795     }
05796   else if (g_code IS G_64)
05797     {
05798       SET_MOTION_CONTROL_MODE(CANON_CONTINUOUS);
05799       settings->control_mode SET_TO CANON_CONTINUOUS;
05800     }
05801   else
05802     BUG_MACRO(name, "Code is not G61 or G64 in convert_control_mode");
05803   return RS274NGC_OK;
05804 }
05805 
05806 /****************************************************************************/
05807 
05808 /* convert_coordinate_system
05809 
05810 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
05811    If any of the following errors occurs, this returns RS274NGC_ERROR.
05812    Otherwise, it returns RS274NGC_OK.
05813    1. The value of the g_code argument is not 540, 550, 560, 570, 580,
05814       590, 591, 592, or 593
05815 
05816 Side effects:
05817    If the NGC coordinate system selected by the g_code is not already in
05818    use, the canonical program coordinate system axis offset values are
05819    reset and the coordinate values of the current point are reset, and
05820    a call is made to SET_ORIGIN_OFFSETS.
05821 
05822 Called by: convert_g.
05823 
05824 COORDINATE SYSTEMS (involves g10, g54, g54 - g59.3, g92)
05825 
05826 The canonical machining functions view of coordinate systems is:
05827 1. There are two coordinate systems: absolute and program.
05828 2. All coordinate values are given in terms of the program coordinate system.
05829 3. The offsets of the program coordinate system may be reset.
05830 
05831 The RS274/NGC view of coordinate systems, as given in section 3.2
05832 of the manual [NCMS] is:
05833 1. there are ten coordinate systems: absolute and 9 program. The
05834    program coordinate systems are numbered 1 to 9.
05835 2. you can switch among the 9 but not to the absolute one. G54
05836    selects coordinate system 1, G55 selects 2, and so on through
05837    G56, G57, G58, G59, G59.1, G59.2, and G59.3.
05838 3. you can set the offsets of the 9 program coordinate systems
05839    using G10 L2 Pn (n is the number of the coordinate system) with
05840    values for the axes in terms of the absolute coordinate system.
05841 4. the first one of the 9 program coordinate systems is the default.
05842 5. data for coordinate systems is stored in parameters [NCMS, pages 59 - 60].
05843 6. g53 means to interpret coordinate values in terms of the absolute
05844    coordinate system for the one block in which g53 appears.
05845 7. You can offset the current coordinate system using g92. This offset
05846    will then apply to all nine program coordinate systems.
05847 
05848 The approach used in the interpreter mates the two views
05849 of coordinate systems as follows:
05850 
05851 During initialization, data from the parameters for the first NGC
05852 coordinate system is used in a SET_ORIGIN_OFFSETS function call and
05853 origin_ngc in the machine model is set to 1.
05854 
05855 If a g_code in the range g54 - g59.3 is encountered in an NC program,
05856 the data from the appropriate NGC coordinate system is copied into the
05857 origin offsets used by the interpreter, a SET_ORIGIN_OFFSETS function
05858 call is made, and the current position is reset.
05859 
05860 If a g10 is encountered, the convert_setup function is called to reset
05861 the offsets of the program coordinate system indicated by the P number
05862 given in the same block.
05863 
05864 If a g53 is encountered, the axis values given in that block are used
05865 to calculate what the coordinates are of that point in the current
05866 coordinate system, and a STRAIGHT_TRAVERSE or STRAIGHT_FEED function
05867 call to that point using the calculated values is made. No offset
05868 values are changed.
05869 
05870 If a g92 is encountered, that is handled by the convert_axis_offsets
05871 function. A g92 results in an axis offset for each axis being calculated
05872 and stored in the machine model. The axis offsets are applied to all
05873 nine coordinate systems. Axis offsets are initialized to zero.
05874 
05875 */
05876 
05877 int convert_coordinate_system( /* ARGUMENT VALUES                         */
05878  int g_code,              /* g_code called (must be one listed above)     */
05879  block_pointer block,     /* pointer to a block of RS274/NGC instructions */
05880  setup_pointer settings)  /* pointer to machine settings                  */
05881 {
05882   static char name[] SET_TO "convert_coordinate_system";
05883   int origin;
05884   double x;
05885   double y;
05886   double z;
05887   double * parameters;
05888 
05889   parameters SET_TO settings->parameters;
05890   switch(g_code)
05891     {
05892     case 540:
05893       origin SET_TO 1;
05894       break;
05895     case 550:
05896       origin SET_TO 2;
05897       break;
05898     case 560:
05899       origin SET_TO 3;
05900       break;
05901     case 570:
05902       origin SET_TO 4;
05903       break;
05904     case 580:
05905       origin SET_TO 5;
05906       break;
05907     case 590:
05908       origin SET_TO 6;
05909       break;
05910     case 591:
05911       origin SET_TO 7;
05912       break;
05913     case 592:
05914       origin SET_TO 8;
05915       break;
05916     case 593:
05917       origin SET_TO 9;
05918       break;
05919     default:
05920       BUG_MACRO(name, "Code is not G54 to G59.3 in convert_coordinate_system");
05921     }
05922 
05923   if (origin IS settings->origin_ngc) /* already using this origin */
05924     {
05925 #ifdef DEBUG_EMC
05926       COMMENT("interpreter: continuing to use same coordinate system");
05927 #endif
05928       return RS274NGC_OK;
05929     }
05930 
05931 /* axis offsets could be included in the following calculcations
05932 but do not need to be because they would not change the result. */
05933   settings->current_x SET_TO
05934     (settings->current_x + settings->origin_offset_x);
05935   settings->current_y SET_TO
05936     (settings->current_y + settings->origin_offset_y);
05937   settings->current_z SET_TO
05938     (settings->current_z + settings->origin_offset_z);
05939 
05940   x SET_TO parameters[5201 + (origin * 20)];
05941   y SET_TO parameters[5202 + (origin * 20)];
05942   z SET_TO parameters[5203 + (origin * 20)];
05943 
05944   settings->origin_offset_x SET_TO x;
05945   settings->origin_offset_y SET_TO y;
05946   settings->origin_offset_z SET_TO z;
05947 
05948   settings->current_x SET_TO (settings->current_x - x);
05949   settings->current_y SET_TO (settings->current_y - y);
05950   settings->current_z SET_TO (settings->current_z - z);
05951   settings->origin_ngc SET_TO origin;
05952 
05953   SET_ORIGIN_OFFSETS(x + settings->axis_offset_x,
05954                      y + settings->axis_offset_y,
05955                      z + settings->axis_offset_z);
05956 
05957   return RS274NGC_OK;
05958 }
05959 
05960 /****************************************************************************/
05961 
05962 /* convert_cutter_compensation
05963 
05964 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
05965    If any of the following errors occur, this returns RS274NGC_ERROR.
05966    Otherwise, it returns RS274NGC_OK.
05967    1. convert_cutter_compensation_on or convert_cutter_compensation_off
05968       is called and returns RS274NGC_ERROR.
05969    2. BUG - g_code is not G_40, G_41, or G_42.
05970 
05971 Side effects:
05972    The value of cutter_radius_compensation in the machine model mode is
05973    set to RIGHT, LEFT, or OFF. The currently active tool table index in
05974    the machine model is updated.
05975 
05976 Since cutter radius compensation is performed in the interpreter, no
05977 call is made to any canonical function regarding cutter radius compensation.
05978 
05979 Called by: convert_g
05980 
05981 */
05982 
05983 int convert_cutter_compensation(  /* ARGUMENT VALUES                  */
05984  int g_code,              /* must be G_40, G_41, or G_42              */
05985  block_pointer block,     /* pointer to a block of RS274 instructions */
05986  setup_pointer settings)  /* pointer to machine settings              */
05987 {
05988   static char name[] SET_TO "convert_cutter_compensation";
05989   if (g_code IS G_40)
05990     {
05991       if (convert_cutter_compensation_off(settings) IS RS274NGC_ERROR)
05992         ERROR_MACRO_PASS(name);
05993     }
05994   else if (g_code IS G_41)
05995     {
05996       if (convert_cutter_compensation_on(LEFT, block, settings)
05997           IS RS274NGC_ERROR)
05998         ERROR_MACRO_PASS(name);
05999     }
06000   else if (g_code IS G_42)
06001     {
06002       if (convert_cutter_compensation_on(RIGHT, block, settings)
06003           IS RS274NGC_ERROR)
06004         ERROR_MACRO_PASS(name);
06005     }
06006   else
06007     BUG_MACRO(name,
06008               "Code is not G40, G41, or G42 in convert_cutter_compensation");
06009 
06010   return RS274NGC_OK;
06011 }
06012 /****************************************************************************/
06013 /* convert_distance_mode
06014 
06015 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
06016    BUG - If g_code isn't G_90 or G_91, this returns RS274NGC_ERROR.
06017    Otherwise, it returns RS274NGC_OK.
06018 
06019 Side effects:
06020    The interpreter switches the machine settings to indicate the current
06021    distance mode (absolute or incremental).
06022 
06023    The canonical machine to which commands are being sent does not have
06024    an incremental mode, so no command setting the distance mode is
06025    generated in this function. A comment function call explaining the
06026    change of mode is made (conditionally), however, if there is a change.
06027 
06028 Called by: convert_g.
06029 
06030 */
06031 
06032 int convert_distance_mode( /* ARGUMENT VALUES                              */
06033  int g_code,               /* g_code being executed (must be G_90 or G_91) */
06034  block_pointer block,      /* pointer to a block of RS274 instructions     */
06035  setup_pointer settings)   /* pointer to machine settings                  */
06036 {
06037   static char name[] SET_TO "convert_distance_mode";
06038   if (g_code IS G_90)
06039     {
06040       if (settings->distance_mode ISNT MODE_ABSOLUTE)
06041         {
06042 #ifdef DEBUG_EMC
06043           COMMENT("interpreter: distance mode changed to absolute");
06044 #endif
06045           settings->distance_mode SET_TO MODE_ABSOLUTE;
06046         }
06047     }
06048   else if (g_code IS G_91)
06049     {
06050       if (settings->distance_mode ISNT MODE_INCREMENTAL)
06051         {
06052 #ifdef DEBUG_EMC
06053           COMMENT("interpreter: distance mode changed to incremental");
06054 #endif
06055           settings->distance_mode SET_TO MODE_INCREMENTAL;
06056         }
06057     }
06058   else
06059     BUG_MACRO(name, "Code is not G90 or G91 in convert_distance_mode");
06060   return RS274NGC_OK;
06061 }
06062 
06063 /****************************************************************************/
06064 
06065 /* convert_dwell
06066 
06067 Returned Value: int (RS274NGC_OK)
06068 
06069 Side effects:
06070    A dwell command is executed.
06071 
06072 Called by: convert_g.
06073 
06074 */
06075 
06076 int convert_dwell( /* ARGUMENT VALUES           */
06077  double time)      /* time in seconds to dwell  */
06078 {
06079   DWELL(time);
06080   return RS274NGC_OK;
06081 }
06082 
06083 /****************************************************************************/
06084 
06085 /* convert_length_units
06086 
06087 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
06088 
06089    If any of the following errors occur, this returns RS274NGC_ERROR.
06090    Otherwise, it returns RS274NGC_OK.
06091    1. Bug - The g_code argument isnt G_20 or G_21.
06092    2. Cutter radius compensation is on.
06093 
06094 Side effects:
06095    A command setting the length units is executed. The machine
06096    settings are reset regarding length units and current position.
06097 
06098 Called by: convert_g.
06099 
06100 We are not changing tool length offset values or tool diameter values.
06101 Those values must be given in the table in the correct units. Thus it
06102 will generally not be feasible to switch units in the middle of a
06103 program.
06104 
06105 We are not changing the parameters that represent the positions
06106 of the nine work coordinate systems.
06107 
06108 We are also not changing feed rate values when length units are
06109 changed, so the actual behavior may change.
06110 
06111 Several other distance items in the settings (such as the various
06112 parameters for cycles) are also not reset.
06113 
06114 We are changing origin offset and axis offset values, which are
06115 critical. If this were not done, when length units are set and the new
06116 length units are not the same as the default length units
06117 (millimeters), and any XYZ origin or axis offset is not zero, then any
06118 subsequent change in XYZ origin or axis offset values will be
06119 incorrect.  Also, g53 (motion in absolute coordinates) will not work
06120 correctly.
06121 
06122 */
06123 
06124 int convert_length_units( /* ARGUMENT VALUES                              */
06125  int g_code,              /* g_code being executed (must be G_20 or G_21) */
06126  setup_pointer settings)  /* pointer to machine settings                  */
06127 {
06128   static char name[] SET_TO "convert_length_units";
06129   if (settings->cutter_radius_compensation ISNT OFF)
06130     ERROR_MACRO(_interpreter_linetext, name, "Cannot change units with cutter radius comp");
06131   else if (g_code IS G_20)
06132     {
06133       USE_LENGTH_UNITS(CANON_UNITS_INCHES);
06134       if (settings->length_units ISNT CANON_UNITS_INCHES)
06135         {
06136           settings->length_units SET_TO CANON_UNITS_INCHES;
06137           settings->current_x SET_TO (settings->current_x * INCH_PER_MM);
06138           settings->current_y SET_TO (settings->current_y * INCH_PER_MM);
06139           settings->current_z SET_TO (settings->current_z * INCH_PER_MM);
06140           settings->axis_offset_x SET_TO
06141             (settings->axis_offset_x * INCH_PER_MM);
06142           settings->axis_offset_y SET_TO
06143             (settings->axis_offset_y * INCH_PER_MM);
06144           settings->axis_offset_z SET_TO
06145             (settings->axis_offset_z * INCH_PER_MM);
06146           settings->origin_offset_x SET_TO
06147             (settings->origin_offset_x * INCH_PER_MM);
06148           settings->origin_offset_y SET_TO
06149             (settings->origin_offset_y * INCH_PER_MM);
06150           settings->origin_offset_z SET_TO
06151             (settings->origin_offset_z * INCH_PER_MM);
06152         }
06153     }
06154   else if (g_code IS G_21)
06155     {
06156       USE_LENGTH_UNITS(CANON_UNITS_MM);
06157       if (settings->length_units ISNT CANON_UNITS_MM)
06158         {
06159           settings->length_units SET_TO CANON_UNITS_MM;
06160           settings->current_x SET_TO (settings->current_x * MM_PER_INCH);
06161           settings->current_y SET_TO (settings->current_y * MM_PER_INCH);
06162           settings->current_z SET_TO (settings->current_z * MM_PER_INCH);
06163           settings->axis_offset_x SET_TO
06164             (settings->axis_offset_x * MM_PER_INCH);
06165           settings->axis_offset_y SET_TO
06166             (settings->axis_offset_y * MM_PER_INCH);
06167           settings->axis_offset_z SET_TO
06168             (settings->axis_offset_z * MM_PER_INCH);
06169           settings->origin_offset_x SET_TO
06170             (settings->origin_offset_x * MM_PER_INCH);
06171           settings->origin_offset_y SET_TO
06172             (settings->origin_offset_y * MM_PER_INCH);
06173           settings->origin_offset_z SET_TO
06174             (settings->origin_offset_z * MM_PER_INCH);
06175         }
06176     }
06177   else
06178     BUG_MACRO(name, "Code is not G20 or G21 in convert_length_units");
06179   return RS274NGC_OK;
06180 }
06181 
06182 /****************************************************************************/
06183 
06184 /* convert_modal_0
06185 
06186 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
06187    If any of the following errors occur, this returns RS274NGC_ERROR.
06188    Otherwise, it returns RS274NGC_OK.
06189    1. code is not G_10, G_92, or G92.2
06190    2. One of the following functions is called and returns an error.
06191       convert_axis_offsets
06192       convert_setup
06193 
06194 Side effects:
06195    A g_code in g modal group 0 is executed (g10, g92, or g92.2)
06196 
06197 Called by: convert_g.
06198 
06199 */
06200 
06201 int convert_modal_0(     /* ARGUMENT VALUES                              */
06202  int code,               /* G code, must be G_10, G_92, or G_92_2        */
06203  block_pointer block,    /* pointer to a block of RS274/NGC instructions */
06204  setup_pointer settings) /* pointer to machine settings                  */
06205 {
06206   static char name[] SET_TO "convert_modal_0";
06207 
06208   if (code IS G_10)
06209     {
06210       if (convert_setup(block, settings) IS RS274NGC_ERROR)
06211         ERROR_MACRO_PASS(name);
06212     }
06213   else if ((code IS G_92) OR (code IS G_92_2))
06214     {
06215       if (convert_axis_offsets(code, block, settings) IS RS274NGC_ERROR)
06216         ERROR_MACRO_PASS(name);
06217     }
06218   else
06219     BUG_MACRO(name, "Code is not G10, G92, or G92.2 in convert_modal_0");
06220   return RS274NGC_OK;
06221 }
06222 
06223 /****************************************************************************/
06224 
06225 /* convert_motion
06226 
06227 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
06228    If any of the following errors occur, this returns RS274NGC_ERROR.
06229    Otherwise, it returns RS274NGC_OK.
06230    1. One of the following functions is called and returns RS274NGC_ERROR
06231       convert_arc
06232       convert_cycle
06233       convert_straight
06234    2. BUG - The motion code is not 0,1,2,3,80,81,82,83,84,85,86,87,88, or 89.
06235    3. An x, y, or z coordinate value is given with g80.
06236    4. Inverse time feed mode is on and no feed setting is in the block.
06237    5. A probe move (G38.2) is called in inverse time feed mode.
06238    6. A probe move is called with cutter radius compensation on.
06239    7. A probe move is called with zero feed rate.
06240 
06241 Side effects:
06242    A g_code from the group causing motion (mode 1) is executed.
06243 
06244 Called by: convert_g.
06245 
06246 Error number 3 above does not prevent the use of coordinate offsets in
06247 a block where the implicit motion mode is G80 because convert motion
06248 will not be called in this case (because the motion_mode will be -1).
06249 
06250 */
06251 
06252 int convert_motion(      /* ARGUMENT VALUES                           */
06253  int motion,             /* g_code for a line, arc, canned cycle      */
06254  block_pointer block,    /* pointer to a block of RS274 instructions  */
06255  setup_pointer settings) /* pointer to machine settings               */
06256 {
06257   static char name[] SET_TO "convert_motion";
06258 
06259   if ((motion IS G_0) OR (motion IS G_1))
06260     {
06261       if (convert_straight (motion, block, settings) IS RS274NGC_ERROR)
06262         ERROR_MACRO_PASS(name);
06263     }
06264   else if ((motion IS G_3) OR (motion IS G_2))
06265     {
06266       if ((settings->feed_mode IS INVERSE_TIME) AND
06267           (block->f_number IS -1.0))
06268         ERROR_MACRO(_interpreter_linetext, name, "F word missing with inverse time arc move");
06269       if (convert_arc (motion, block, settings) IS RS274NGC_ERROR)
06270         ERROR_MACRO_PASS(name);
06271     }
06272   else if (motion IS G_38_2)
06273     {
06274       if (settings->feed_mode IS INVERSE_TIME)
06275         ERROR_MACRO(_interpreter_linetext, name, "Cannot probe in inverse time feed mode");
06276       if (settings->cutter_radius_compensation ISNT OFF) /* NOT "IS ON"! */
06277         ERROR_MACRO(_interpreter_linetext, name, "Cannot probe with cutter radius compensation on");
06278       if (settings->feed_rate IS 0.0)
06279         ERROR_MACRO(_interpreter_linetext, name, "Cannot probe with zero feed rate");
06280       if (convert_probe (block, settings) IS RS274NGC_ERROR)
06281         ERROR_MACRO_PASS(name);
06282     }
06283   else if (motion IS G_80)
06284     {
06285       if((block->x_flag) OR (block->y_flag) OR (block->z_flag))
06286         ERROR_MACRO(_interpreter_linetext, name, "Coordinate setting given with G80");
06287       else
06288         {
06289 #ifdef DEBUG_EMC
06290           COMMENT("interpreter: motion mode set to none");
06291 #endif
06292           settings->motion_mode SET_TO G_80;
06293         }
06294     }
06295   else if ((motion > G_80) AND (motion < G_90))
06296     {
06297      if (convert_cycle(motion, block, settings) IS RS274NGC_ERROR)
06298         ERROR_MACRO_PASS(name);
06299     }
06300   else
06301     BUG_MACRO(name, "Code is not G0 to G3 or G80 to G89 in convert_motion");
06302 
06303   return RS274NGC_OK;
06304 }
06305 
06306 /****************************************************************************/
06307 
06308 /* convert_retract_mode
06309 
06310 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
06311    If any of the following errors occur, this returns RS274NGC_ERROR.
06312    Otherwise, it returns RS274NGC_OK.
06313    1. BUG - g_code isn't G_98 or G_99.
06314 
06315 Side effects:
06316    The interpreter switches the machine settings to indicate the current
06317    retract mode for canned cycles (OLD_Z or R_PLANE).
06318 
06319 Called by: convert_g.
06320 
06321 The canonical machine to which commands are being sent does not have a
06322 retract mode, so no command setting the retract mode is generated in
06323 this function.
06324 
06325 */
06326 
06327 int convert_retract_mode( /* ARGUMENT VALUES                              */
06328  int g_code,              /* g_code being executed (must be G_98 or G_99) */
06329  block_pointer block,     /* pointer to a block of RS274/NGC instructions */
06330  setup_pointer settings)  /* pointer to machine settings                  */
06331 {
06332   static char name[] SET_TO "convert_retract_mode";
06333   if (g_code IS G_98)
06334     {
06335 #ifdef DEBUG_EMC
06336       COMMENT("interpreter: retract mode set to old_z");
06337 #endif
06338       settings->retract_mode SET_TO OLD_Z;
06339     }
06340   else if (g_code IS G_99)
06341     {
06342 #ifdef DEBUG_EMC
06343       COMMENT("interpreter: retract mode set to r_plane");
06344 #endif
06345       settings->retract_mode SET_TO R_PLANE;
06346     }
06347   else
06348     BUG_MACRO(name, "Code is not G98 or G99 in convert_retract_mode");
06349   return RS274NGC_OK;
06350 }
06351 
06352 /****************************************************************************/
06353 
06354 /* convert_set_plane
06355 
06356 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
06357    If any of the following errors occur, this returns RS274NGC_ERROR.
06358    Otherwise, it returns RS274NGC_OK.
06359    1. G_18 or G_19 is called when cutter radius compensation is on.
06360    2. BUG - The g_code is not G_17, G_18, or G_19,
06361 
06362 Side effects:
06363    A command setting the current plane is executed.
06364 
06365 Called by: convert_g.
06366 
06367 */
06368 
06369 int convert_set_plane(    /* ARGUMENT VALUES                          */
06370  int g_code,              /* must be G_17, G_18, or G_19              */
06371  block_pointer block,     /* pointer to a block of RS274 instructions */
06372  setup_pointer settings)  /* pointer to machine settings              */
06373 {
06374   static char name[] SET_TO "convert_set_plane";
06375   if (g_code IS G_17)
06376     {
06377       SELECT_PLANE(CANON_PLANE_XY);
06378       settings->plane SET_TO CANON_PLANE_XY;
06379     }
06380   else if (g_code IS G_18)
06381     {
06382       if (settings->cutter_radius_compensation ISNT OFF)
06383         ERROR_MACRO(_interpreter_linetext, name, "Cannot use XZ plane with cutter radius comp");
06384       SELECT_PLANE(CANON_PLANE_XZ);
06385       settings->plane SET_TO CANON_PLANE_XZ;
06386     }
06387   else if (g_code IS G_19)
06388     {
06389       if (settings->cutter_radius_compensation ISNT OFF)
06390         ERROR_MACRO(_interpreter_linetext, name, "Cannot use YZ plane with cutter radius comp");
06391       SELECT_PLANE(CANON_PLANE_YZ);
06392       settings->plane SET_TO CANON_PLANE_YZ;
06393     }
06394   else
06395     BUG_MACRO(name, "Code is not G17, G18, or G19 in convert_set_plane");
06396   return RS274NGC_OK;
06397 }
06398 
06399 /****************************************************************************/
06400 
06401 /* convert_tool_change
06402 
06403 Returned Value: int (RS274NGC_OK)
06404 
06405 Side effects:
06406    This makes function calls to primitive machining functions, and sets
06407    the machine model as described below.
06408 
06409 Called by: convert_m
06410 
06411 This function carries out an m6 command, which changes the tool in the
06412 spindle. The only function call this makes is to the CHANGE_TOOL
06413 function. The semantics of this function call is that when it is
06414 completely carried out, the tool that was selected is in the spindle,
06415 the tool that was in the spindle (if any) is returned to its changer
06416 slot, the spindle will be stopped (but the spindle speed setting will
06417 not have changed) and the x, y, z, and b positions will be the same
06418 as they were before (although they may have moved around during the
06419 change).
06420 
06421 It would be nice to add more flexibility to this function by allowing
06422 more changes to occur (position changes, for example) as a result of
06423 the tool change. There are at least two ways of doing this:
06424 
06425 1. Require that certain machine settings always have a given fixed
06426 value after a tool change (which may be different from what the value
06427 was before the change), and record the fixed values somewhere (in the
06428 world model that is read at initialization, perhaps) so that this
06429 function can retrieve them and reset any settings that have changed.
06430 Fixed values could even be hard coded in this function.
06431 
06432 2. Allow the executor of the CHANGE_TOOL function to change the state
06433 of the world however it pleases, and have the interpreter read the
06434 executor's world model after the CHANGE_TOOL function is carried out.
06435 Implementing this would require a change in other parts of the EMC
06436 system, since calls to the interpreter would then have to be
06437 interleaved with execution of the function calls output by the
06438 interpreter.
06439 
06440 There may be other commands in the block that includes the tool change.
06441 They will be executed in the order described in execute_block.
06442 
06443 This implements the "Next tool in T word" approach to tool selection.
06444 The tool is selected when the T word is read (and the carousel may
06445 move at that time) but is changed when M6 is read.
06446 
06447 Note that if a different tool is put into the spindle, the current_z
06448 location setting may be incorrect for a time. It is assumed the
06449 program will contain an appropriate USE_TOOL_LENGTH_OFFSET command
06450 near the CHANGE_TOOL command, so that the incorrect setting is only
06451 temporary.
06452 
06453 In [NCMS, page 73, 74] there are three other legal approaches in addition
06454 to this one.
06455 
06456 */
06457 
06458 int convert_tool_change(  /* ARGUMENT VALUES                             */
06459  block_pointer block,     /* pointer to a block of RS274NGC instructions */
06460  setup_pointer settings)  /* pointer to machine settings                 */
06461 {
06462   static char name[] SET_TO "convert_tool_change";
06463 
06464   CHANGE_TOOL(settings->selected_tool_slot);
06465   settings->current_slot SET_TO settings->selected_tool_slot;
06466   settings->spindle_turning SET_TO CANON_STOPPED;
06467 
06468   return RS274NGC_OK;
06469 }
06470 
06471 /****************************************************************************/
06472 
06473 /* convert_tool_length_offset
06474 
06475 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
06476    If any of the following errors occur, this returns RS274NGC_ERROR.
06477    Otherwise, it returns RS274NGC_OK.
06478    1. The block has no offset index (h number).
06479    2. The h number is bigger than the largest table index.
06480    3. BUG - The g_code argument is not G_43 or G_49.
06481 
06482 Side effects:
06483    A USE_TOOL_LENGTH_OFFSET function call is made. Current_z,
06484    tool_length_offset, and length_offset_index are reset.
06485 
06486 Called by: convert_g
06487 
06488 This is called to execute g43 or g49.
06489 
06490 The g49 RS274/NGC command translates into a USE_TOOL_LENGTH_OFFSET(0.0)
06491 function call.
06492 
06493 The g43 RS274/NGC command translates into a USE_TOOL_LENGTH_OFFSET(length)
06494 function call, where length is the value of the entry in the tool length
06495 offset table whose index is the H number in the block.
06496 
06497 The H number in the block (if present) was checked for being a
06498 positive integer when it was read, so it that check does not need to
06499 be repeated.
06500 
06501 */
06502 
06503 int convert_tool_length_offset( /* ARGUMENT VALUES                       */
06504  int g_code,             /* g_code being executed (must be G_43 or G_49) */
06505  block_pointer block,    /* pointer to a block of RS274/NGC instructions */
06506  setup_pointer settings) /* pointer to machine settings                  */
06507 {
06508   static char name[] SET_TO "convert_tool_length_offset";
06509   int index;
06510   double offset;
06511 
06512   if (g_code IS G_49)
06513     {
06514       USE_TOOL_LENGTH_OFFSET(0.0);
06515       settings->current_z SET_TO (settings->current_z +
06516                                   settings->tool_length_offset);
06517       settings->tool_length_offset SET_TO 0.0;
06518       settings->length_offset_index SET_TO 0;
06519     }
06520   else if (g_code IS G_43)
06521     {
06522       index SET_TO block->h_number;
06523       if (index IS -1)
06524         ERROR_MACRO(_interpreter_linetext, name, "Offset index missing");
06525       else if (index > GET_EXTERNAL_TOOL_MAX())
06526         ERROR_MACRO(_interpreter_linetext, name, "Tool index out of bounds");
06527       else
06528         {
06529           offset SET_TO settings->tool_table[index].length;
06530           USE_TOOL_LENGTH_OFFSET(offset);
06531           settings->current_z SET_TO
06532             (settings->current_z + settings->tool_length_offset - offset);
06533           settings->tool_length_offset SET_TO offset;
06534           settings->length_offset_index SET_TO index;
06535         }
06536     }
06537   else
06538     BUG_MACRO(name, "Code is not G43 or G49 in convert_tool_length_offset");
06539   return RS274NGC_OK;
06540 }
06541 
06542 /****************************************************************************/
06543 
06544 /* read_line_number
06545 
06546 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
06547    If any of the following errors occur, this returns RS274NGC_ERROR.
06548    Otherwise, it returns RS274NGC_OK.
06549    1. The first character read is not n (bug).
06550    2. read_integer_unsigned returns RS274NGC_ERROR.
06551    3. The line number is too large (more than 5 digits).
06552 
06553 Side effects:
06554    counter is reset to the character following the line number.
06555    A line number is inserted in the block.
06556 
06557 Called by: read_items
06558 
06559 When this function is called, counter is pointing at an item on the
06560 line that starts with the character 'n', indicating a line number.
06561 The function reads characters which give the (integer) value of the
06562 line number.
06563 
06564 Note that extra initial zeros in a line number will not cause the
06565 line number to be too large.
06566 
06567 */
06568 
06569 int read_line_number( /* ARGUMENT VALUES                                */
06570  char * line,         /* string: line of RS274    code being processed  */
06571  int * counter,       /* pointer to a counter for position on the line  */
06572  block_pointer block) /* pointer to a block being filled from the line  */
06573 {
06574   static char name[] SET_TO "read_line_number";
06575   int value;
06576 
06577   if (line[*counter] ISNT 'n')
06578     BUG_MACRO(name, "Read_line_number should not have been called");
06579 
06580   *counter SET_TO (*counter + 1);
06581   if (read_integer_unsigned(line, counter, &value) IS RS274NGC_ERROR)
06582     ERROR_MACRO_PASS(name);
06583   else if (value > 99999)
06584     ERROR_MACRO(_interpreter_linetext, name, "Line number greater than 99999");
06585   else
06586     {
06587       block->line_number SET_TO value;
06588       return RS274NGC_OK;
06589     }
06590 }
06591 
06592 /****************************************************************************/
06593 
06594 /* read_one_item
06595 
06596 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
06597    If the first character read is not a known character for starting a
06598    word, or if the reader function which is called returns RS274NGC_ERROR, this
06599    returns RS274NGC_ERROR. Otherwise, it returns RS274NGC_OK.
06600 
06601 Side effects:
06602    This function reads one item from a line of RS274/NGC code and inserts
06603    the information in a block. System parameters may be reset.
06604 
06605 Called by: read_items.
06606 
06607 When this function is called, the counter is set so that the position
06608 being considered is the first position of a word. The character at
06609 that position must be one known to the system.  In this version those
06610 characters are: d,f,g,h,i,j,k,l,m,n,p,q,r,s,t,u,x,y,z,(,#.
06611 This function does not look for N words because read_items calls
06612 read_line_number directly.
06613 
06614 The function looks for a letter or special character and calls a
06615 selected function according to what the letter or character is.  The
06616 selected function will be responsible to consider all the characters
06617 that comprise the remainder of the item, and reset the pointer so that
06618 points to the next character after the end of the item (which may be
06619 the end of the line or the first character of another item).
06620 
06621 After an item is read, the counter is set at the index of the
06622 next unread character. The item data is stored in the block.
06623 
06624 It is expected that the format of a comment will have been checked;
06625 this is being done by close_and_downcase. Bad format comments will
06626 have prevented the system from getting this far, so that this function
06627 can assume a close parenthesis will be found when an open parenthesis
06628 has been found, and that comments are not nested.
06629 
06630 */
06631 
06632 typedef int (*_function_pointer) (char *, int *, block_pointer, double *);
06633 
06634 int read_one_item(    /* ARGUMENT VALUES                                */
06635  char * line,         /* string: line of RS274/NGC code being processed */
06636  int * counter,       /* pointer to a counter for position on the line  */
06637  block_pointer block, /* pointer to a block being filled from the line  */
06638  double * parameters) /* array of system parameters                     */
06639 {
06640   static char name[] SET_TO "read_one_item";
06641   _function_pointer function_pointer;
06642 
06643   switch (line[*counter])
06644     {
06645     case 'd':
06646       function_pointer SET_TO read_d;
06647       break;
06648     case 'f':
06649       function_pointer SET_TO read_f;
06650       break;
06651     case 'g':
06652       function_pointer SET_TO read_g;
06653       break;
06654     case 'h':
06655       function_pointer SET_TO read_tool_length_offset;
06656       break;
06657     case 'i':
06658       function_pointer SET_TO read_i;
06659       break;
06660     case 'j':
06661       function_pointer SET_TO read_j;
06662       break;
06663     case 'k':
06664       function_pointer SET_TO read_k;
06665       break;
06666     case 'l':
06667       function_pointer SET_TO read_l;
06668       break;
06669     case 'm':
06670       function_pointer SET_TO read_m;
06671       break;
06672     case 'p':
06673       function_pointer SET_TO read_p;
06674       break;
06675     case 'q':
06676       function_pointer SET_TO read_q;
06677       break;
06678     case 'r':
06679       function_pointer SET_TO read_r;
06680       break;
06681     case 's':
06682       function_pointer SET_TO read_spindle_speed;
06683       break;
06684     case 't':
06685       function_pointer SET_TO read_tool;
06686       break;
06687     case 'x':
06688       function_pointer SET_TO read_x;
06689       break;
06690     case 'y':
06691       function_pointer SET_TO read_y;
06692       break;
06693     case 'z':
06694       function_pointer SET_TO read_z;
06695       break;
06696     case '(':
06697       function_pointer SET_TO read_comment;
06698       break;
06699     case '#':
06700       function_pointer SET_TO read_parameter_setting;
06701       break;
06702     default:
06703       ERROR_MACRO(_interpreter_linetext, name, "Bad character used");
06704     }
06705   if (function_pointer (line, counter, block, parameters) IS RS274NGC_ERROR)
06706     ERROR_MACRO_PASS(name);
06707   return RS274NGC_OK;
06708 }
06709 
06710 /****************************************************************************/
06711 
06712 /* check_items
06713 
06714 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
06715    If any of the following errors occur, this returns RS274NGC_ERROR
06716    Otherwise, it returns RS274NGC_OK.
06717    1. check_g_codes return RS274NGC_ERROR.
06718    2. check_m_codes returns RS274NGC_ERROR.
06719    3. check_other_codes returns RS274NGC_ERROR.
06720    4. A G38.2 (probing) is used with an M code for stopping.
06721 
06722 Side effects: none
06723 
06724 Called by: read_line
06725 
06726 This runs checks on a block of RS274 code.
06727 
06728 The functions named read_XXXX check for errors which would foul up the
06729 reading. This function checks for additional logical errors.
06730 
06731 A block has an array of g_codes, which are initialized to -1
06732 (meaning no code). This calls check_g_codes to check the g_codes.
06733 
06734 A block has an array of m_codes, which are initialized to -1
06735 (meaning no code). This calls check_m_codes to check the m_codes.
06736 
06737 Items in the block which are not m or g codes are checked by
06738 check_other_codes.
06739 
06740 */
06741 
06742 int check_items(          /* ARGUMENT VALUES                  */
06743  block_pointer block,     /* pointer to a block to be checked */
06744  setup_pointer settings)  /* pointer to machine settings      */
06745 {
06746   static char name[] SET_TO "check_items";
06747   if (check_g_codes(block, settings) ISNT RS274NGC_OK)
06748     ERROR_MACRO_PASS(name);
06749   if (check_m_codes(block, settings) ISNT RS274NGC_OK)
06750     ERROR_MACRO_PASS(name);
06751   if (check_other_codes(block, settings) ISNT RS274NGC_OK)
06752     ERROR_MACRO_PASS(name);
06753   if ((block->m_modes[4] ISNT -1) AND (block->g_modes[1] IS G_38_2))
06754     ERROR_MACRO(_interpreter_linetext, name, "Cannot put an M code for stopping with G38.2");
06755   return RS274NGC_OK;
06756 }
06757 
06758 /****************************************************************************/
06759 
06760 /* close_and_downcase
06761 
06762 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
06763    If one of the following errors occurs, this returns RS274NGC_ERROR
06764    Otherwise, it returns RS274NGC_OK.
06765    1. A left parenthesis is found inside a comment.
06766    2. The line ends before an open comment is closed
06767    3. A newline character is found that is not followed by null
06768    4. The input line was too long
06769 
06770 Side effects:
06771    see below.
06772 
06773 Called by:
06774    read_text
06775 
06776 To simplify handling upper case letters, spaces, and tabs, this
06777 function removes spaces and and tabs and downcases everything on a
06778 line which is not part of a comment.
06779 
06780 Comments are left unchanged in place. Comments are anything
06781 enclosed in parentheses. Nested comments, indicated by a left
06782 parenthesis inside a comment, are illegal.
06783 
06784 The line must have a null character at the end when it comes in.
06785 The line may have one newline character just before the end. If
06786 there is a newline, it will be removed.
06787 
06788 Although this software system detects and rejects all illegal characters
06789 and illegal syntax, this particular function does not detect problems
06790 with anything but comments.
06791 
06792 We are treating RS274 code here as case-insensitive and spaces and
06793 tabs as if they have no meaning. RS274D, page 6 says spaces and tabs
06794 are to be ignored by control.
06795 
06796 The manual [NCMS] says nothing about case or spaces and tabs.
06797 
06798 */
06799 
06800 int close_and_downcase( /* ARGUMENT VALUES             */
06801  char * line)           /* string: one line of NC code */
06802 {
06803   static char name[] SET_TO "close_and_downcase";
06804   int m;
06805   int n;
06806   int comment;
06807   char item;
06808   comment SET_TO 0;
06809   for (n SET_TO 0, m SET_TO 0; (item SET_TO line[m]) ISNT (char) NULL; m++)
06810     {
06811       if (comment)
06812         {
06813           line[n++] SET_TO item;
06814           if (item IS ')')
06815             {
06816               comment SET_TO 0;
06817             }
06818           else if (item IS '(')
06819             ERROR_MACRO(_interpreter_linetext, name, "Nested comment found");
06820         }
06821       else if ((item IS ' ') OR (item IS '\t') OR (item IS '\r'));
06822                                       /* don't copy blank or tab  or CR */
06823       else if (item IS '\n')          /* don't copy newline             */
06824         {                             /* but check null follows         */
06825           if (line[m+1] ISNT 0)
06826             ERROR_MACRO(_interpreter_linetext, name, "Null missing after newline");
06827         }
06828       else if ((64 < item) AND (item < 91)) /* downcase upper case letters */
06829         {
06830           line[n++] SET_TO (32 + item);
06831         }
06832       else if (item IS '(')   /* comment is starting */
06833         {
06834           comment SET_TO 1;
06835           line[n++] SET_TO item;
06836         }
06837       else
06838         {
06839           line[n++] SET_TO item; /* copy anything else */
06840         }
06841     }
06842   if (m IS (INTERP_TEXT_SIZE - 1)) /* line was too long */
06843     ERROR_MACRO(_interpreter_linetext, name, "Command too long");
06844   else if (comment)
06845     ERROR_MACRO(_interpreter_linetext, name, "Unclosed comment found");
06846   line[n] SET_TO 0;
06847   return RS274NGC_OK;
06848 }
06849 
06850 /****************************************************************************/
06851 
06852 /* convert_comment
06853 
06854 Returned Value: int (RS274NGC_OK)
06855 
06856 Side effects:
06857    The message function is called if the string starts with "MSG,".
06858    Otherwise, the comment function is called.
06859 
06860 Called by: execute_block
06861 
06862 To be a message, the first four characters of the comment after the
06863 opening left parenthesis must be "MSG,", ignoring the case of the
06864 letters and allowing spaces or tabs anywhere before the comma (to make
06865 the treatment of case and white space consistent with how it is
06866 handled elsewhere).
06867 
06868 Messages are not provided for in [NCMS]. They are implemented here as a
06869 subtype of comment.
06870 
06871 */
06872 
06873 int convert_comment( /*ARGUMENT VALUES      */
06874  char * comment)     /* string with comment */
06875 {
06876   int m;
06877   int item;
06878 
06879   for (m SET_TO 0; ((item SET_TO comment[m]) IS ' ') OR (item IS '\t') ; m++);
06880   if ((item ISNT 'M') AND (item ISNT 'm'))
06881     {
06882       COMMENT(comment);
06883       return RS274NGC_OK;
06884     }
06885   for (m++; ((item SET_TO comment[m]) IS ' ') OR (item IS '\t') ; m++);
06886   if ((item ISNT 'S') AND (item ISNT 's'))
06887     {
06888       COMMENT(comment);
06889       return RS274NGC_OK;
06890     }
06891   for (m++; ((item SET_TO comment[m]) IS ' ') OR (item IS '\t') ; m++);
06892   if ((item ISNT 'G') AND (item ISNT 'g'))
06893     {
06894       COMMENT(comment);
06895       return RS274NGC_OK;
06896     }
06897   for (m++; ((item SET_TO comment[m]) IS ' ') OR (item IS '\t') ; m++);
06898   if (item ISNT ',')
06899     {
06900       COMMENT(comment);
06901       return RS274NGC_OK;
06902     }
06903   MESSAGE(comment + m + 1);
06904   return RS274NGC_OK;
06905 }
06906 
06907 /****************************************************************************/
06908 
06909 /* convert_feed_mode
06910 
06911 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
06912    BUG - If g_code isn't G_93 or G_94, this returns RS274NGC_ERROR.
06913    Otherwise, it returns RS274NGC_OK.
06914 
06915 Side effects:
06916    The interpreter switches the machine settings to indicate the current
06917    feed mode (UNITS_PER_MINUTE or INVERSE_TIME).
06918 
06919    The canonical machine to which commands are being sent does not have
06920    a feed mode, so no command setting the distance mode is generated in
06921    this function. A comment function call is made (conditionally)
06922    explaining the change in mode, however.
06923 
06924 Called by: execute_block.
06925 
06926 */
06927 
06928 int convert_feed_mode(     /* ARGUMENT VALUES                              */
06929  int g_code,               /* g_code being executed (must be G_93 or G_94) */
06930  block_pointer block,      /* pointer to a block of RS274 instructions     */
06931  setup_pointer settings)   /* pointer to machine settings                  */
06932 {
06933   static char name[] SET_TO "convert_feed_mode";
06934   if (g_code IS G_93)
06935     {
06936 #ifdef DEBUG_EMC
06937       COMMENT("interpreter: feed mode set to inverse time");
06938 #endif
06939       settings->feed_mode SET_TO INVERSE_TIME;
06940     }
06941   else if (g_code IS G_94)
06942     {
06943 #ifdef DEBUG_EMC
06944       COMMENT("interpreter: feed mode set to units per minute");
06945 #endif
06946       settings->feed_mode SET_TO UNITS_PER_MINUTE;
06947     }
06948   else
06949     BUG_MACRO(name, "Code is not G93 or G94 in convert_feed_mode");
06950   return RS274NGC_OK;
06951 }
06952 
06953 /****************************************************************************/
06954 
06955 /* convert_feed_rate
06956 
06957 Returned Value: int (RS274NGC_OK)
06958 
06959 Side effects:
06960    The machine feed_rate is set to the value of f_number in the
06961      block by function call.
06962    The machine model feed_rate is set to that value.
06963 
06964 Called by: execute_block
06965 
06966 */
06967 
06968 int convert_feed_rate(   /* ARGUMENT VALUES                          */
06969  block_pointer block,    /* pointer to a block of RS274 instructions */
06970  setup_pointer settings) /* pointer to machine settings              */
06971 {
06972   SET_FEED_RATE(block->f_number);
06973   settings->feed_rate SET_TO block->f_number;
06974   return RS274NGC_OK;
06975 }
06976 
06977 /****************************************************************************/
06978 
06979 /* convert_g
06980 
06981 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
06982    If any of the following errors occurs, this returns RS274NGC_ERROR.
06983    Otherwise, it returns RS274NGC_OK.
06984    1. A g code in the block is not known to the system.
06985    2. One of the following functions is called and returns error:
06986       convert_control_mode
06987       convert_coordinate_system
06988       convert_cutter_compensation
06989       convert_distance_mode
06990       convert_length_units
06991       convert_modal_0
06992       convert_motion
06993       convert_retract_mode
06994       convert_set_plane
06995       convert_tool_length_offset
06996 
06997 Side effects:
06998    Any g_codes in the block (excluding g93 and 94) and any implicit
06999    motion g_code are executed.
07000 
07001 Called by: execute_block.
07002 
07003 This takes a pointer to a block of RS274/NGC instructions (already
07004 read in) and creates the appropriate output commands corresponding to
07005 any "g" codes in the block.
07006 
07007 Codes g93 and g94, which set the feed mode, are executed earlier by
07008 execute_block before reading the feed rate.
07009 
07010 G codes are are executed in the following order.
07011 1. mode 4, (G4) - dwell. G53 is also in mode 4 but is not executed
07012    here.
07013 2. mode 2, one of (G17, G18, G19) - plane selection.
07014 3. mode 6, one of (G20, G21) - length units.
07015 4. mode 7, one of (G40, G41, G42) - cutter radius compensation.
07016 5. mode 8, one of (G43, G49) - tool length offset
07017 6. mode 12, one of (G54, G55, G56, G57, G58, G59, G59.1, G59.2, G59.3)
07018    - coordinate system selection.
07019 7. mode 13, one of (G61, G64) - control mode (exact path or continuous)
07020 8. mode 3, one of (G90, G91) - distance mode.
07021 9. mode 10, one of (G98, G99) - retract mode.
07022 10. mode 0 (G10, G92, G92.2) - setting coordinate system locations, setting
07023     or cancelling axis offsets.
07024 11. mode 1, one of (G0, G1, G2, G3, G38.2, G80, G81 to G89) - motion or cancel.
07025 
07026 The mode 0 and mode 1 G codes must be executed after the length units
07027 are set, since they use coordinate values. Mode 1 codes also must wait
07028 until most of the other modes are set.
07029 
07030 The mode 0 and mode 1 G codes are not quite mutually exclusive because
07031 G80 is in the mode 1 group. check_g_codes prevents competition between
07032 the mode 0 and mode 1 G codes for the use of axis values. If a mode 0
07033 G code is used, only G80 from the mode 1 group is possible.
07034 
07035 Mode 4 codes (G4 and G53) can coexist with mode 1 codes.
07036 
07037 */
07038 
07039 int convert_g(           /* ARGUMENT VALUES                              */
07040  block_pointer block,    /* pointer to a block of RS274/NGC instructions */
07041  setup_pointer settings) /* pointer to machine settings                  */
07042 {
07043   static char name[] SET_TO "convert_g";
07044 
07045   if (block->g_modes[4] IS G_4)
07046     {
07047       if (convert_dwell(block->p_number) IS RS274NGC_ERROR)
07048         ERROR_MACRO_PASS(name);
07049     }
07050 
07051   if (block->g_modes[2] ISNT -1)
07052     {
07053       if (convert_set_plane(block->g_modes[2], block, settings)
07054           IS RS274NGC_ERROR)
07055         ERROR_MACRO_PASS(name);
07056     }
07057 
07058   if (block->g_modes[6] ISNT -1)
07059     {
07060       if (convert_length_units(block->g_modes[6], settings) IS RS274NGC_ERROR)
07061         ERROR_MACRO_PASS(name);
07062     }
07063 
07064   if (block->g_modes[7] ISNT -1)
07065     {
07066       if (convert_cutter_compensation(block->g_modes[7], block, settings)
07067           IS RS274NGC_ERROR)
07068         ERROR_MACRO_PASS(name);
07069     }
07070 
07071   if (block->g_modes[8] ISNT -1)
07072     {
07073       if (convert_tool_length_offset(block->g_modes[8], block, settings)
07074           IS RS274NGC_ERROR)
07075         ERROR_MACRO_PASS(name);
07076     }
07077 
07078   if (block->g_modes[12] ISNT -1)
07079     {
07080       if (convert_coordinate_system (block->g_modes[12], block, settings)
07081           IS RS274NGC_ERROR)
07082         ERROR_MACRO_PASS(name);
07083     }
07084 
07085   if (block->g_modes[13] ISNT -1)
07086     {
07087       if (convert_control_mode (block->g_modes[13], block, settings)
07088           IS RS274NGC_ERROR)
07089         ERROR_MACRO_PASS(name);
07090     }
07091 
07092   if (block->g_modes[3] ISNT -1)
07093     {
07094       if (convert_distance_mode(block->g_modes[3], block, settings)
07095           IS RS274NGC_ERROR)
07096         ERROR_MACRO_PASS(name);
07097     }
07098 
07099   if (block->g_modes[10] ISNT -1)
07100     {
07101       if (convert_retract_mode(block->g_modes[10], block, settings)
07102           IS RS274NGC_ERROR)
07103         ERROR_MACRO_PASS(name);
07104     }
07105 
07106   if (block->g_modes[0] ISNT -1)
07107     {
07108       if (convert_modal_0(block->g_modes[0], block, settings)
07109           IS RS274NGC_ERROR)
07110         ERROR_MACRO_PASS(name);
07111     }
07112 
07113   if (block->motion_to_be ISNT -1)
07114     {
07115       if (convert_motion(block->motion_to_be, block, settings)
07116           IS RS274NGC_ERROR)
07117         ERROR_MACRO_PASS(name);
07118     }
07119   return RS274NGC_OK;
07120 }
07121 
07122 /****************************************************************************/
07123 
07124 /* convert_m
07125 
07126 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
07127    If convert_tool_change is returns RS274NGC_ERROR, this
07128    returns RS274NGC_ERROR. Otherwise, it returns RS274NGC_OK.
07129 
07130 Side effects:
07131    m_codes in the block are executed. For each m_code
07132    this consists of making a function call to a primitive machining
07133    function and setting the machine model.
07134 
07135 Called by: execute_block.
07136 
07137 This handles the following types of activity in order:
07138 1. changing the tool (m6) - which also retracts and stops the spindle.
07139 2. Turning the spindle on or off (m3, m4, and m5)
07140 3. Turning coolant on and off (m7, m8, and m9)
07141 4. enabling or disabling feed and speed overrides (m49, m49).
07142 Within each group, only the first code encountered will be executed.
07143 
07144 This is called if any m_code is programmed, but does nothing with m0,
07145 m1, m2, m30, or m60 (which are handled in convert_stop).
07146 
07147 */
07148 
07149 int convert_m(           /* ARGUMENT VALUES                              */
07150  block_pointer block,    /* pointer to a block of RS274/NGC instructions */
07151  setup_pointer settings) /* pointer to machine settings                  */
07152 {
07153   static char name[] SET_TO "convert_m";
07154   if (block->m_modes[6] ISNT -1)
07155     if (convert_tool_change(block, settings) IS RS274NGC_ERROR)
07156       ERROR_MACRO_PASS(name);
07157 
07158   if (block->m_modes[7] IS 3)
07159     {
07160       START_SPINDLE_CLOCKWISE();
07161       settings->spindle_turning SET_TO CANON_CLOCKWISE;
07162     }
07163   else if (block->m_modes[7] IS 4)
07164     {
07165       START_SPINDLE_COUNTERCLOCKWISE();
07166       settings->spindle_turning SET_TO CANON_COUNTERCLOCKWISE;
07167     }
07168   else if (block->m_modes[7] IS 5)
07169     {
07170       STOP_SPINDLE_TURNING();
07171       settings->spindle_turning SET_TO CANON_STOPPED;
07172     }
07173 
07174   if (block->m_modes[8] IS 7)
07175     {
07176       MIST_ON();
07177       settings->mist SET_TO ON;
07178     }
07179   else if (block->m_modes[8] IS 8)
07180     {
07181       FLOOD_ON();
07182       settings->flood SET_TO ON;
07183     }
07184   else if (block->m_modes[8] IS 9)
07185     {
07186       MIST_OFF();
07187       settings->mist SET_TO OFF;
07188       FLOOD_OFF();
07189       settings->flood SET_TO OFF;
07190     }
07191 
07192   if (block->m_modes[9] IS 48)
07193     {
07194       ENABLE_FEED_OVERRIDE();
07195       ENABLE_SPEED_OVERRIDE();
07196       settings->feed_override SET_TO ON;
07197       settings->speed_override SET_TO ON;
07198     }
07199   else if (block->m_modes[9] IS 49)
07200     {
07201       DISABLE_FEED_OVERRIDE();
07202       DISABLE_SPEED_OVERRIDE();
07203       settings->feed_override SET_TO OFF;
07204       settings->speed_override SET_TO OFF;
07205     }
07206   return RS274NGC_OK;
07207 }
07208 
07209 /****************************************************************************/
07210 
07211 /* convert_speed
07212 
07213 Returned Value: int (RS274NGC_OK)
07214 
07215 Side effects:
07216   The machine spindle speed is set to the value of s_number in the
07217   block by function call.
07218   The machine model for spindle speed is set to that value.
07219 
07220 Called by: execute_block.
07221 
07222 */
07223 
07224 int convert_speed(       /* ARGUMENT VALUES                          */
07225  block_pointer block,    /* pointer to a block of RS274 instructions */
07226  setup_pointer settings) /* pointer to machine settings              */
07227 {
07228   SET_SPINDLE_SPEED(block->s_number);
07229   settings->speed SET_TO block->s_number;
07230   return RS274NGC_OK;
07231 }
07232 
07233 /****************************************************************************/
07234 
07235 /* convert_stop
07236 
07237 Returned Value: int (RS274NGC_OK or RS274NGC_EXIT)
07238    When an m2 or m30 (program_end) is encountered, this returns RS274NGC_EXIT.
07239    BUG - If the code is not m0, m1, m2, m30, or m60, this
07240    returns RS274NGC_ERROR. Otherwise, it returns RS274NGC_OK.
07241 
07242 Side effects:
07243    An m0, m1, m2, m30, or m60 in the block is executed.
07244    This consists of making a function call to a primitive
07245    machining function.
07246 
07247    For m0, m1, and m60, this makes a function call to a canonical
07248    machining function that stops the machine. In addition, m60 calls
07249    PALLET_SHUTTLE.
07250 
07251    For m2 and m30, this resets the machine and then calls PROGRAM_END.
07252    In addition, m30 calls PALLET_SHUTTLE.
07253 
07254 Called by: execute_block.
07255 
07256 This handles stopping or ending the program (m0, m1, m2, m30, m60)
07257 
07258 [NCMS] specifies how the following modes should be reset at m2 or
07259 m30. The descriptions are not collected in one place, so this list
07260 may be incomplete.
07261 
07262 G52 offsetting coordinate zero points [NCMS, page 10]
07263 G92 coordinate offset using tool position [NCMS, page 10]
07264 
07265 The following should have reset values, but no description of reset
07266 behavior could be found in [NCMS].
07267 G17, G18, G19 selected plane [NCMS, pages 14, 20]
07268 G90, G91 distance mode [NCMS, page 15]
07269 G93, G94 feed mode [NCMS, pages 35 - 37]
07270 M48, M49 overrides enabled, disabled [NCMS, pages 37 - 38]
07271 M3, M4, M5 spindle turning [NCMS, page 7]
07272 
07273 The following should be set to some value at machine start-up but
07274 not automatically reset by any of the stopping codes.
07275 1. G20, G21 length units [NCMS, page 15]. This is up to the installer.
07276 2. motion_control_mode. This is set in rs274ngc_init but not reset here.
07277    Might add it here.
07278 
07279 The following resets have been added by calling the appropriate
07280 canonical machining command and/or by resetting interpreter
07281 settings. They occur on M2 or M30.
07282 
07283 1. Axis offsets are set to zero (like g92.2) and      - SET_ORIGIN_OFFSETS
07284    origin offsets are set to the default (like G54)
07285 2. Selected plane is set to CANON_PLANE_XY (like G17) - SELECT_PLANE
07286 3. Distance mode is set to ABSOLUTE (like G90)        - no canonical call
07287 4. Feed mode is set to UNITS_PER_MINUTE (like G94)    - no canonical call
07288 5. Feed and speed overrides are set to ON (like G48)  - ENABLE_FEED_OVERRIDE
07289                                                       - ENABLE_SPEED_OVERRIDE
07290 6. Cutter compensation is turned off (like G40)       - no canonical call
07291 7. The spindle is stopped (like M5)                   - STOP_SPINDLE_TURNING
07292 8. The motion mode is set to G_1 (like G1)            - no canonical call
07293 9. Coolant is turned off (like M9)                    - FLOOD_OFF & MIST_OFF
07294 
07295 */
07296 
07297 int convert_stop(         /* ARGUMENT VALUES                              */
07298  block_pointer block,     /* pointer to a block of RS274/NGC instructions */
07299  setup_pointer settings)  /* pointer to machine settings                  */
07300 {
07301   static char name[] SET_TO "convert_stop";
07302 
07303   if (block->m_modes[4] IS 0)
07304     {
07305       PROGRAM_STOP();
07306     }
07307   else if (block->m_modes[4] IS 60)
07308     {
07309       PALLET_SHUTTLE();
07310       PROGRAM_STOP();
07311     }
07312   else if (block->m_modes[4] IS 1)
07313     {
07314       OPTIONAL_PROGRAM_STOP();
07315     }
07316   else if ((block->m_modes[4] IS 2) OR (block->m_modes[4] IS 30))
07317     { /* reset stuff here */
07318 /*1*/
07319       settings->current_x SET_TO settings->current_x
07320         + settings->origin_offset_x;
07321       settings->current_y SET_TO settings->current_y
07322         + settings->origin_offset_y;
07323       settings->current_z SET_TO settings->current_z
07324         + settings->origin_offset_z;
07325       settings->origin_offset_x SET_TO settings->parameters[5221];
07326       settings->origin_offset_y SET_TO settings->parameters[5222];
07327       settings->origin_offset_z SET_TO settings->parameters[5223];
07328       settings->current_x SET_TO settings->current_x -
07329         settings->origin_offset_x;
07330       settings->current_y SET_TO settings->current_y -
07331         settings->origin_offset_y;
07332       settings->current_z SET_TO settings->current_z -
07333         settings->origin_offset_z;
07334       SET_ORIGIN_OFFSETS(settings->origin_offset_x + settings->axis_offset_x,
07335                          settings->origin_offset_y + settings->axis_offset_y,
07336                          settings->origin_offset_z + settings->axis_offset_z);
07337 
07338 /*2*/ if (settings->plane ISNT CANON_PLANE_XY)
07339         {
07340           SELECT_PLANE(CANON_PLANE_XY);
07341           settings->plane SET_TO CANON_PLANE_XY;
07342         }
07343 
07344 /*3*/ settings->distance_mode SET_TO MODE_ABSOLUTE;
07345 
07346 /*4*/ settings->feed_mode SET_TO UNITS_PER_MINUTE;
07347 
07348 /*5*/ if (settings->feed_override ISNT ON)
07349         {
07350           ENABLE_FEED_OVERRIDE();
07351           settings->feed_override SET_TO ON;
07352         }
07353       if (settings->speed_override ISNT ON)
07354         {
07355           ENABLE_SPEED_OVERRIDE();
07356           settings->speed_override SET_TO ON;
07357         }
07358 
07359 /*6*/ settings->cutter_radius_compensation SET_TO OFF;
07360       settings->program_x SET_TO UNKNOWN;
07361 
07362 /*7*/ STOP_SPINDLE_TURNING();
07363       settings->spindle_turning SET_TO CANON_STOPPED;
07364 
07365 /*8*/ settings->motion_mode SET_TO G_1;
07366 
07367 /*9*/ if (settings->mist IS ON)
07368         {
07369           MIST_OFF();
07370           settings->mist SET_TO OFF;
07371         }
07372       if (settings->flood IS ON)
07373         {
07374           FLOOD_OFF();
07375           settings->flood SET_TO OFF;
07376         }
07377 
07378       if (block->m_modes[4] IS 30)
07379         PALLET_SHUTTLE();
07380       PROGRAM_END();
07381       return RS274NGC_EXIT;
07382     }
07383   else
07384     BUG_MACRO(name, "Code is not M0, M1, M2, M30 or M60 in convert_stop");
07385   return RS274NGC_OK;
07386 }
07387 
07388 /****************************************************************************/
07389 
07390 /* convert_tool_select
07391 
07392 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
07393    If the tool number in the block is out of bounds, this
07394    returns RS274NGC_ERROR. Otherwise, it returns RS274NGC_OK.
07395 
07396 Side effects:
07397   A select tool command is given, which causes the changer chain
07398   to move so that the selected slot is next to the tool changer,
07399   ready for a tool change. The settings->selected_tool_slot is set
07400   to the t number in the block.
07401 
07402 Called by: execute_block.
07403 
07404 */
07405 
07406 int convert_tool_select( /* ARGUMENT VALUES                                 */
07407  block_pointer block,    /* pointer to a block of RS274/NGC instructions    */
07408  setup_pointer settings) /* pointer to machine settings                     */
07409 {
07410   static char name[] SET_TO "convert_tool_select";
07411 
07412   if (block->t_number > GET_EXTERNAL_TOOL_MAX())
07413     ERROR_MACRO(_interpreter_linetext, name, "Selected tool slot number too large");
07414 
07415   SELECT_TOOL(block->t_number);
07416   settings->selected_tool_slot SET_TO block->t_number;
07417   return RS274NGC_OK;
07418 }
07419 
07420 /****************************************************************************/
07421 
07422 /* init_block
07423 
07424 Returned Value: int (RS274NGC_OK)
07425 
07426 Side effects:
07427    Values in the block are reset as described below.
07428 
07429 Called by: read_line
07430 
07431 This system reuses the same block over and over, rather than building
07432 a new one for each line of NC code. The block is re-initialized before
07433 each new line of NC code is read.
07434 
07435 The block contains many slots for values which may or may not be present
07436 on a line of NC code. For some of these slots, there is a flag which
07437 is turned on (at the time time value of the slot is read) if the item
07438 is present.  For slots whose values are to be read which do not have a
07439 flag, there is always some excluded range of values. Setting the
07440 initial value of these slot to some number in the excluded range
07441 serves to show that a value for that slot has not been read.
07442 
07443 The rules for the indicators for slots whose values may be read are:
07444 1. If the value may be an arbitrary real number (which is always stored
07445    internally as a double), a flag is needed to indicate if a value has
07446    been read. All such flags are initialized to OFF.
07447    Note that the value itself is not initialized; there is no point in it.
07448 2. If the value must be a non-negative real number (which is always stored
07449    internally as a double), a value of -1.0 indicates the item is not present.
07450 3. If the value must be an unsigned integer (which is always stored
07451    internally as an int), a value of -1 indicates the item is not present.
07452    (RS274/NGC does not use any negative integers.)
07453 4. If the value is a character string (only the comment slot is one), the
07454    first character is set to 0 (NULL).
07455 
07456 */
07457 
07458 int init_block(        /* ARGUMENT VALUES                               */
07459  block_pointer block)  /* pointer to a block to be initialized or reset */
07460 {
07461   int n;
07462   block->comment[0] SET_TO 0;
07463   block->d_number SET_TO -1;
07464   block->f_number SET_TO -1.0;
07465   block->g_count SET_TO 0;
07466   for (n SET_TO 0; n < 14; n++)
07467     {
07468       block->g_modes[n] SET_TO -1;
07469     }
07470   block->h_number SET_TO -1;
07471   block->i_flag SET_TO OFF;
07472   block->j_flag SET_TO OFF;
07473   block->k_flag SET_TO OFF;
07474   block->l_number SET_TO -1;
07475   block->line_number SET_TO -1;
07476   block->motion_to_be SET_TO -1;
07477   block->m_count SET_TO 0;
07478   for (n SET_TO 0; n < 10; n++)
07479     {
07480       block->m_modes[n] SET_TO -1;
07481     }
07482   block->p_number SET_TO -1.0;
07483   block->q_number SET_TO -1.0;
07484   block->r_flag SET_TO OFF;
07485   block->s_number SET_TO -1.0;
07486   block->t_number SET_TO -1;
07487   block->x_flag SET_TO OFF;
07488   block->y_flag SET_TO OFF;
07489   block->z_flag SET_TO OFF;
07490 
07491   return RS274NGC_OK;
07492 }
07493 
07494 /****************************************************************************/
07495 
07496 /* read_items
07497 
07498 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
07499    If read_line_number or read_one_item returns RS274NGC_ERROR,
07500    this returns RS274NGC_ERROR. Otherwise, it returns RS274NGC_OK.
07501 
07502 Side effects:
07503    One line of RS274 code is read and data inserted into a block.
07504    The counter which is passed around among the readers is initialized.
07505    System parameters may be reset.
07506 
07507 Called by: read_line
07508 
07509 */
07510 
07511 int read_items(       /* ARGUMENT VALUES                                */
07512  block_pointer block, /* pointer to a block being filled from the line  */
07513  char * line,         /* string: line of RS274/NGC code being processed */
07514  double * parameters) /* array of system parameters                     */
07515 {
07516   static char name[] SET_TO "read_items";
07517   int counter;
07518   int length;
07519 
07520   length SET_TO strlen(line);
07521   counter SET_TO 0;
07522 
07523   if (line[counter] IS '/')
07524     counter++;
07525   if (line[counter] IS 'n')
07526     {
07527       if (read_line_number(line, &counter, block) IS RS274NGC_ERROR)
07528         ERROR_MACRO_PASS(name);
07529     }
07530   for ( ; counter < length; )
07531     {
07532       if (read_one_item (line, &counter, block, parameters) IS RS274NGC_ERROR)
07533         ERROR_MACRO_PASS(name);
07534     }
07535   return RS274NGC_OK;
07536 }
07537 
07538 /****************************************************************************/
07539 
07540 /* utility_enhance_block
07541 
07542 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
07543    If
07544    this returns RS274NGC_ERROR. Otherwise, it returns RS274NGC_OK.
07545 
07546 Side effects:
07547    The value of motion_to_be in the block is set.
07548 
07549 Called by: read_line
07550 
07551 It does not seem practical to change x and y to absolute coordinates
07552 here because they are handled differently according to whether cutter
07553 radius compensation is on or not.
07554 
07555 */
07556 
07557 int utility_enhance_block( /* ARGUMENT VALUES                   */
07558  block_pointer block,      /* pointer to a block to be checked  */
07559  setup_pointer settings)   /* pointer to machine settings       */
07560 {
07561   static char name[] SET_TO "utility_enhance_block";
07562   if (block->g_modes[1] ISNT -1)
07563     block->motion_to_be SET_TO block->g_modes[1];
07564   else if ((((block->x_flag IS ON) OR (block->y_flag IS ON)) OR
07565             (block->z_flag IS ON)) AND
07566            (block->g_modes[0] IS -1))
07567     block->motion_to_be SET_TO settings->motion_mode;
07568   return RS274NGC_OK;
07569 }
07570 
07571 /****************************************************************************/
07572 
07573 /* write_g_codes
07574 
07575 Returned Value: int (RS274NGC_OK)
07576 
07577 Side effects:
07578    The gez struct is updated.
07579 
07580 Called by:
07581    rs274ngc_execute
07582    rs274ngc_init
07583 
07584 The block may be NULL.
07585 
07586 This writes active g_codes into the gez structure by examining the
07587 interpreter settings. The gez structure array of actives is composed
07588 of ints, so (to handle codes like 59.1) all g_codes are is reported as
07589 ints ten times the actual value. For example, 59.1 is reported as 591.
07590 
07591 Note: the recent_gees_pointer is no longer an array with the history.
07592 It's only a single group of the most recent ones.
07593 
07594 */
07595 
07596 int write_g_codes(        /* ARGUMENT VALUES                              */
07597  block_pointer block,     /* pointer to a block of RS274/NGC instructions */
07598  setup_pointer settings,  /* pointer to machine settings                  */
07599  int line_number,         /* number of line being executed                */
07600  int * gez)               /* pointer to array of G code numbers           */
07601 {
07602   gez[0] SET_TO line_number;            /* 0 line number     */
07603   gez[1] SET_TO settings->motion_mode;  /* 1 motion mode     */
07604   gez[2] SET_TO
07605     (block IS NULL)             ? -1                :
07606     (block->g_modes[0] ISNT -1) ? block->g_modes[0] :  /* 2 g10,g92         */
07607                                   block->g_modes[4] ;  /* 2 g4, g53         */
07608   gez[3] SET_TO
07609     (settings->plane IS CANON_PLANE_XY) ? G_17 :       /* 3 active plane    */
07610     (settings->plane IS CANON_PLANE_XZ) ? G_18 : G_19;
07611   gez[4] SET_TO                         /* 4 cut radius comp */
07612     (settings->cutter_radius_compensation IS RIGHT) ? G_42 :
07613     (settings->cutter_radius_compensation IS LEFT) ? G_41 : G_40;
07614   gez[5] SET_TO                         /* 5 length units    */
07615     (settings->length_units IS CANON_UNITS_INCHES) ? G_20 : G_21;
07616   gez[6] SET_TO                         /* 6 distance mode   */
07617     (settings->distance_mode IS MODE_ABSOLUTE) ? G_90 : G_91;
07618   gez[7] SET_TO                         /* 7 feed mode       */
07619     (settings->feed_mode IS INVERSE_TIME) ? G_93 : G_94;
07620   gez[8] SET_TO                         /* 8 coord system    */
07621     (settings->origin_ngc < 7) ? (530 + (10 * settings->origin_ngc)) :
07622                                  (584 + settings->origin_ngc);
07623   gez[9] SET_TO                         /* 9 tool len offset */
07624     (settings->tool_length_offset IS 0.0) ? G_49 : G_43;
07625   gez[10] SET_TO                        /* 10 retract mode   */
07626     (settings->retract_mode IS OLD_Z) ? G_98 : G_99;
07627   gez[11] SET_TO                        /* 11 control mode   */
07628     (settings->control_mode IS CANON_CONTINUOUS) ? G_64 : G_61;
07629 
07630   return RS274NGC_OK;
07631 }
07632 
07633 /****************************************************************************/
07634 
07635 /* write_m_codes
07636 
07637 Returned Value: int (RS274NGC_OK)
07638 
07639 Side effects:
07640    The array of ints is updated.
07641 
07642 Called by:
07643    rs274ngc_execute
07644    rs274ngc_init
07645 
07646 Note that this no longer maintains a history, and just returns the current
07647 M code settings.
07648 
07649 */
07650 
07651 int write_m_codes(       /* ARGUMENT VALUES                              */
07652  block_pointer block,    /* pointer to a block of RS274/NGC instructions */
07653  setup_pointer settings, /* pointer to machine settings                  */
07654  int line_number,        /* number of line being executed                */
07655  int * ems)              /* pointer to array of M code numbers           */
07656 {
07657   ems[0] SET_TO line_number;                           /* 0 line number */
07658   ems[1] SET_TO
07659     (block IS NULL) ? -1 : block->m_modes[4];          /* 1 stopping    */
07660   ems[2] SET_TO
07661     (settings->spindle_turning IS CANON_STOPPED) ? 5 : /* 2 spindle     */
07662     (settings->spindle_turning IS CANON_CLOCKWISE) ? 3 : 4;
07663   ems[3] SET_TO                                        /* 3 tool change */
07664     (block IS NULL) ? -1 : block->m_modes[6];
07665   ems[4] SET_TO                                        /* 4 mist        */
07666     (settings->mist IS ON) ? 7 :
07667     (settings->flood IS ON) ? -1 : 9;
07668   ems[5] SET_TO                                        /* 5 flood       */
07669     (settings->flood IS ON) ? 8 : -1;
07670   ems[6] SET_TO                                        /* 6 overrides   */
07671     (settings->feed_override IS ON) ? 48 : 49;         /* both overrides*/
07672 
07673   return RS274NGC_OK;
07674 }
07675 
07676 /****************************************************************************/
07677 
07678 /* write_settings
07679 
07680 Returned Value: int (RS274NGC_OK)
07681 
07682 Side effects:
07683    The array of doubles is updated with the F and S code settings
07684 
07685 Called by:
07686    rs274ngc_execute
07687    rs274ngc_init
07688 */
07689 
07690 int write_settings(      /* ARGUMENT VALUES                              */
07691  block_pointer block,    /* pointer to a block of RS274/NGC instructions */
07692  setup_pointer settings, /* pointer to machine settings                  */
07693  int line_number,        /* number of line being executed                */
07694  double * vals)          /* pointer to array of M code numbers           */
07695 {
07696   vals[0] SET_TO line_number;   /* 0 line number */
07697   vals[1] SET_TO settings->feed_rate; /* 1 feed rate */
07698   vals[2] SET_TO settings->speed; /* 2 spindle speed */
07699 
07700   return RS274NGC_OK;
07701 }
07702 
07703 /****************************************************************************/
07704 
07705 /* execute_block
07706 
07707 Returned Value: int (RS274NGC_OK, RS274NGC_EXIT, RS274NGC_EXECUTE_FINISH,
07708                      or RS274NGC_ERROR)
07709    If any function called returns RS274NGC_ERROR, this returns RS274NGC_ERROR.
07710    Else if convert_stop returns RS274NGC_EXIT, this returns RS274NGC_EXIT.
07711    Else if the probe_flag in the settings is ON, this returns
07712       RS274NGC_EXECUTE_FINISH
07713    Otherwise, it returns RS274NGC_OK.
07714 
07715 Side effects:
07716    One block of RS274/NGC instructions is executed.
07717 
07718 Called by:
07719    rs274ngc_execute
07720 
07721 This converts a block to zero to many actions. The order of execution
07722 of items in a block is critical to safe and effective machine
07723 operation, but is not specified clearly in the [NCMS] documentation.
07724 
07725 Actions are executed in the following order:
07726 1. any comment.
07727 2. a feed mode setting (g93, g94)
07728 3. a feed rate (f) setting if in units_per_minute feed mode.
07729 4. a spindle speed (s) setting.
07730 5. a tool selection (t).
07731 6. "m" commands as described in convert_m (includes tool change).
07732 7. any g_codes (except g93, g94) as described in convert_g.
07733 8. stopping commands (m0, m1, m2, m30, or m60).
07734 
07735 In inverse time feed mode, the explicit and implicit g code executions
07736 include feed rate setting with g1, g2, and g3. Also in inverse time
07737 feed mode, attempting a canned cycle cycle (g81 to g89) or setting a
07738 feed rate with g0 is illegal and will be detected and result in an
07739 error message.
07740 
07741 */
07742 
07743 int execute_block(         /* ARGUMENT VALUES                              */
07744  block_pointer block,      /* pointer to a block of RS274/NGC instructions */
07745  setup_pointer settings,   /* pointer to machine settings                  */
07746  int line_number)          /* number of line being executed                */
07747 {
07748   static char name[] SET_TO "execute_block";
07749   if (block->comment[0] ISNT 0)
07750     {
07751       if (convert_comment(block->comment) IS RS274NGC_ERROR)
07752         ERROR_MACRO_PASS(name);
07753     }
07754   if (block->g_modes[5] ISNT -1)
07755     {
07756       if (convert_feed_mode(block->g_modes[5], block, settings)
07757           IS RS274NGC_ERROR)
07758         ERROR_MACRO_PASS(name);
07759     }
07760   if (block->f_number > -1.0)
07761     {
07762       if (settings->feed_mode IS INVERSE_TIME); /* handle elsewhere */
07763       else if (convert_feed_rate(block, settings) IS RS274NGC_ERROR)
07764         ERROR_MACRO_PASS(name);
07765     }
07766   if (block->s_number > -1.0)
07767     {
07768       if (convert_speed(block, settings) IS RS274NGC_ERROR)
07769         ERROR_MACRO_PASS(name);
07770     }
07771   if (block->t_number ISNT -1)
07772     {
07773       if (convert_tool_select(block, settings) IS RS274NGC_ERROR)
07774         ERROR_MACRO_PASS(name);
07775     }
07776   if (convert_m(block, settings) IS RS274NGC_ERROR)
07777         ERROR_MACRO_PASS(name);
07778   if (convert_g(block, settings) IS RS274NGC_ERROR)
07779     ERROR_MACRO_PASS(name);
07780   if (block->m_modes[4] ISNT -1) /* converts m0, m1, m2, m30, or m60 */
07781     {
07782       if (convert_stop(block, settings) IS RS274NGC_EXIT)
07783         return RS274NGC_EXIT;
07784     }
07785   if (settings->probe_flag IS ON)
07786     return RS274NGC_EXECUTE_FINISH;
07787   else
07788     return RS274NGC_OK;
07789 }
07790 
07791 /****************************************************************************/
07792 
07793 /* read_line
07794 
07795 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
07796    If any of the calls to init_block, read_items, utility_enhance_block,
07797    or check_items returns RS274NGC_ERROR, this returns RS274NGC_ERROR.
07798    Otherwise, it returns RS274NGC_OK.
07799 
07800 Side effects:
07801    One RS274 line is read into a block and the block is checked for
07802    errors. System parameters may be reset.
07803 
07804 Called by:
07805    rs274ngc_execute
07806    rs274ngc_read
07807 
07808 */
07809 
07810 int read_line(           /* ARGUMENT VALUES                      */
07811  char * line,            /* array holding a line of RS274 code   */
07812  block_pointer block,    /* pointer to a block to be filled      */
07813  setup_pointer settings) /* pointer to machine settings          */
07814 {
07815   static char name[] SET_TO "read_line";
07816   if (init_block (block) IS RS274NGC_ERROR)
07817     ERROR_MACRO_PASS(name);
07818   if (read_items(block, line, settings->parameters) IS RS274NGC_ERROR)
07819     ERROR_MACRO_PASS(name);
07820   if (utility_enhance_block(block, settings) IS RS274NGC_ERROR)
07821     ERROR_MACRO_PASS(name);
07822   if (check_items (block, settings) IS RS274NGC_ERROR)
07823     ERROR_MACRO_PASS(name);
07824   return RS274NGC_OK;
07825 }
07826 
07827 /****************************************************************************/
07828 
07829 /* read_text
07830 
07831 Returned Value: int - (RS274NGC_OK or RS274NGC_ERROR)
07832    If close_and_downcase returns RS274NGC_ERROR, this returns RS274NGC_ERROR;
07833    if the file end is found, this returns RS274NGC_ENDFILE.
07834    Otherwise, it returns RS274NGC_OK.
07835 
07836 Side effects:
07837    The value of the length argument is set to the number of
07838    characters on the reduced line. The line is written into.
07839 
07840 Called by:
07841    rs274ngc_execute
07842    rs274ngc_read
07843 
07844 This reads a line of RS274 code from a command string or a file into
07845 the line array. If the string is not null, the file is ignored.
07846 
07847 If the end of file is reached, that means that the file has run out
07848 without a stopping command having been given (a stopping command
07849 causes other functions to close the file), so RS274NGC_ERROR is returned.
07850 
07851 This then calls close_and_downcase to remove tabs and spaces from
07852 everything on the line that is not part of a comment. Any comment is
07853 left as is.
07854 
07855 The length is set to zero if any of the following occur:
07856 1. The line now starts with a slash, and block_delete is on.
07857 2. The line now starts with a slash, but the second character is NULL.
07858 3. The first character is NULL.
07859 Otherwise, length is set to the length of the line.
07860 
07861 An input line is blank if the first character is NULL or it consists
07862 entirely of tabs and spaces and, possibly, a newline before the first
07863 NULL.
07864 
07865 If there is newline character at the end of the line, it is replaced
07866 in close_and_downcase with a NULL character.
07867 
07868 The rule used here (for what to do with a line starting with a slash)
07869 is: if block delete is on, the line is read but not executed; if block
07870 delete is off, ignore the slash and process the line normally. A slash
07871 anywhere else on a line, except inside a comment, is illegal and will
07872 cause an error.
07873 
07874 Block delete is discussed in [NCMS, page 3] but the discussion makes no
07875 sense.
07876 
07877 */
07878 
07879 int read_text(         /* ARGUMENT VALUES                             */
07880  const char * command, /* a string which may have input text, or null */
07881  FILE * inport,        /* a file pointer for an input file, or null   */
07882  char * raw_line,      /* array to write raw input line into          */
07883  char * line,          /* array for input line to be processed in     */
07884  int * length,         /* a pointer an integer to be set              */
07885  ON_OFF block_delete)  /* whether block_delete switch is on or off    */
07886 {
07887   static char name[] SET_TO "read_text";
07888   char * returned_value;
07889   int len;
07890 
07891   if (command IS NULL)
07892     {
07893       returned_value SET_TO fgets(raw_line, INTERP_TEXT_SIZE, inport);
07894       // knock off CR, LF
07895       len = strlen(raw_line) - 1; // set len to index of last char
07896       while (len >= 0)
07897         {
07898           if (isspace(raw_line[len]))
07899             {
07900               raw_line[len] = 0;
07901               len--;
07902             }
07903           else
07904             {
07905               break;
07906             }
07907         }
07908       if (returned_value IS NULL)
07909         return RS274NGC_ENDFILE;
07910       else
07911         strcpy(line, raw_line);
07912     }
07913   else
07914     {
07915       strcpy(raw_line, command);
07916       strcpy(line, command);
07917     }
07918 
07919   if (close_and_downcase(line) IS RS274NGC_ERROR)
07920     ERROR_MACRO_PASS(name);
07921 
07922   if (((line[0] IS '/') AND ((block_delete IS ON) OR (line[1] IS 0))) OR
07923       (line[0] IS 0))
07924     *length SET_TO 0;
07925   else
07926     *length SET_TO strlen(line);
07927 
07928   return RS274NGC_OK;
07929 }
07930 
07931 /****************************************************************************/
07932 
07933 /* set_probe_data
07934 
07935 Returned Value: int (RS274NGC_OK)
07936 
07937 Side effects:
07938    System parameters may be reset.
07939 
07940 Called by:
07941    rs274ngc_execute
07942    rs274ngc_read
07943 
07944 */
07945 
07946 int set_probe_data(        /* ARGUMENT VALUES             */
07947  setup_pointer settings)   /* pointer to machine settings */
07948 {
07949   static char name[] SET_TO "set_probe_data";
07950   CANON_POSITION position;
07951   CANON_POSITION probe_position;
07952 
07953   position SET_TO GET_EXTERNAL_POSITION();
07954   settings->current_x SET_TO position.x;
07955   settings->current_y SET_TO position.y;
07956   settings->current_z SET_TO position.z;
07957   probe_position SET_TO GET_EXTERNAL_PROBE_POSITION();
07958   settings->parameters[5061] SET_TO probe_position.x;
07959   settings->parameters[5062] SET_TO probe_position.y;
07960   settings->parameters[5063] SET_TO probe_position.z;
07961   settings->parameters[5067] SET_TO GET_EXTERNAL_PROBE_VALUE();
07962   return RS274NGC_OK;
07963 }
07964 
07965 /****************************************************************************/
07966 /****************************************************************************/
07967 
07968 /*
07969 
07970 The functions defined below this point in this file comprise an
07971 interface between the rs274 interpreter kernel and the rest of the
07972 EMC software system.
07973 
07974 The interface also includes ten data items, as listed immediately below.
07975 These are passed by reference only to the interpreter kernel. In the
07976 integrated EMC system, none of these is known outside this file. In
07977 the stand-alone version seven of them are used in the driver source code
07978 file.
07979 
07980 */
07981 
07982 /***********************************************************************/
07983 
07984 /* rs274ngc_reset
07985 
07986 Returned Value: none
07987 
07988 Side Effects:
07989    This function resets the status, so that when the interpreter
07990    is through with a part program (error, end of file, etc.) certain
07991    status data is zero.
07992 
07993 Called By:
07994    rs274ngc_close
07995    rs274ngc_exit
07996    rs274ngc_init
07997    rs274ngc_read
07998 
07999 */
08000 
08001 int rs274ngc_reset()
08002 {
08003   _textline SET_TO 0;
08004   _interpreter_filename[0] SET_TO 0;
08005   _interpreter_linetext[0] SET_TO 0;
08006   _interpreter_blocktext[0] SET_TO 0;
08007   _interpreter_fp SET_TO NULL;
08008   _interpreter_status SET_TO 0;
08009   _interpreter_length SET_TO 0;
08010 
08011   return 0;
08012 }
08013 
08014 /***********************************************************************/
08015 
08016 /* rs274ngc_command
08017 
08018 Returned Value: char * (a string with the last line of NC code read)
08019 
08020 Side Effects: (none)
08021 
08022 Called By:
08023    This not called by any other function in the interpreter source file.
08024    It is intended to be called externally.
08025    It is not used in the stand-alone interpreter.
08026 
08027 */
08028 
08029 const char * rs274ngc_command()
08030 {
08031   return _interpreter_linetext;
08032 }
08033 
08034 /***********************************************************************/
08035 
08036 /* rs274ngc_file
08037 
08038 Returned Value: char * (the name of the NC program file being interpreted)
08039 
08040 Side Effects: (none)
08041 
08042 Called By:
08043    This not called by any other function in the interpreter source file.
08044    It is intended to be called externally.
08045    It is not used in the stand-alone interpreter.
08046 
08047 */
08048 
08049 const char * rs274ngc_file()
08050 {
08051   return _interpreter_filename;
08052 }
08053 
08054 /***********************************************************************/
08055 
08056 /* rs274ngc_line
08057 
08058 Returned Value: int
08059 
08060 Side Effects: (none)
08061 
08062 Called By:
08063    This not called by any other function in the interpreter source file.
08064    It is intended to be called externally.
08065    It is not used in the stand-alone interpreter.
08066 
08067 This returns the line number assigned by the interpreter to the current
08068 line of NC code. This is not the "N" number which is usually at the
08069 beginning of a line of code. The _textline is initialized to 1 in
08070 rs274ngc_open and is incremented by one each time rs274ngc_read
08071 is called.
08072 
08073 */
08074 
08075 int rs274ngc_line()
08076 {
08077   return _textline;
08078 }
08079 
08080 /***********************************************************************/
08081 
08082 /* rs274ngc_close
08083 
08084 Returned Value: int (RS274NGC_OK)
08085 
08086 Side Effects:
08087    The NC-code file is closed if open.
08088 
08089 Called By:
08090    This not called by any other function in the interpreter source file.
08091    It is intended to be called externally.
08092    It is not used in the stand-alone interpreter.
08093 
08094 */
08095 
08096 int rs274ngc_close()
08097 {
08098   if (_interpreter_fp ISNT NULL)
08099   {
08100     fclose(_interpreter_fp);
08101     _interpreter_fp SET_TO NULL;
08102   }
08103 
08104   _interpreter_filename[0] SET_TO 0;
08105 
08106   rs274ngc_reset();
08107 
08108   return RS274NGC_OK;
08109 }
08110 
08111 /***********************************************************************/
08112 
08113 /* rs274ngc_execute
08114 
08115 Returned Value: int (RS274NGC_OK, RS274NGC_EXIT, RS274NGC_EXECUTE_FINISH,
08116                      or RS274NGC_ERROR)
08117    If any of the following errors occur, this returns RS274NGC_ERROR.
08118    1. The command argument is not null and any of the following happens
08119      A. The command string is too long.
08120      B. The command string cannot be handled correctly by read_text.
08121      C. The command string cannot be parsed correctly by read_line.
08122    2. execute_block returns RS274NGC_ERROR.
08123    3. The probe_flag is ON but the HME command queue is not empty.
08124 
08125    If execute_block returns RS274NGC_EXIT, this returns RS274NGC_EXIT.
08126    if execute_block returns RS274NGC_EXECUTE_FINISH this returns that.
08127    Otherwise, this returns RS274NGC_OK.
08128 
08129 Side Effects:
08130    Calls to canonical machining commands are made.
08131    The interpreter variables are changed.
08132    At the end of the program, the file is closed.
08133 
08134 Called By:
08135    This not called by any other function in the interpreter source file.
08136    It is intended to be called externally.
08137    It is called by interpret_from_file and interpret_from_keyboard
08138     in the driver.
08139 
08140 If the command argument is a non-null string, the string is assumed to
08141 be a line of NC code. It is parsed into the _interpreter_block,
08142 and the block is executed.
08143 
08144 If the command argument is null, it is assumed that the
08145 _interpreter_block already contains the information from a line of NC
08146 code and the block is executed.
08147 
08148 */
08149 
08150 int rs274ngc_execute(   /* ARGUMENT VALUES */
08151  const char * command)  /* string: line of NC code to execute (may be null) */
08152 {
08153   static char name[] SET_TO "rs274ngc_execute";
08154 
08155   if (_interpreter_settings.probe_flag IS ON)
08156     {
08157       if (IS_EXTERNAL_QUEUE_EMPTY() IS 0)
08158         {
08159           /* rs274ngc_reset(); */
08160           ERR_MACRO(_interpreter_linetext, name, "Queue is not empty after probing");
08161         }
08162       set_probe_data(&_interpreter_settings);
08163       _interpreter_settings.probe_flag SET_TO OFF;
08164     }
08165 
08166   if (command ISNT NULL)
08167     {
08168       if(strlen(command) > (INTERP_TEXT_SIZE -1))
08169         ERR_MACRO(_interpreter_linetext, name, "Command too long");
08170       else if (read_text (command, NULL, _interpreter_linetext,
08171                           _interpreter_blocktext, &_interpreter_length,
08172                           _interpreter_settings.block_delete) ISNT RS274NGC_OK)
08173         ERR_MACRO_PASS(name);
08174       else if (_interpreter_length IS 0) /* blank or block-deleted command */
08175         return RS274NGC_OK;
08176       else if (read_line(_interpreter_blocktext, &_interpreter_block,
08177                          &_interpreter_settings) IS RS274NGC_ERROR)
08178         ERR_MACRO_PASS(name);
08179       _textline++;
08180     }
08181 
08182   if (_interpreter_length IS 0) /* blank or block deleted line from file */
08183     {
08184       write_g_codes((block *) NULL, &_interpreter_settings, _textline,
08185                     &_interpreter_active_g_codes[0]);
08186       write_m_codes((block *) NULL, &_interpreter_settings, _textline,
08187                     &_interpreter_active_m_codes[0]);
08188       write_settings((block *) NULL, &_interpreter_settings, _textline,
08189                     &_interpreter_active_settings[0]);
08190 
08191       return RS274NGC_OK;
08192     }
08193 
08194   _interpreter_status SET_TO
08195     execute_block (&_interpreter_block, &_interpreter_settings, _textline);
08196 
08197   // write most recent G codes
08198   write_g_codes(&_interpreter_block, &_interpreter_settings, _textline,
08199                 &_interpreter_active_g_codes[0]);
08200   write_m_codes(&_interpreter_block, &_interpreter_settings, _textline,
08201                 &_interpreter_active_m_codes[0]);
08202   write_settings(&_interpreter_block, &_interpreter_settings, _textline,
08203                  &_interpreter_active_settings[0]);
08204 
08205   if (_interpreter_status IS RS274NGC_EXIT) /* program over */
08206     {
08207       if (_interpreter_fp ISNT NULL)
08208         {
08209           fclose(_interpreter_fp);
08210           _interpreter_fp SET_TO NULL;
08211           _textline SET_TO 0;
08212           _interpreter_linetext[0] SET_TO 0;
08213         }
08214       return RS274NGC_EXIT;
08215     }
08216   else if (_interpreter_status IS RS274NGC_ERROR)
08217     ERR_MACRO_PASS(name);
08218   else if (_interpreter_status IS RS274NGC_EXECUTE_FINISH)
08219     return RS274NGC_EXECUTE_FINISH;
08220   else /* satisfactory execution of a line */
08221     return RS274NGC_OK;
08222 }
08223 
08224 /***********************************************************************/
08225 
08226 /* rs274ngc_exit
08227 
08228 Returned Value: int (RS274NGC_OK)
08229 
08230 Side Effects:
08231    Some parts of the world model are reset.
08232 
08233 Called By:
08234    This not called by any other function in the interpreter source file.
08235    It is intended to be called externally.
08236    It is not used in the stand-alone interpreter.
08237 
08238 */
08239 
08240 int rs274ngc_exit()
08241 {
08242   rs274ngc_save_parameters(RS274NGC_PARAMETER_FILE,
08243                            _interpreter_settings.parameters);
08244 
08245   rs274ngc_reset();
08246 
08247   return RS274NGC_OK;
08248 }
08249 
08250 /***********************************************************************/
08251 
08252 /* rs274ngc_synch
08253 
08254 Returned Value: int (RS274NGC_OK)
08255 
08256 Side Effects:
08257    sets the value of _interpreter_settings.current_x/y/z to what's
08258    returned by GET_EXTERNAL_POSITION().
08259 
08260 Called By:
08261    This not called by any other function in the interpreter source file.
08262    It is intended to be called externally.
08263    It is not used in the stand-alone interpreter.
08264 
08265    */
08266 
08267 int rs274ngc_synch()
08268 {
08269   CANON_POSITION current_point;
08270 
08271   _interpreter_settings.current_slot SET_TO GET_EXTERNAL_TOOL();
08272 
08273   rs274ngc_load_tool_table();
08274 
08275   _interpreter_settings.control_mode SET_TO GET_MOTION_CONTROL_MODE();
08276   current_point SET_TO GET_EXTERNAL_POSITION();
08277   _interpreter_settings.current_x SET_TO current_point.x;
08278   _interpreter_settings.current_y SET_TO current_point.y;
08279   _interpreter_settings.current_z SET_TO current_point.z;
08280 
08281   _interpreter_settings.feed_rate SET_TO GET_EXTERNAL_FEED_RATE();
08282 
08283   _interpreter_settings.flood SET_TO (GET_EXTERNAL_FLOOD() ISNT 0) ? ON : OFF;
08284 
08285   _interpreter_settings.mist SET_TO (GET_EXTERNAL_MIST() ISNT 0) ? ON : OFF;
08286 
08287   _interpreter_settings.selected_tool_slot SET_TO GET_EXTERNAL_POCKET();
08288 
08289   _interpreter_settings.speed SET_TO GET_EXTERNAL_SPEED();
08290 
08291   _interpreter_settings.spindle_turning SET_TO GET_EXTERNAL_SPINDLE();
08292 
08293   _interpreter_settings.traverse_rate SET_TO GET_EXTERNAL_TRAVERSE_RATE();
08294 
08295   return RS274NGC_OK;
08296 }
08297 
08298 /***********************************************************************/
08299 
08300 /* rs274ngc_init
08301 
08302 Returned Value: int (RS274NGC_OK)
08303 
08304 Side Effects:
08305    Many values in the _interpreter_settings structure are reset.
08306    Many interpreter variables are reset.
08307    A SET_FEED_REFERENCE canonical command call is made.
08308    An INIT_CANON call is made.
08309    write_g_codes, write_m_codes, and write_settings are called.
08310    reset_interp_wm is called.
08311 
08312 
08313 Called By:
08314    This not called by any other function in the interpreter source file.
08315    It is intended to be called externally.
08316    It is called by interpret_from_file and interpret_from_keyboard
08317      in the interpreter driver.
08318 
08319 Currently we are running only in CANON_XYZ feed_reference mode.  There
08320 is no command regarding feed_reference in the rs274 language (we
08321 should try to get one added). The initialization routine, therefore,
08322 always calls SET_FEED_REFERENCE(CANON_XYZ).
08323 
08324 Length units are set early since the default is inches, and calling
08325 USE_LENGTH_UNITS before extracting data from the rest of the system
08326 will get the rest of the system to use inches.
08327 
08328 The early current position settings are used by the stand-alone interpreter.
08329 
08330 */
08331 
08332 int rs274ngc_init()
08333 {
08334   CANON_VECTOR axis_offset;
08335   CANON_VECTOR origin_point;
08336   int k;
08337 
08338   rs274ngc_reset();
08339 
08340   INIT_CANON();
08341 
08342   _interpreter_settings.length_units SET_TO CANON_UNITS_INCHES;
08343   USE_LENGTH_UNITS(_interpreter_settings.length_units);
08344 
08345   for (k SET_TO 5161; k < 5387; k++)
08346     {
08347       _interpreter_settings.parameters[k] SET_TO 0.0;
08348     }
08349   rs274ngc_restore_parameters(
08350 #ifdef STAND_ALONE_INTERP
08351 DEFAULT_RS274NGC_PARAMETER_FILE,
08352 #else
08353 RS274NGC_PARAMETER_FILE,
08354 #endif
08355                               _interpreter_settings.parameters);
08356 
08357   // Get axis offsets from parameters
08358   axis_offset.x SET_TO _interpreter_settings.parameters[5211];
08359   axis_offset.y SET_TO _interpreter_settings.parameters[5212];
08360   axis_offset.z SET_TO _interpreter_settings.parameters[5213];
08361 
08362   // Get origin from parameters for G54 default coordinate system
08363   origin_point.x SET_TO _interpreter_settings.parameters[5221];
08364   origin_point.y SET_TO _interpreter_settings.parameters[5222];
08365   origin_point.z SET_TO _interpreter_settings.parameters[5223];
08366 
08367   // Set the origin offsets
08368   SET_ORIGIN_OFFSETS(origin_point.x + axis_offset.x,
08369                      origin_point.y + axis_offset.y,
08370                      origin_point.z + axis_offset.z);
08371 
08372   SET_FEED_REFERENCE(CANON_XYZ);
08373 
08374   _textline SET_TO 0;
08375   _interpreter_settings.axis_offset_x SET_TO 0.0;
08376   _interpreter_settings.axis_offset_y SET_TO 0.0;
08377   _interpreter_settings.axis_offset_z SET_TO 0.0;
08378   _interpreter_settings.block_delete SET_TO OFF;
08379 /*_interpreter_settings.control_mode set in rs274ngc_synch */
08380 /*_interpreter_settings.current_slot set in rs274ngc_synch */
08381   _interpreter_settings.current_x SET_TO 0.0; /* reset by rs274ngc_synch */
08382   _interpreter_settings.current_y SET_TO 0.0; /* reset by rs274ngc_synch */
08383   _interpreter_settings.current_z SET_TO 0.0; /* reset by rs274ngc_synch */
08384   _interpreter_settings.cutter_radius_compensation SET_TO OFF;
08385 /* cycle values do not need initialization */
08386   _interpreter_settings.distance_mode SET_TO MODE_ABSOLUTE;
08387   _interpreter_settings.feed_mode SET_TO UNITS_PER_MINUTE;
08388   _interpreter_settings.feed_override SET_TO ON;
08389 /*_interpreter_settings.feed_rate set in rs274ngc_synch */
08390 /*_interpreter_settings.flood set in rs274ngc_synch */
08391   _interpreter_settings.length_offset_index SET_TO 1;
08392 /*_interpreter_settings.length_units set above */
08393 /*_interpreter_settings.mist set in rs274ngc_synch */
08394   _interpreter_settings.motion_mode SET_TO G_1;
08395   _interpreter_settings.origin_ngc SET_TO 1;
08396   _interpreter_settings.axis_offset_x SET_TO axis_offset.x;
08397   _interpreter_settings.axis_offset_y SET_TO axis_offset.y;
08398   _interpreter_settings.axis_offset_z SET_TO axis_offset.z;
08399   _interpreter_settings.origin_offset_x SET_TO origin_point.x;
08400   _interpreter_settings.origin_offset_y SET_TO origin_point.y;
08401   _interpreter_settings.origin_offset_z SET_TO origin_point.z;
08402 /*_interpreter_settings.parameters set above */
08403   _interpreter_settings.plane SET_TO CANON_PLANE_XY;
08404   SELECT_PLANE(_interpreter_settings.plane);
08405   _interpreter_settings.probe_flag SET_TO OFF;
08406   _interpreter_settings.program_x SET_TO UNKNOWN; /* for cutter comp */
08407   _interpreter_settings.program_y SET_TO UNKNOWN; /* for cutter comp */
08408   _interpreter_settings.retract_mode SET_TO R_PLANE;
08409 /*_interpreter_settings.selected_tool_slot set in rs274ngc_synch */
08410 /*_interpreter_settings.speed set in rs274ngc_synch */
08411   _interpreter_settings.speed_feed_mode SET_TO CANON_INDEPENDENT;
08412   _interpreter_settings.speed_override SET_TO ON;
08413   _interpreter_settings.tool_length_offset SET_TO 0.0;
08414 /*_interpreter_settings.tool_table set in rs274ngc_synch */
08415   _interpreter_settings.tool_table_index SET_TO 1;
08416 /*_interpreter_settings.traverse_rate set in rs274ngc_synch */
08417 
08418   write_g_codes((block_pointer)NULL, &_interpreter_settings,
08419                 _textline, &_interpreter_active_g_codes[0]);
08420   write_m_codes((block_pointer)NULL, &_interpreter_settings,
08421                 _textline, &_interpreter_active_m_codes[0]);
08422   write_settings((block_pointer)NULL, &_interpreter_settings,
08423                 _textline, &_interpreter_active_settings[0]);
08424 
08425   // Synch rest of settings to external world
08426   rs274ngc_synch();
08427 
08428   return RS274NGC_OK;
08429 }
08430 
08431 /***********************************************************************/
08432 
08433 /* rs274ngc_open
08434 
08435 Returned Value: int (RS274NGC_OK or RS274NGC_ERROR)
08436    If any of the following errors occur, this returns RS274NGC_ERROR.
08437    Otherwise it returns RS274NGC_OK.
08438    1. The file cannot be opened.
08439 
08440 Side Effects:
08441    The file is opened for reading and _interpreter_fp is set.
08442    The input program line counter, _textline, is set.
08443    _interpreter_filename is set.
08444 
08445 Called By:
08446    This not called by any other function in the interpreter source file.
08447    It is intended to be called externally.
08448    It is called by interpret_from_file in the interpreter driver.
08449 
08450 The manual [NCMS, page 3] discusses the use of the "%" character at the
08451 beginning of a "tape". It is not clear whether it is intended that
08452 every NC-code file should begin with that character. Here, we are not
08453 requiring it. _textline is set to 0.
08454 
08455 */
08456 
08457 int rs274ngc_open(     /* ARGUMENT VALUES                               */
08458  const char *filename) /* string: the name of the input NC-program file */
08459 {
08460   static char name[] SET_TO "rs274ngc_open";
08461 
08462   _interpreter_fp SET_TO fopen(filename, "r");
08463   if (_interpreter_fp IS NULL)
08464     ERR_MACRO(_interpreter_linetext, name, "Unable to open file");
08465 
08466   _textline SET_TO 0;
08467 
08468   strcpy(_interpreter_filename, filename);
08469 
08470   return RS274NGC_OK;
08471 }
08472 
08473 /***********************************************************************/
08474 
08475 /* rs274ngc_read
08476 
08477 Returned Value: int (RS274NGC_OK, RS274NGC_EXIT, RS274NGC_ENDFILE,
08478                      or RS274NGC_ERROR)
08479    If _interpreter_fp is NULL and _interpreter_status is RS274NGC_EXIT,
08480    this returns RS274NGC_EXIT.
08481    If the end of the file is found, this returns RS274NGC_ENDFILE.
08482    Otherwise, if any of the following errors occur, this
08483    returns RS274NGC_ERROR. Otherwise, it returns RS274NGC_OK.
08484    1. The _interpreter_fp file pointer is NULL.
08485    2. read_text (which gets a line of NC code from file)
08486       returns RS274NGC_ERROR.
08487    3. read_line (which parses the line) returns RS274NGC_ERROR.
08488    4. The end of the file has been reached.
08489    5. The probe_flag is ON but the HME command queue is not empty.
08490 
08491 Side Effects:
08492    If there is an error, the file is closed.
08493    _textline is incremented.
08494    The _interpreter_block is filled with data.
08495 
08496 Called By:
08497    This not called by any other function in the interpreter source file.
08498    It is intended to be called externally.
08499    It is called by interpret_from_file in the driver.
08500 
08501 This reads a line of NC-code from a file. The _interpreter_length will
08502 be set by read_text. This will be zero if the line is blank or block
08503 deleted. If the length is not zero, this parses the line into the
08504 _interpreter_block.
08505 
08506 Before reading a line of NC-code, this checks to see it the probe_flag
08507 in the settings in ON. If so, this means the STRAIGHT_PROBE function
08508 was called last, so data about the current position and the probe
08509 position is updated, after checking to be sure the command queue of
08510 the HME is empty (so that the probing is sure to have been executed).
08511 
08512 This is not closing the file when the end of the file has been
08513 reached. When this function returns RS274NGC_ENDFILE, rs274ngc_close
08514 should be called.
08515 
08516 */
08517 
08518 int rs274ngc_read()
08519 {
08520   static char name[] SET_TO "rs274ngc_read";
08521   int status;
08522 
08523   if (_interpreter_settings.probe_flag IS ON)
08524     {
08525       if (IS_EXTERNAL_QUEUE_EMPTY() IS 0)
08526         {
08527           /* rs274ngc_reset(); */
08528           ERR_MACRO(_interpreter_linetext, name, "Queue is not empty after probing");
08529         }
08530       set_probe_data(&_interpreter_settings);
08531       _interpreter_settings.probe_flag SET_TO OFF;
08532     }
08533 
08534   if (_interpreter_fp IS NULL)
08535     {
08536       if (_interpreter_status IS RS274NGC_EXIT)
08537         {   /* set in call to rs274ngc_execute */
08538           /* rs274ngc_reset(); */
08539           return RS274NGC_EXIT;
08540         }
08541       else
08542         {
08543           /* rs274ngc_reset(); */
08544           ERR_MACRO_PASS(name);
08545         }
08546     }
08547 
08548   /* read the line */
08549   _textline++;
08550   status SET_TO read_text (NULL, _interpreter_fp, _interpreter_linetext,
08551                            _interpreter_blocktext, &_interpreter_length,
08552                            _interpreter_settings.block_delete);
08553   if ((status IS RS274NGC_ENDFILE) AND (_interpreter_block.m_modes[4] IS 1))
08554     return RS274NGC_ENDFILE;  /* m1 appeared on the preceding line */
08555   else if (status IS RS274NGC_ENDFILE)
08556     {
08557       fclose(_interpreter_fp);
08558       _interpreter_fp SET_TO NULL;
08559       /* rs274ngc_reset(); */
08560       ERR_MACRO(_interpreter_linetext, name, "File ended with no stopping command given");
08561     }
08562   else if (status IS RS274NGC_ERROR)
08563     {
08564       /* rs274ngc_reset(); */
08565       ERR_MACRO_PASS(name);
08566     }
08567   if (_interpreter_length IS 0)
08568     return RS274NGC_OK;
08569 
08570   /* parse the block */
08571   else if (read_line(_interpreter_blocktext, &_interpreter_block,
08572                      &_interpreter_settings) IS RS274NGC_ERROR)
08573     {
08574       /* rs274ngc_reset(); */
08575       ERR_MACRO_PASS(name);
08576     }
08577   else
08578     return RS274NGC_OK;
08579 }
08580 
08581 /***********************************************************************/
08582 
08583 /* rs274ngc_load_tool_table()
08584 
08585 Returned Value: RS274NGC_OK
08586 
08587 Side Effects:
08588    _interpreter_settings.tool_table[] is modified.
08589 
08590 Called By:
08591    rs274ngc_init().
08592    It is also intended to be called externally, when the tool table
08593    is changed.
08594 
08595 This function calls the canonical interface function GET_EXTERNAL_TOOL_TABLE
08596 to load the whole tool table into the _interpreter_settings.
08597 */
08598 
08599 int rs274ngc_load_tool_table()
08600 {
08601   int t;
08602 
08603   for (t SET_TO 0; t <= CANON_TOOL_MAX; t++)
08604     {
08605       _interpreter_settings.tool_table[t] SET_TO GET_EXTERNAL_TOOL_TABLE(t);
08606     }
08607 
08608   return 0;
08609 }
08610 
08611 /***********************************************************************/
08612 
08613 /* rs274ngc_restore_parameters()
08614 
08615 Returned Value: RS274NGC_OK, RS274NGC_ERROR
08616 
08617 Side Effects:
08618    parameters[] array in settings structure passed (e.g.,
08619    _interpreter_settings.parameters[]) is modified.
08620 
08621 Called By:
08622    rs274ngc_init().
08623 
08624 This function restores the parameters from a file containing lines of
08625 the form:
08626 
08627 <variable number> <value>
08628 
08629 e.g.,
08630 
08631 5161 10.456
08632 */
08633 int rs274ngc_restore_parameters(const char *filename, double parameters[])
08634 {
08635   static char name[] SET_TO "rs274ngc_restore_parameters";
08636   FILE *infp = NULL;
08637   char line[256];
08638   int variable;
08639   double value;
08640 
08641   // open original for reading
08642   if (NULL == (infp = fopen(filename, "r")))
08643     {
08644       ERR_MACRO(_interpreter_linetext, name, "Cannot open file");
08645       return -1;
08646     }
08647 
08648   while (!feof(infp))
08649     {
08650       if (NULL == fgets(line, 256, infp))
08651         {
08652           break;
08653         }
08654 
08655       // try for a variable-value match
08656       if (2 == sscanf(line, "%d %lf", &variable, &value))
08657         {
08658           // write it into variable array
08659           if (variable <= 0 || variable >= RS274NGC_MAX_PARAMETERS)
08660             {
08661               ERR_MACRO(_interpreter_linetext, name, "Parameter number out of range");
08662             }
08663           else
08664             {
08665               parameters[variable] = value;
08666             }
08667         }
08668     }
08669 
08670   fclose(infp);
08671 
08672   return 0;
08673 }
08674 
08675 /***********************************************************************/
08676 
08677 /* rs274ngc_save_parameters()
08678 
08679 Returned Value: RS274NGC_OK, RS274NGC_ERROR
08680 
08681 Side Effects:
08682    A file containing variable-value assignments is updated with the
08683    current value of the variables.
08684 
08685 Called By:
08686    rs274ngc_exit().
08687 
08688 This function saves parameters to a file containing lines of
08689 the form:
08690 
08691 <variable number> <value>
08692 
08693 e.g.,
08694 
08695 5161 10.456
08696 */
08697 int rs274ngc_save_parameters(const char *filename, const double parameters[])
08698 {
08699   static char name[] SET_TO "rs274ngc_save_parameters";
08700   FILE *infp = NULL;
08701   FILE *outfp = NULL;
08702   char line[256];
08703   int variable;
08704   double value;
08705 
08706   // rename as .bak
08707   strcpy(line, filename);
08708   strcat(line, RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX);
08709   if (0 != rename(filename, line))
08710     {
08711       ERR_MACRO(_interpreter_linetext, name, "Cannot create backup file");
08712       return -1;
08713     }
08714 
08715   // open backup for reading
08716   if (NULL == (infp = fopen(line, "r")))
08717     {
08718       ERR_MACRO(_interpreter_linetext, name, "Cannot open backup file");
08719       return -1;
08720     }
08721 
08722   // open original for writing
08723   if (NULL == (outfp = fopen(filename, "w")))
08724     {
08725       ERR_MACRO(_interpreter_linetext, name, "Cannot open variable file");
08726       return -1;
08727     }
08728 
08729   while (!feof(infp))
08730     {
08731       if (NULL == fgets(line, 256, infp))
08732         {
08733           break;
08734         }
08735 
08736       // try for a variable-value match
08737       if (2 == sscanf(line, "%d %f", &variable, &value))
08738         {
08739           // overwrite with internal variable
08740           if (variable <= 0 || variable >= RS274NGC_MAX_PARAMETERS)
08741             {
08742               ERR_MACRO(_interpreter_linetext, name, "Parameter number out of range");
08743             }
08744           else
08745             {
08746               sprintf(line, "%d\t%f\n", variable, parameters[variable]);
08747             }
08748         }
08749 
08750       // write it out
08751       fputs(line, outfp);
08752     }
08753 
08754   fclose(infp);
08755   fclose(outfp);
08756 
08757   return 0;
08758 }
08759 
08760 /***********************************************************************/
08761 
08762 /* rs274ngc_ini_load()
08763 
08764 Returned Value: RS274NGC_OK, RS274NGC_ERROR
08765 
08766 Side Effects:
08767    An INI file containing values for global variables is used to
08768    update the globals
08769 
08770 Called By:
08771    rs274ngc_init()
08772 
08773 The file looks like this:
08774 
08775 [RS274NGC]
08776 VARIABLE_FILE = rs274ngc.var
08777 
08778 */
08779 
08780 #ifdef STAND_ALONE_INTERP
08781 
08782 int rs274ngc_ini_load(const char *filename)
08783 {
08784   return RS274NGC_OK;
08785 }
08786 
08787 #else
08788 
08789 #include "inifile.h"            // INIFILE
08790 
08791 int rs274ngc_ini_load(const char *filename)
08792 {
08793   INIFILE inifile;
08794   const char *inistring;
08795 
08796   // open it
08797   if (-1 == inifile.open(filename))
08798     {
08799       return -1;
08800     }
08801 
08802   if (NULL != (inistring = inifile.find("PARAMETER_FILE", "RS274NGC")))
08803     {
08804       // found it
08805       strcpy(RS274NGC_PARAMETER_FILE, inistring);
08806     }
08807   else
08808     {
08809       // not found, leave RS274NGC_PARAMETER_FILE alone
08810     }
08811 
08812   // close it
08813   inifile.close();
08814 
08815   return RS274NGC_OK;
08816 }
08817 
08818 #endif
08819 
08820 /***********************************************************************/
08821 
08822 /* rs274ngc_active_g_codes
08823 
08824 Returned Value: RS274NGC_OK
08825 
08826 Side Effects: copies active G codes into arg array
08827 
08828 Called By: external programs
08829 
08830 */
08831 
08832 int rs274ngc_active_g_codes(int *codes)
08833 {
08834   int t;
08835 
08836   for (t = 0; t < RS274NGC_ACTIVE_G_CODES; t++)
08837     {
08838       codes[t] = _interpreter_active_g_codes[t];
08839     }
08840 
08841   return RS274NGC_OK;
08842 }
08843 
08844 /***********************************************************************/
08845 
08846 /* rs274ngc_active_m_codes
08847 
08848 Returned Value: RS274NGC_OK
08849 
08850 Side Effects: copies active M codes into arg array
08851 
08852 Called By: external programs
08853 
08854 */
08855 
08856 int rs274ngc_active_m_codes(int *codes)
08857 {
08858   int t;
08859 
08860   for (t = 0; t < RS274NGC_ACTIVE_M_CODES; t++)
08861     {
08862       codes[t] = _interpreter_active_m_codes[t];
08863     }
08864 
08865   return RS274NGC_OK;
08866 }
08867 
08868 /***********************************************************************/
08869 
08870 /* rs274ngc_active_settings
08871 
08872 Returned Value: RS274NGC_OK
08873 
08874 Side Effects: copies active F, S settings into array
08875 
08876 Called By: external programs
08877 
08878 */
08879 
08880 int rs274ngc_active_settings(double *settings)
08881 {
08882   int t;
08883 
08884   for (t = 0; t < RS274NGC_ACTIVE_SETTINGS; t++)
08885     {
08886       settings[t] = _interpreter_active_settings[t];
08887     }
08888 
08889   return RS274NGC_OK;
08890 }
08891 
08892 /***********************************************************************/

Generated on Sun Dec 2 15:27:42 2001 for EMC by doxygen1.2.11.1 written by Dimitri van Heesch, © 1997-2001