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

emcsh.cc

Go to the documentation of this file.
00001 /*
00002   emcsh.cc
00003 
00004   Extended-Tcl-based EMC automatic test interface
00005 
00006   Modification history:
00007 
00008   17-Jul-2001 WPS add optional default parameter to emc_ini.
00009   27-July-2001 PC added a crude routine for emc_pendant with an idea of
00010   using a butchered serial mouse as a remote control to EMC.
00011   31-May-2001  FMP added emc_debug command that shows/sets the debug level
00012   (EMC_DEBUG global) both here and in the EMC itself. I thought about doing
00013   them separately, but it seemed too confusing for the user ("which one did
00014   I set? How come my debug info isn't being displayed?").
00015   25-May-2001  FMP added emc_joint_type,units, which will enable GUI to
00016   show joint units appropriately
00017   24-May-2001  FMP added unit handling, with linearUnitConversion,
00018   convertLinearUnits, and angular counterparts. This meant running all
00019   the emcStatus position, length information through convertLinearUnits,
00020   which is visible at most calls to Tcl_NewDoubleObj().
00021   23-May-2001  FMP added emc_program_units
00022   1-May-2001 WPS added emc_joint_pos, emc_teleop_enable and 
00023   emc_kinematics_type
00024   15-Sep-2000  FMP added EMC_INIFILE Tcl var. This is so that Tcl/Tk
00025   scripts can reference the actual ini file name, for passing to subscripts.
00026   16-Aug-2000  FMP added axis alter; changed return value of functions
00027   from TCL_ERROR to TCL_OK for errors sending commands to EMC, since these
00028   are not syntax errors and shouldn't pop up the Tcl error box. These push
00029   string notices on errors instead, nothing on success.
00030   11-Aug-2000  FMP fixed bug in args processing of emc_axis_load_comp
00031   10-Aug-2000  FMP added EMC_AXIS_LOAD_COMP stuff, for loading compensation
00032   table information
00033   4-Aug-2000  FMP fixed string mismatch in a probe function name and its
00034   error diagnostic; added operator_display,text so that messages like
00035   "change tool" don't have to be called an error.
00036   31-Jul-2000 WPS added probing stuff
00037   8-Jun-2000 WPS modified the code to work with gcc 2.95 without the
00038   -fpermissive option. The main problem was lines where the ? :
00039   expression was used to select a string to pass to Tcl_SetResult. ie
00040   Tcl_SetResult(, val == 1 ? "s1":"s2"). Unfortunately Tcl_SetResult is
00041   declared as taking a char * instead of const char * so passing a
00042   string constant causes an error. Apparently there is some sort of
00043   exception where if the string constant is passed directly, it will
00044   still allow it but not if it is the result of some more complicated
00045   expression.
00046   31-May-2000  FMP changed return value of emc_ini from -1 to TCL_OK, when
00047   EMC_INIFILE couldn't be opened-- this is not a Tcl error. The return string
00048   is left unset.
00049   19-May-2000  FMP added lube functions
00050   12-Apr-2000 WPS traded the ifndef WIN32 for ifdef HAVE_TCL_EXTEND since
00051   I don't have the tcl extensions on the SUN either.
00052   12-Apr-2000 WPS fixed iniLoad to explicitly declare the return type.
00053   17-Mar-2000  FMP pulled NML connect code out of main() and put it in
00054   tryNml()
00055   16-Mar-2000  FMP took out error messages while trying to connect to NML,
00056   before it actually timed out; added "int" and "round" for WIN32
00057   3-Mar-2000  FMP added emc_joint_fault; estop_in
00058   25-Feb-2000  FMP added deadband to emc_axis_gains
00059   24-Jan-2000  FMP added emc_axis_enable
00060   22-Jan-2000  FMP took out trapping of ^C with the intr flag, which prevented
00061   signaling a termination, and replaced with call to thisQuit. thisQuit calls
00062   Tcl_Exit() and exit(). Now, ^C at the emcsh interactive prompt will quit,
00063   rather than aborting any NML read that may be in process. Added pos_voltage
00064   logging. Added emc_axis_set_output.
00065   18-Jan-2000  FMP fixed bug in which relative actual position was set
00066   to relative commanded position; note the nifty new Y2K-compliant
00067   date on the comment.
00068   12-Nov-1999  FMP added emc_set_tool_offset <tool> <len> <diam>;
00069   emc_override_limit; passed return value of emcCommandWaitReceived/Done
00070   for those commands for which it was omitted; added emc_override_limit
00071   10-Nov-1999  FMP added #ifndef WIN32 around extended Tcl, which Windows
00072   doesn't have; got rid of etime() calls using delta clocks; made emc_time
00073   return 0 when we have the gettimeofday() bug
00074   9-Nov-1999  FMP added emc_program_status
00075   8-Nov-1999  FMP added emc_time; log functions; emc_wait
00076   2-Nov-1999  FMP added "all" to emc_axis_gains, to set them all in one call
00077   1-Nov-1999  FMP cast double to int in feed override, fixing warning; added
00078   command, serial number, and status for task, io, and motion
00079   30-Oct-1999  FMP added emc_program_codes; emc_load_tool_table
00080   12-Oct-1999  FMP added run, pause, resume, step; pos offset
00081   11-Oct-1999  FMP added relative position functions; tool and tool offset;
00082   joint limit and home
00083   10-Oct-1999  FMP added jogging
00084   8-Oct-1999  FMP changed name to emcsh.cc; added delayed timeout to
00085   connect to NML buffers
00086   22-Sep-1999  FMP added emc_update and related vars; emc_ini
00087   17-Sep-1999  FMP added Tk support, so you can build GUIs with this, e.g,
00088   'autoemc gui.tcl -- -ini emc.ini'. Could call this emcwish.
00089   16-Sep-1999  FMP converted to object API
00090   22-Mar-1999  FMP added line to emc_task_plan_run_msg
00091   26-Feb-1999  FMP added saving and restoring of emc serial number via
00092   EMC_NULL command
00093   25-Feb-1999  FMP took out timed wait from emcCommandWaitDone() since
00094   we don't know how long to wait; added more commands
00095   17-Feb-1999  FMP added EMC commands for state, mode, spindle, coolant,
00096   mdi, and program running
00097   1-Feb-1999  FMP created
00098   */
00099 
00100 #include <stdio.h>
00101 #include <string.h>
00102 #include <stdlib.h>
00103 #include <signal.h>
00104 #include "tcl.h"
00105 #ifdef HAVE_TCL_EXTEND
00106 #include "tclExtend.h"
00107 #endif
00108 #include "tk.h"
00109 
00110 #include "rcs.hh"               // etime()
00111 #include "posemath.h"           // PM_POSE, TO_RAD
00112 #include "emc.hh"               // EMC NML
00113 #include "canon.hh"             // CANON_UNITS, CANON_UNITS_INCHES,MM,CM
00114 #include "emcglb.h"             // EMC_NMLFILE, TRAJ_MAX_VELOCITY, etc.
00115 #include "emccfg.h"             // DEFAULT_TRAJ_MAX_VELOCITY
00116 #include "inifile.h"            // INIFILE
00117 #include "emcmotlog.h" // EMCLOG_TRIGGER_TYPE, EMCLOG_TRIGGER_VAR
00118 
00119 // ident tag
00120 #ifndef __GNUC__
00121 #ifndef __attribute__
00122 #define __attribute__(x)
00123 #endif
00124 #endif
00125 
00126 static char __attribute__((unused)) ident[] = "$Id: emcsh.cc,v 1.23 2001/09/27 18:10:42 paul_c Exp $";
00127 
00128 #ifdef SUN
00129 /*
00130   The following variable is a special hack that is needed in order for
00131   Sun shared libraries to be used for Tcl.
00132  */
00133 extern "C" int matherr();
00134 int *tclDummyMathPtr = (int *) matherr;
00135 #endif
00136 
00137 /*
00138   Using emcsh:
00139 
00140   emcsh {<filename>} {-- -ini <ini file>}
00141 
00142   With filename, it opens NML buffers to the EMC, runs the script, closes
00143   the buffers, and quits.
00144 
00145   With -- -ini <inifile>, uses inifile instead of emc.ini. Note that
00146   the two dashes prevents Tcl from looking at the remaining args, which
00147   would otherwise trigger a Tcl error that it doesn't understand what
00148   -ini means.
00149 
00150   Without filename, it runs interactively.
00151 
00152   The files (or manual input) are Tcl scripts, extended with EMC-specific
00153   commands. These commands are all prefixed with "emc_", which makes them
00154   somewhat inconvenient for typing but avoids name conflicts, e.g., open.
00155 
00156   Some commands take 0 or more arguments. 0 arguments means they return
00157   the associated value; the argument would be to set the value.
00158 
00159   Commands are sent to the EMC, and control resumes immediately. You can
00160   call a timed wait until the command got there, or a timed wait until the
00161   command completed, or not wait at all.
00162 
00163   EMC commands:
00164 
00165   EMC_INIFILE
00166   Exported values of the EMC global of the same name
00167 
00168   emc_plat
00169   Returns the platform for which this was compiled, e.g., linux_2_0_36
00170 
00171   emc_ini <var> <section>
00172   Returns the string value of <var> in section <section>, in EMC_INIFILE
00173 
00174   emc_debug {<new value>}
00175   With no arg, returns the integer value of EMC_DEBUG, in the EMC. Note that
00176   it may not be true that the local EMC_DEBUG variable here (in emcsh and
00177   the GUIs that use it) is the same as the EMC_DEBUG value in the EMC. This
00178   can happen if the EMC is started from one .ini file, and the GUI is started
00179   with another that has a different value for DEBUG.
00180   With an arg, sends a command to the EMC to set the new debug level,
00181   and sets the EMC_DEBUG global here to the same value. This will make
00182   the two values the same, since they really ought to be the same.
00183 
00184   emc_set_wait none | received | done
00185   Set the wait for commands to return to be right away (none), after the
00186   command was sent and received (received), or after the command was
00187   done (done).
00188 
00189   emc_wait received | done
00190   Force a wait for the previous command to be received, or done. This lets
00191   you wait in the event that "emc_set_wait none" is in effect.
00192 
00193   emc_set_timeout <timeout>
00194   Set the timeout for commands to return to <timeout>, in seconds. Timeout
00195   is a real number. If it's <= 0.0, it means wait forever. Default is 0.0,
00196   wait forever.
00197 
00198   emc_update (none) | none | auto
00199   With no arg, forces an update of the EMC status. With "none", doesn't
00200   cause an automatic update of status with other emc_ words. With "auto",
00201   makes emc_ words automatically update status before they return values.
00202 
00203   emc_error
00204   Returns the current EMC error string, or "ok" if no error.
00205 
00206   emc_operator_display
00207   Returns the current EMC operator display string, or "ok" if none.
00208 
00209   emc_operator_text
00210   Returns the current EMC operator text string, or "ok" if none.
00211 
00212   emc_time
00213   Returns the time, in seconds, from the start of the epoch. This starting
00214   time depends on the platform.
00215 
00216   emc_estop (none) | on | off
00217   With no arg, returns the estop setting as "on" or "off". Otherwise,
00218   sends an estop on or off command.
00219 
00220   emc_estop_in
00221   Returns the estop input setting as "on" or "off".
00222 
00223   emc_machine (none) | on | off
00224   With no arg, returns the machine setting as "on" or "off". Otherwise,
00225   sends a machine on or off command.
00226 
00227   emc_mode (none) | manual | auto | mdi
00228   With no arg, returns the mode setting as "manual", "auto", or "mdi".
00229   Otherwise, sends a mode manual, auto, or mdi command.
00230 
00231   emc_mist (none) | on | off
00232   With no arg, returns the mist setting as "on" or "off". Otherwise,
00233   sends a mist on or off command.
00234 
00235   emc_flood (none) | on | off
00236   With no arg, returns the flood setting as "on" or "off". Otherwise,
00237   sends a flood on or off command.
00238 
00239   emc_lube (none) | on | off
00240   With no arg, returns the lubricant pump setting as "on" or "off".
00241   Otherwise, sends a lube on or off command.
00242 
00243   emc_lube_level
00244   Returns the lubricant level sensor reading as "ok" or "low".
00245 
00246   emc_spindle (none) | forward | reverse | increase | decrease | constant | off
00247   With no arg, returns the value of the spindle state as "forward",
00248   "reverse", "increase", "decrease", or "off". With arg, sends the spindle
00249   command. Note that "increase" and "decrease" will cause a speed change in
00250   the corresponding direction until a "constant" command is sent.
00251 
00252   emc_brake (none) | on | off
00253   With no arg, returns the brake setting. Otherwise sets the brake.
00254 
00255   emc_tool
00256   Returns the id of the currently loaded tool
00257 
00258   emc_tool_offset
00259   Returns the currently applied tool length offset
00260 
00261   emc_load_tool_table <file>
00262   Loads the tool table specified by <file>
00263 
00264   emc_home 0 | 1 | 2 | ...
00265   Homes the indicated axis.
00266 
00267   emc_jog_stop 0 | 1 | 2 | ...
00268   Stop the axis jog
00269 
00270   emc_jog 0 | 1 | 2 | ... <speed>
00271   Jog the indicated axis at <speed>; sign of speed is direction
00272 
00273   emc_jog_incr 0 | 1 | 2 | ... <speed> <incr>
00274   Jog the indicated axis by increment <incr> at the <speed>; sign of
00275   speed is direction
00276 
00277   emc_feed_override {<percent>}
00278   With no args, returns the current feed override, as a percent. With
00279   argument, set the feed override to be the percent value
00280 
00281   emc_abs_cmd_pos 0 | 1 | ...
00282   Returns double obj containing the XYZ-SXYZ commanded pos in abs coords,
00283   at given index, 0 = X, etc.
00284 
00285   emc_abs_act_pos
00286   Returns double objs containing the XYZ-SXYZ actual pos in abs coords
00287 
00288   emc_rel_cmd_pos 0 | 1 | ...
00289   Returns double obj containing the XYZ-SXYZ commanded pos in rel coords,
00290   at given index, 0 = X, etc., including tool length offset
00291 
00292   emc_rel_act_pos
00293   Returns double objs containing the XYZ-SXYZ actual pos in rel coords,
00294   including tool length offset
00295 
00296   emc_joint_pos
00297   Returns double objs containing the actual pos in absolute coords of individual
00298   joint/slider positions, excludes tool length offset
00299 
00300   emc_pos_offset X | Y | Z | R | P | W
00301   Returns the position offset associated with the world coordinate provided
00302 
00303   emc_joint_limit 0 | 1 | ...
00304   Returns "ok", "minsoft", "minhard", "maxsoft", "maxhard"
00305 
00306   emc_joint_fault 0 | 1 | ...
00307   Returns "ok" or "fault"
00308 
00309   emc_joint_homed 0 | 1 | ...
00310   Returns "homed", "not"
00311 
00312   emc_mdi <string>
00313   Sends the <string> as an MDI command
00314 
00315   emc_task_plan_init
00316   Initializes the program interpreter
00317 
00318   emc_open <filename>
00319   Opens the named file
00320 
00321   emc_run {<start line>}
00322   Without start line, runs the opened program from the beginning. With
00323   start line, runs from that line. A start line of -1 runs in verify mode.
00324 
00325   emc_pause
00326   Pause program execution
00327 
00328   emc_resume
00329   Resume program execution
00330 
00331   emc_step
00332   Step the program one line
00333 
00334   emc_program
00335   Returns the name of the currently opened program, or "none"
00336 
00337   emc_program_line
00338   Returns the currently executing line of the program
00339 
00340   emc_program_status
00341   Returns "idle", "running", or "paused"
00342 
00343   emc_program_codes
00344   Returns the string for the currently active program codes
00345 
00346   emc_joint_type <joint>
00347   Returns "linear", "angular", or "custom" for the type of the specified joint
00348 
00349   emc_joint_units <joint>
00350   Returns "inch", "mm", "cm", or "deg", "rad", "grad", or "custom",
00351   for the corresponding native units of the specified axis. The type
00352   of the axis (linear or angular) is used to resolve which type of units
00353   are returned. The units are obtained heuristically, based on the
00354   EMC_AXIS_STAT::units numerical value of user units per mm or deg.
00355   For linear joints, something close to 0.03937 is deemed "inch",
00356   1.000 is "mm", 0.1 is "cm", otherwise it's "custom".
00357   For angular joints, something close to 1.000 is deemed "deg",
00358   PI/180 is "rad", 100/90 is "grad", otherwise it's "custom".
00359  
00360   emc_program_units
00361   emc_program_linear_units
00362   Returns "inch", "mm", "cm", or "none", for the corresponding linear 
00363   units that are active in the program interpreter.
00364 
00365   emc_program_angular_units
00366   Returns "deg", "rad", "grad", or "none" for the corresponding angular
00367   units that are active in the program interpreter.
00368 
00369   emc_user_linear_units
00370   Returns "inch", "mm", "cm", or "custom", for the
00371   corresponding native user linear units of the EMC trajectory
00372   level. This is obtained heuristically, based on the
00373   EMC_TRAJ_STAT::linearUnits numerical value of user units per mm.
00374   Something close to 0.03937 is deemed "inch", 1.000 is "mm", 0.1 is
00375   "cm", otherwise it's "custom".
00376 
00377   emc_user_angular_units
00378   Returns "deg", "rad", "grad", or "custom" for the corresponding native
00379   user angular units of the EMC trajectory level. Like with linear units,
00380   this is obtained heuristically.
00381 
00382   emc_display_linear_units
00383   emc_display_angular_units
00384   Returns "inch", "mm", "cm", or "deg", "rad", "grad", or "custom",
00385   for the linear or angular units that are active in the display. 
00386   This is effectively the value of linearUnitConversion or
00387   angularUnitConversion, resp.
00388 
00389   emc_linear_unit_conversion {inch | mm | cm | auto}
00390   With no args, returns the unit conversion active. With arg, sets the
00391   units to be displayed. If it's "auto", the units to be displayed match
00392   the program units.
00393  
00394   emc_angular_unit_conversion {deg | rad | grad | auto}
00395   With no args, returns the unit conversion active. With arg, sets the
00396   units to be displayed. If it's "auto", the units to be displayed match
00397   the program units.
00398 
00399   emc_log_open <file> <type> <size> <skip> <triggertype> <triggervar> <triggerthreshold> {<axis>}
00400   Open a log into file named <file>. <type> is one of axis_pos, axes_inpos,
00401   axes_outpos, axis_vel, axes_ferror, traj_pos, traj_vel, traj_acc,
00402   pos_voltage. <size> is size in entries. <skip> is how many to skip.
00403   <axis> pertains only to axis_pos and axis_vel types.
00404 
00405   emc_log_start
00406   emc_log_stop
00407   Starts or stops logging into the log, which must be already opened
00408   with emc_log_open.
00409 
00410   emc_log_close
00411   Close the log and dump contents to the file specified in emc_log_open.
00412 
00413 
00414   emc_probe_index
00415   Which wire is the probe on or which bit of digital IO to use? (No args
00416   gets it, one arg sets it.)
00417 
00418   emc_probe_polarity
00419   Value to look for for probe tripped? (0 args gets it, one arg sets it.)
00420 
00421   emc_probe_clear
00422   Clear the probe tripped flag.
00423 
00424   emc_probe_tripped
00425   Has the probe been tripped since the last clear.
00426 
00427   emc_probe_value
00428   Value of current probe signal. (read-only)
00429 
00430   emc_probe
00431   Move toward a certain location. If the probe is tripped on the way stop
00432   motion, record the position and raise the probe tripped flag.
00433 
00434   emc_teleop_enable
00435   Should motion run in teleop mode? (No args
00436   gets it, one arg sets it.)
00437 
00438   emc_kinematics_type
00439   returns the type of kinematics functions used identity=1, serial=2,
00440   parallel=3, custom=4
00441 */
00442 
00443 // the NML channels to the EMC task
00444 static RCS_CMD_CHANNEL *emcCommandBuffer = 0;
00445 static RCS_STAT_CHANNEL *emcStatusBuffer = 0;
00446 EMC_STAT *emcStatus = 0;
00447 
00448 // the NML channel for errors
00449 static NML *emcErrorBuffer = 0;
00450 static char error_string[EMC_OPERATOR_ERROR_LEN] = "";
00451 static char operator_text_string[EMC_OPERATOR_TEXT_LEN] = "";
00452 static char operator_display_string[EMC_OPERATOR_DISPLAY_LEN] = "";
00453 
00454 // the current command numbers, set up updateStatus(), used in main()
00455 static int emcCommandSerialNumber = 0;
00456 static int saveEmcCommandSerialNumber = 0;
00457 
00458 // default value for timeout, 0 means wait forever
00459 static double emcTimeout = 0.0;
00460 
00461 static enum {
00462   EMC_WAIT_NONE = 1,
00463   EMC_WAIT_RECEIVED,
00464   EMC_WAIT_DONE
00465 } emcWaitType = EMC_WAIT_DONE;
00466 
00467 static enum {
00468   EMC_UPDATE_NONE = 1,
00469   EMC_UPDATE_AUTO
00470 } emcUpdateType = EMC_UPDATE_AUTO;
00471 
00472 static int emcTaskNmlGet()
00473 {
00474   int retval = 0;
00475 
00476   // try to connect to EMC cmd
00477   if (emcCommandBuffer == 0) {
00478     emcCommandBuffer = new RCS_CMD_CHANNEL(emcFormat, "emcCommand", "xemc", EMC_NMLFILE);
00479     if (! emcCommandBuffer->valid()) {
00480       delete emcCommandBuffer;
00481       emcCommandBuffer = 0;
00482       retval = -1;
00483     }
00484   }
00485 
00486   // try to connect to EMC status
00487   if (emcStatusBuffer == 0) {
00488     emcStatusBuffer = new RCS_STAT_CHANNEL(emcFormat, "emcStatus", "xemc", EMC_NMLFILE);
00489     if (! emcStatusBuffer->valid() ||
00490         EMC_STAT_TYPE != emcStatusBuffer->peek()) {
00491       delete emcStatusBuffer;
00492       emcStatusBuffer = 0;
00493       emcStatus = 0;
00494       retval = -1;
00495     }
00496     else {
00497       emcStatus = (EMC_STAT *) emcStatusBuffer->get_address();
00498     }
00499   }
00500 
00501   return retval;
00502 }
00503 
00504 static int emcErrorNmlGet()
00505 {
00506   int retval = 0;
00507 
00508   if (emcErrorBuffer == 0) {
00509     emcErrorBuffer = new NML(nmlErrorFormat, "emcError", "xemc", EMC_NMLFILE);
00510     if (! emcErrorBuffer->valid()) {
00511       delete emcErrorBuffer;
00512       emcErrorBuffer = 0;
00513       retval = -1;
00514     }
00515   }
00516 
00517   return retval;
00518 }
00519 
00520 static int tryNml()
00521 {
00522   double end;
00523   int good;
00524 #define RETRY_TIME 10.0         // seconds to wait for subsystems to come up
00525 #define RETRY_INTERVAL 1.0      // seconds between wait tries for a subsystem
00526 
00527   if (EMC_DEBUG & EMC_DEBUG_NML == 0) {
00528     set_rcs_print_destination(RCS_PRINT_TO_NULL);       // inhibit diag messages
00529   }
00530   end = RETRY_TIME;
00531   good = 0;
00532   do {
00533     if (0 == emcTaskNmlGet()) {
00534       good = 1;
00535       break;
00536     }
00537     esleep(RETRY_INTERVAL);
00538     end -= RETRY_INTERVAL;
00539   } while (end > 0.0);
00540   if (EMC_DEBUG & EMC_DEBUG_NML == 0) {
00541     set_rcs_print_destination(RCS_PRINT_TO_STDOUT);     // inhibit diag messages
00542   }
00543   if (! good) {
00544     return -1;
00545   }
00546 
00547   if (EMC_DEBUG & EMC_DEBUG_NML == 0) {
00548     set_rcs_print_destination(RCS_PRINT_TO_NULL);       // inhibit diag messages
00549   }
00550   end = RETRY_TIME;
00551   good = 0;
00552   do {
00553     if (0 == emcErrorNmlGet()) {
00554       good = 1;
00555       break;
00556     }
00557     esleep(RETRY_INTERVAL);
00558     end -= RETRY_INTERVAL;
00559   } while (end > 0.0);
00560   if (EMC_DEBUG & EMC_DEBUG_NML == 0) {
00561     set_rcs_print_destination(RCS_PRINT_TO_STDOUT);     // inhibit diag messages
00562   }
00563   if (! good) {
00564     return -1;
00565   }
00566 
00567   return 0;
00568 
00569 #undef RETRY_TIME
00570 #undef RETRY_INTERVAL
00571 }
00572 
00573 static int updateStatus()
00574 {
00575   NMLTYPE type;
00576 
00577   if (0 == emcStatus ||
00578       0 == emcStatusBuffer ||
00579       ! emcStatusBuffer->valid()) {
00580     return -1;
00581   }
00582 
00583   switch (type = emcStatusBuffer->peek()) {
00584   case -1:
00585     // error on CMS channel
00586     return -1;
00587     break;
00588 
00589   case 0:                       // no new data
00590   case EMC_STAT_TYPE:   // new data
00591     // new data
00592     break;
00593 
00594   default:
00595     return -1;
00596     break;
00597   }
00598 
00599   return 0;
00600 }
00601 
00602 /*
00603   updateError() updates "errors," which are true errors and also
00604   operator display and text messages.
00605 */
00606 static int updateError()
00607 {
00608   NMLTYPE type;
00609 
00610   if (0 == emcErrorBuffer ||
00611       ! emcErrorBuffer->valid()) {
00612     return -1;
00613   }
00614 
00615   switch (type = emcErrorBuffer->read()) {
00616   case -1:
00617     // error reading channel
00618     return -1;
00619     break;
00620 
00621   case 0:
00622     // nothing new
00623     break;
00624 
00625   case EMC_OPERATOR_ERROR_TYPE:
00626     strncpy(error_string,
00627             ((EMC_OPERATOR_ERROR *) (emcErrorBuffer->get_address()))->error,
00628             EMC_OPERATOR_ERROR_LEN - 1);
00629     error_string[EMC_OPERATOR_ERROR_LEN - 1] = 0;
00630     break;
00631 
00632   case EMC_OPERATOR_TEXT_TYPE:
00633     strncpy(operator_text_string,
00634             ((EMC_OPERATOR_TEXT *) (emcErrorBuffer->get_address()))->text,
00635             EMC_OPERATOR_TEXT_LEN - 1);
00636     operator_text_string[EMC_OPERATOR_TEXT_LEN - 1] = 0;
00637     break;
00638 
00639   case EMC_OPERATOR_DISPLAY_TYPE:
00640     strncpy(operator_display_string,
00641             ((EMC_OPERATOR_DISPLAY *) (emcErrorBuffer->get_address()))->display,
00642             EMC_OPERATOR_DISPLAY_LEN - 1);
00643     operator_display_string[EMC_OPERATOR_DISPLAY_LEN - 1] = 0;
00644     break;
00645 
00646 
00647   case NML_ERROR_TYPE:
00648     strncpy(error_string,
00649             ((NML_ERROR *) (emcErrorBuffer->get_address()))->error,
00650             NML_ERROR_LEN - 1);
00651     error_string[NML_ERROR_LEN - 1] = 0;
00652     break;
00653 
00654   case NML_TEXT_TYPE:
00655     strncpy(operator_text_string,
00656             ((NML_TEXT *) (emcErrorBuffer->get_address()))->text,
00657             NML_TEXT_LEN - 1);
00658     operator_text_string[NML_TEXT_LEN - 1] = 0;
00659     break;
00660 
00661   case NML_DISPLAY_TYPE:
00662     strncpy(operator_display_string,
00663             ((NML_DISPLAY *) (emcErrorBuffer->get_address()))->display,
00664             NML_DISPLAY_LEN - 1);
00665     operator_display_string[NML_DISPLAY_LEN - 1] = 0;
00666     break;
00667 
00668   default:
00669     // if not recognized, set the error string
00670     sprintf(error_string, "unrecognized error %ld",type);
00671     return -1;
00672     break;
00673   }
00674 
00675   return 0;
00676 }
00677 
00678 #define EMC_COMMAND_DELAY   0.1 // how long to sleep between checks
00679 
00680 /*
00681   emcCommandWaitReceived() waits until the EMC reports that it got
00682   the command with the indicated serial_number.
00683   emcCommandWaitDone() waits until the EMC reports that it got the
00684   command with the indicated serial_number, and it's done, or error.
00685 */
00686 
00687 static int emcCommandWaitReceived(int serial_number)
00688 {
00689   double end = 0.0;
00690 
00691   while (emcTimeout <= 0.0 ||
00692          end < emcTimeout) {
00693     updateStatus();
00694 
00695     if (emcStatus->echo_serial_number == serial_number) {
00696       return 0;
00697     }
00698 
00699     esleep(EMC_COMMAND_DELAY);
00700     end += EMC_COMMAND_DELAY;
00701   }
00702 
00703   return -1;
00704 }
00705 
00706 static int emcCommandWaitDone(int serial_number)
00707 {
00708   double end = 0.0;
00709 
00710   // first get it there
00711   if (0 != emcCommandWaitReceived(serial_number)) {
00712     return -1;
00713   }
00714 
00715   // now wait until it, or subsequent command (e.g., abort) is done
00716   while (emcTimeout <= 0.0 ||
00717          end < emcTimeout) {
00718     updateStatus();
00719 
00720     if (emcStatus->status == RCS_DONE) {
00721       return 0;
00722     }
00723 
00724     if (emcStatus->status == RCS_ERROR) {
00725       return -1;
00726     }
00727 
00728     esleep(EMC_COMMAND_DELAY);
00729     end += EMC_COMMAND_DELAY;
00730   }
00731 
00732   return -1;
00733 }
00734 
00735 static void thisQuit(ClientData clientData)
00736 {
00737   EMC_NULL emc_null_msg;
00738 
00739   if (0 != emcStatusBuffer) {
00740     // wait until current message has been received
00741     emcCommandWaitReceived(emcCommandSerialNumber);
00742   }
00743 
00744   if (0 != emcCommandBuffer) {
00745     // send null message to reset serial number to original
00746     emc_null_msg.serial_number = saveEmcCommandSerialNumber;
00747     emcCommandBuffer->write(emc_null_msg);
00748   }
00749 
00750   // clean up NML buffers
00751 
00752   if (emcErrorBuffer != 0) {
00753     delete emcErrorBuffer;
00754     emcErrorBuffer = 0;
00755   }
00756 
00757   if (emcStatusBuffer != 0) {
00758     delete emcStatusBuffer;
00759     emcStatusBuffer = 0;
00760     emcStatus = 0;
00761   }
00762 
00763   if (emcCommandBuffer != 0) {
00764     delete emcCommandBuffer;
00765     emcCommandBuffer = 0;
00766   }
00767 
00768   Tcl_Exit(0);
00769   exit(0);
00770 }
00771 
00772 /*
00773   Unit conversion
00774 
00775   Length and angle units in the EMC status buffer are in user units, as
00776   defined in the INI file in [TRAJ] LINEAR,ANGULAR_UNITS. These may differ
00777   from the program units, and when they are the display is confusing.
00778 
00779   It may be desirable to synchronize the display units with the program
00780   units automatically, and also to break this sync and allow independent
00781   display of position values.
00782 
00783   The global variable "linearUnitConversion" is set by the Tcl commands
00784   emc_linear_unit_conversion to correspond to either "inch",
00785   "mm", "cm", "auto", or "custom". This forces numbers to be returned in the
00786   units specified, in program units when "auto" is set, or not converted
00787   at all if "custom" is specified.
00788 
00789   Ditto for "angularUnitConversion", set by emc_angular_unit_conversion
00790   to "deg", "rad", "grad", "auto", or "custom".
00791 
00792   With no args, emc_linear/angular_unit_conversion return the setting.
00793 
00794   The functions convertLinearUnits and convertAngularUnits take a length
00795   or angle value, typically from the emcStatus structure, and convert it
00796   as indicated by linearUnitConversion and angularUnitConversion, resp.
00797 */
00798 
00799 static enum {
00800   LINEAR_UNITS_CUSTOM = 1,
00801   LINEAR_UNITS_AUTO,
00802   LINEAR_UNITS_MM,
00803   LINEAR_UNITS_INCH,
00804   LINEAR_UNITS_CM
00805 } linearUnitConversion = LINEAR_UNITS_AUTO;
00806 
00807 static enum {
00808   ANGULAR_UNITS_CUSTOM = 1,
00809   ANGULAR_UNITS_AUTO,
00810   ANGULAR_UNITS_DEG,
00811   ANGULAR_UNITS_RAD,
00812   ANGULAR_UNITS_GRAD
00813 } angularUnitConversion = ANGULAR_UNITS_AUTO;
00814 
00815 #define CLOSE(a,b,eps) ((a)-(b) < +(eps) && (a)-(b) > -(eps))
00816 #define LINEAR_CLOSENESS 0.0001
00817 #define ANGULAR_CLOSENESS 0.0001
00818 #define INCH_PER_MM (1.0/25.4)
00819 #define CM_PER_MM 0.1
00820 #define GRAD_PER_DEG (100.0/90.0)
00821 #define RAD_PER_DEG TO_RAD // from posemath.h
00822 
00823 /*
00824   to convert linear units, values are converted to mm, then to desired
00825   units
00826 */
00827 static double convertLinearUnits(double u)
00828 {
00829   double in_mm;
00830 
00831   /* convert u to mm */
00832   in_mm = u / emcStatus->motion.traj.linearUnits;
00833 
00834   /* convert u to display units */  
00835   switch (linearUnitConversion) {
00836   case LINEAR_UNITS_MM:
00837     return in_mm;
00838     break;
00839   case LINEAR_UNITS_INCH:
00840     return in_mm * INCH_PER_MM;
00841     break;
00842   case LINEAR_UNITS_CM:
00843     return in_mm * CM_PER_MM;
00844     break;
00845   case LINEAR_UNITS_AUTO:
00846     switch (emcStatus->task.programUnits) {
00847     case CANON_UNITS_MM:
00848       return in_mm;
00849       break;
00850     case CANON_UNITS_INCHES:
00851       return in_mm * INCH_PER_MM;
00852       break;
00853     case CANON_UNITS_CM:
00854       return in_mm * CM_PER_MM;
00855       break;
00856     }
00857     break;
00858 
00859   case LINEAR_UNITS_CUSTOM:
00860     return u;
00861     break;
00862   }
00863 
00864   // If it ever gets here we have an error.
00865 
00866   return u;
00867 }
00868 
00869 #if 0
00870 static double convertAngularUnits(double u)
00871 {
00872   double in_deg;
00873 
00874   /* convert u to deg */
00875   in_deg = u / emcStatus->motion.traj.angularUnits;
00876 
00877   /* convert u to display units */  
00878   switch (angularUnitConversion) {
00879   case ANGULAR_UNITS_DEG:
00880     return in_deg;
00881     break;
00882   case ANGULAR_UNITS_RAD:
00883     return in_deg * RAD_PER_DEG;
00884     break;
00885   case ANGULAR_UNITS_GRAD:
00886     return in_deg * GRAD_PER_DEG;
00887     break;
00888   case ANGULAR_UNITS_AUTO:
00889     return in_deg;              // FIXME-- program units always degrees now
00890     break;
00891 
00892   case ANGULAR_UNITS_CUSTOM:
00893     return u;
00894     break;
00895 
00896   }
00897 
00898   /* should never get here */
00899   return u;
00900 }
00901 #endif
00902 
00903 // polarities for axis jogging, from ini file
00904 static int jogPol[EMC_AXIS_MAX];
00905 
00906 static int sendDebug(int level)
00907 {
00908   EMC_SET_DEBUG debug_msg;
00909 
00910   debug_msg.debug = level;
00911   debug_msg.serial_number = ++emcCommandSerialNumber;
00912   emcCommandBuffer->write(debug_msg);
00913   if (emcWaitType == EMC_WAIT_RECEIVED) {
00914     return emcCommandWaitReceived(emcCommandSerialNumber);
00915   }
00916   else if (emcWaitType == EMC_WAIT_DONE) {
00917     return emcCommandWaitDone(emcCommandSerialNumber);
00918   }
00919 
00920   return 0;
00921 }
00922 
00923 static int sendEstop()
00924 {
00925   EMC_TASK_SET_STATE state_msg;
00926 
00927   state_msg.state = EMC_TASK_STATE_ESTOP;
00928   state_msg.serial_number = ++emcCommandSerialNumber;
00929   emcCommandBuffer->write(state_msg);
00930   if (emcWaitType == EMC_WAIT_RECEIVED) {
00931     return emcCommandWaitReceived(emcCommandSerialNumber);
00932   }
00933   else if (emcWaitType == EMC_WAIT_DONE) {
00934     return emcCommandWaitDone(emcCommandSerialNumber);
00935   }
00936 
00937   return 0;
00938 }
00939 
00940 static int sendEstopReset()
00941 {
00942   EMC_TASK_SET_STATE state_msg;
00943 
00944   state_msg.state = EMC_TASK_STATE_ESTOP_RESET;
00945   state_msg.serial_number = ++emcCommandSerialNumber;
00946   emcCommandBuffer->write(state_msg);
00947   if (emcWaitType == EMC_WAIT_RECEIVED) {
00948     return emcCommandWaitReceived(emcCommandSerialNumber);
00949   }
00950   else if (emcWaitType == EMC_WAIT_DONE) {
00951     return emcCommandWaitDone(emcCommandSerialNumber);
00952   }
00953 
00954   return 0;
00955 }
00956 
00957 static int sendMachineOn()
00958 {
00959   EMC_TASK_SET_STATE state_msg;
00960 
00961   state_msg.state = EMC_TASK_STATE_ON;
00962   state_msg.serial_number = ++emcCommandSerialNumber;
00963   emcCommandBuffer->write(state_msg);
00964   if (emcWaitType == EMC_WAIT_RECEIVED) {
00965     return emcCommandWaitReceived(emcCommandSerialNumber);
00966   }
00967   else if (emcWaitType == EMC_WAIT_DONE) {
00968     return emcCommandWaitDone(emcCommandSerialNumber);
00969   }
00970 
00971   return 0;
00972 }
00973 
00974 static int sendMachineOff()
00975 {
00976   EMC_TASK_SET_STATE state_msg;
00977 
00978   state_msg.state = EMC_TASK_STATE_OFF;
00979   state_msg.serial_number = ++emcCommandSerialNumber;
00980   emcCommandBuffer->write(state_msg);
00981   if (emcWaitType == EMC_WAIT_RECEIVED) {
00982     return emcCommandWaitReceived(emcCommandSerialNumber);
00983   }
00984   else if (emcWaitType == EMC_WAIT_DONE) {
00985     return emcCommandWaitDone(emcCommandSerialNumber);
00986   }
00987 
00988   return 0;
00989 }
00990 
00991 static int sendManual()
00992 {
00993   EMC_TASK_SET_MODE mode_msg;
00994 
00995   mode_msg.mode = EMC_TASK_MODE_MANUAL;
00996   mode_msg.serial_number = ++emcCommandSerialNumber;
00997   emcCommandBuffer->write(mode_msg);
00998   if (emcWaitType == EMC_WAIT_RECEIVED) {
00999     return emcCommandWaitReceived(emcCommandSerialNumber);
01000   }
01001   else if (emcWaitType == EMC_WAIT_DONE) {
01002     return emcCommandWaitDone(emcCommandSerialNumber);
01003   }
01004 
01005   return 0;
01006 }
01007 
01008 static int sendAuto()
01009 {
01010   EMC_TASK_SET_MODE mode_msg;
01011 
01012   mode_msg.mode = EMC_TASK_MODE_AUTO;
01013   mode_msg.serial_number = ++emcCommandSerialNumber;
01014   emcCommandBuffer->write(mode_msg);
01015   if (emcWaitType == EMC_WAIT_RECEIVED) {
01016     return emcCommandWaitReceived(emcCommandSerialNumber);
01017   }
01018   else if (emcWaitType == EMC_WAIT_DONE) {
01019     return emcCommandWaitDone(emcCommandSerialNumber);
01020   }
01021 
01022   return 0;
01023 }
01024 
01025 static int sendMdi()
01026 {
01027   EMC_TASK_SET_MODE mode_msg;
01028 
01029   mode_msg.mode = EMC_TASK_MODE_MDI;
01030   mode_msg.serial_number = ++emcCommandSerialNumber;
01031   emcCommandBuffer->write(mode_msg);
01032   if (emcWaitType == EMC_WAIT_RECEIVED) {
01033     return emcCommandWaitReceived(emcCommandSerialNumber);
01034   }
01035   else if (emcWaitType == EMC_WAIT_DONE) {
01036     return emcCommandWaitDone(emcCommandSerialNumber);
01037   }
01038 
01039   return 0;
01040 }
01041 
01042 static int sendOverrideLimits(int axis)
01043 {
01044   EMC_AXIS_OVERRIDE_LIMITS lim_msg;
01045 
01046   lim_msg.axis = axis;          // neg means off, else on for all
01047   lim_msg.serial_number = ++emcCommandSerialNumber;
01048   emcCommandBuffer->write(lim_msg);
01049   if (emcWaitType == EMC_WAIT_RECEIVED) {
01050     return emcCommandWaitReceived(emcCommandSerialNumber);
01051   }
01052   else if (emcWaitType == EMC_WAIT_DONE) {
01053     return emcCommandWaitDone(emcCommandSerialNumber);
01054   }
01055 
01056   return 0;
01057 }
01058 static int axisJogging = -1;
01059 
01060 static int sendJogStop(int axis)
01061 {
01062   EMC_AXIS_ABORT emc_axis_abort_msg;
01063 
01064   if (axis < 0 || axis >= EMC_AXIS_MAX) {
01065     return -1;
01066   }
01067 
01068   emc_axis_abort_msg.serial_number = ++emcCommandSerialNumber;
01069   emc_axis_abort_msg.axis = axis;
01070   emcCommandBuffer->write(emc_axis_abort_msg);
01071 
01072   if (emcWaitType == EMC_WAIT_RECEIVED) {
01073     return emcCommandWaitReceived(emcCommandSerialNumber);
01074   }
01075   else if (emcWaitType == EMC_WAIT_DONE) {
01076     return emcCommandWaitDone(emcCommandSerialNumber);
01077   }
01078 
01079   axisJogging = -1;
01080   return 0;
01081 }
01082 
01083 static int sendJogCont(int axis, double speed)
01084 {
01085   EMC_AXIS_JOG emc_axis_jog_msg;
01086   EMC_TRAJ_SET_TELEOP_VECTOR emc_set_teleop_vector;
01087 
01088   if (axis < 0 || axis >= EMC_AXIS_MAX) {
01089     return -1;
01090   }
01091 
01092   if (emcStatus->motion.traj.mode != EMC_TRAJ_MODE_TELEOP) {
01093     if (0 == jogPol[axis]) {
01094       speed = -speed;
01095     }
01096       
01097     emc_axis_jog_msg.serial_number = ++emcCommandSerialNumber;
01098     emc_axis_jog_msg.axis = axis;
01099     emc_axis_jog_msg.vel = speed / 60.0;
01100     emcCommandBuffer->write(emc_axis_jog_msg);
01101   }
01102   else {
01103     emc_set_teleop_vector.serial_number = ++emcCommandSerialNumber;
01104     emc_set_teleop_vector.vector.tran.x = 0.0;
01105     emc_set_teleop_vector.vector.tran.y = 0.0;
01106     emc_set_teleop_vector.vector.tran.z = 0.0;
01107 
01108     switch(axis) {
01109     case 0:
01110       emc_set_teleop_vector.vector.tran.x = speed/60.0;
01111       break;
01112     case 1:
01113       emc_set_teleop_vector.vector.tran.y = speed/60.0;
01114       break;
01115     case 2:
01116       emc_set_teleop_vector.vector.tran.z = speed/60.0;
01117       break;
01118     case 3:
01119       emc_set_teleop_vector.vector.a = speed/60.0;
01120       break;
01121     case 4:
01122       emc_set_teleop_vector.vector.b = speed/60.0;
01123       break;
01124     case 5:
01125       emc_set_teleop_vector.vector.c = speed/60.0;
01126       break;
01127     }
01128     emcCommandBuffer->write(emc_set_teleop_vector);
01129   }
01130 
01131   axisJogging = axis;
01132   if (emcWaitType == EMC_WAIT_RECEIVED) {
01133     return emcCommandWaitReceived(emcCommandSerialNumber);
01134   }
01135   else if (emcWaitType == EMC_WAIT_DONE) {
01136     return emcCommandWaitDone(emcCommandSerialNumber);
01137   }
01138 
01139   return 0;
01140 }
01141 
01142 static int sendJogIncr(int axis, double speed, double incr)
01143 {
01144   EMC_AXIS_INCR_JOG emc_axis_incr_jog_msg;
01145 
01146   if (axis < 0 || axis >= EMC_AXIS_MAX) {
01147     return -1;
01148   }
01149 
01150   if (0 == jogPol[axis]) {
01151     speed = -speed;
01152   }
01153 
01154   emc_axis_incr_jog_msg.serial_number = ++emcCommandSerialNumber;
01155   emc_axis_incr_jog_msg.axis = axis;
01156   emc_axis_incr_jog_msg.vel = speed / 60.0;
01157   emc_axis_incr_jog_msg.incr = incr;
01158   emcCommandBuffer->write(emc_axis_incr_jog_msg);
01159 
01160   if (emcWaitType == EMC_WAIT_RECEIVED) {
01161     return emcCommandWaitReceived(emcCommandSerialNumber);
01162   }
01163   else if (emcWaitType == EMC_WAIT_DONE) {
01164     return emcCommandWaitDone(emcCommandSerialNumber);
01165   }
01166   axisJogging = -1;
01167 
01168   return 0;
01169 }
01170 
01171 static int sendMistOn()
01172 {
01173   EMC_COOLANT_MIST_ON emc_coolant_mist_on_msg;
01174 
01175   emc_coolant_mist_on_msg.serial_number = ++emcCommandSerialNumber;
01176   emcCommandBuffer->write(emc_coolant_mist_on_msg);
01177   if (emcWaitType == EMC_WAIT_RECEIVED) {
01178     return emcCommandWaitReceived(emcCommandSerialNumber);
01179   }
01180   else if (emcWaitType == EMC_WAIT_DONE) {
01181     return emcCommandWaitDone(emcCommandSerialNumber);
01182   }
01183 
01184   return 0;
01185 }
01186 
01187 static int sendMistOff()
01188 {
01189   EMC_COOLANT_MIST_OFF emc_coolant_mist_off_msg;
01190 
01191   emc_coolant_mist_off_msg.serial_number = ++emcCommandSerialNumber;
01192   emcCommandBuffer->write(emc_coolant_mist_off_msg);
01193   if (emcWaitType == EMC_WAIT_RECEIVED) {
01194     return emcCommandWaitReceived(emcCommandSerialNumber);
01195   }
01196   else if (emcWaitType == EMC_WAIT_DONE) {
01197     return emcCommandWaitDone(emcCommandSerialNumber);
01198   }
01199 
01200   return 0;
01201 }
01202 
01203 static int sendFloodOn()
01204 {
01205   EMC_COOLANT_FLOOD_ON emc_coolant_flood_on_msg;
01206 
01207   emc_coolant_flood_on_msg.serial_number = ++emcCommandSerialNumber;
01208   emcCommandBuffer->write(emc_coolant_flood_on_msg);
01209   if (emcWaitType == EMC_WAIT_RECEIVED) {
01210     return emcCommandWaitReceived(emcCommandSerialNumber);
01211   }
01212   else if (emcWaitType == EMC_WAIT_DONE) {
01213     return emcCommandWaitDone(emcCommandSerialNumber);
01214   }
01215 
01216   return 0;
01217 }
01218 
01219 static int sendFloodOff()
01220 {
01221   EMC_COOLANT_FLOOD_OFF emc_coolant_flood_off_msg;
01222 
01223   emc_coolant_flood_off_msg.serial_number = ++emcCommandSerialNumber;
01224   emcCommandBuffer->write(emc_coolant_flood_off_msg);
01225   if (emcWaitType == EMC_WAIT_RECEIVED) {
01226     return emcCommandWaitReceived(emcCommandSerialNumber);
01227   }
01228   else if (emcWaitType == EMC_WAIT_DONE) {
01229     return emcCommandWaitDone(emcCommandSerialNumber);
01230   }
01231 
01232   return 0;
01233 }
01234 
01235 static int sendLubeOn()
01236 {
01237   EMC_LUBE_ON emc_lube_on_msg;
01238 
01239   emc_lube_on_msg.serial_number = ++emcCommandSerialNumber;
01240   emcCommandBuffer->write(emc_lube_on_msg);
01241   if (emcWaitType == EMC_WAIT_RECEIVED) {
01242     return emcCommandWaitReceived(emcCommandSerialNumber);
01243   }
01244   else if (emcWaitType == EMC_WAIT_DONE) {
01245     return emcCommandWaitDone(emcCommandSerialNumber);
01246   }
01247 
01248   return 0;
01249 }
01250 
01251 static int sendLubeOff()
01252 {
01253   EMC_LUBE_OFF emc_lube_off_msg;
01254 
01255   emc_lube_off_msg.serial_number = ++emcCommandSerialNumber;
01256   emcCommandBuffer->write(emc_lube_off_msg);
01257   if (emcWaitType == EMC_WAIT_RECEIVED) {
01258     return emcCommandWaitReceived(emcCommandSerialNumber);
01259   }
01260   else if (emcWaitType == EMC_WAIT_DONE) {
01261     return emcCommandWaitDone(emcCommandSerialNumber);
01262   }
01263 
01264   return 0;
01265 }
01266 
01267 static int sendSpindleForward()
01268 {
01269   EMC_SPINDLE_ON emc_spindle_on_msg;
01270 
01271   emc_spindle_on_msg.speed = +1;
01272   emc_spindle_on_msg.serial_number = ++emcCommandSerialNumber;
01273   emcCommandBuffer->write(emc_spindle_on_msg);
01274   if (emcWaitType == EMC_WAIT_RECEIVED) {
01275     return emcCommandWaitReceived(emcCommandSerialNumber);
01276   }
01277   else if (emcWaitType == EMC_WAIT_DONE) {
01278     return emcCommandWaitDone(emcCommandSerialNumber);
01279   }
01280 
01281   return 0;
01282 }
01283 
01284 static int sendSpindleReverse()
01285 {
01286   EMC_SPINDLE_ON emc_spindle_on_msg;
01287 
01288   emc_spindle_on_msg.speed = -1;
01289   emc_spindle_on_msg.serial_number = ++emcCommandSerialNumber;
01290   emcCommandBuffer->write(emc_spindle_on_msg);
01291   if (emcWaitType == EMC_WAIT_RECEIVED) {
01292     return emcCommandWaitReceived(emcCommandSerialNumber);
01293   }
01294   else if (emcWaitType == EMC_WAIT_DONE) {
01295     return emcCommandWaitDone(emcCommandSerialNumber);
01296   }
01297 
01298   return 0;
01299 }
01300 
01301 static int sendSpindleOff()
01302 {
01303   EMC_SPINDLE_OFF emc_spindle_off_msg;
01304 
01305   emc_spindle_off_msg.serial_number = ++emcCommandSerialNumber;
01306   emcCommandBuffer->write(emc_spindle_off_msg);
01307   if (emcWaitType == EMC_WAIT_RECEIVED) {
01308     return emcCommandWaitReceived(emcCommandSerialNumber);
01309   }
01310   else if (emcWaitType == EMC_WAIT_DONE) {
01311     return emcCommandWaitDone(emcCommandSerialNumber);
01312   }
01313 
01314   return 0;
01315 }
01316 
01317 static int sendSpindleIncrease()
01318 {
01319   EMC_SPINDLE_INCREASE emc_spindle_increase_msg;
01320 
01321   emc_spindle_increase_msg.serial_number = ++emcCommandSerialNumber;
01322   emcCommandBuffer->write(emc_spindle_increase_msg);
01323   if (emcWaitType == EMC_WAIT_RECEIVED) {
01324     return emcCommandWaitReceived(emcCommandSerialNumber);
01325   }
01326   else if (emcWaitType == EMC_WAIT_DONE) {
01327     return emcCommandWaitDone(emcCommandSerialNumber);
01328   }
01329 
01330   return 0;
01331 }
01332 
01333 static int sendSpindleDecrease()
01334 {
01335   EMC_SPINDLE_DECREASE emc_spindle_decrease_msg;
01336 
01337   emc_spindle_decrease_msg.serial_number = ++emcCommandSerialNumber;
01338   emcCommandBuffer->write(emc_spindle_decrease_msg);
01339   if (emcWaitType == EMC_WAIT_RECEIVED) {
01340     return emcCommandWaitReceived(emcCommandSerialNumber);
01341   }
01342   else if (emcWaitType == EMC_WAIT_DONE) {
01343     return emcCommandWaitDone(emcCommandSerialNumber);
01344   }
01345 
01346   return 0;
01347 }
01348 
01349 static int sendSpindleConstant()
01350 {
01351   EMC_SPINDLE_CONSTANT emc_spindle_constant_msg;
01352 
01353   emc_spindle_constant_msg.serial_number = ++emcCommandSerialNumber;
01354   emcCommandBuffer->write(emc_spindle_constant_msg);
01355   if (emcWaitType == EMC_WAIT_RECEIVED) {
01356     return emcCommandWaitReceived(emcCommandSerialNumber);
01357   }
01358   else if (emcWaitType == EMC_WAIT_DONE) {
01359     return emcCommandWaitDone(emcCommandSerialNumber);
01360   }
01361 
01362   return 0;
01363 }
01364 
01365 static int sendBrakeEngage()
01366 {
01367   EMC_SPINDLE_BRAKE_ENGAGE emc_spindle_brake_engage_msg;
01368 
01369   emc_spindle_brake_engage_msg.serial_number = ++emcCommandSerialNumber;
01370   emcCommandBuffer->write(emc_spindle_brake_engage_msg);
01371   if (emcWaitType == EMC_WAIT_RECEIVED) {
01372     return emcCommandWaitReceived(emcCommandSerialNumber);
01373   }
01374   else if (emcWaitType == EMC_WAIT_DONE) {
01375     return emcCommandWaitDone(emcCommandSerialNumber);
01376   }
01377 
01378   return 0;
01379 }
01380 
01381 static int sendBrakeRelease()
01382 {
01383   EMC_SPINDLE_BRAKE_RELEASE emc_spindle_brake_release_msg;
01384 
01385   emc_spindle_brake_release_msg.serial_number = ++emcCommandSerialNumber;
01386   emcCommandBuffer->write(emc_spindle_brake_release_msg);
01387   if (emcWaitType == EMC_WAIT_RECEIVED) {
01388     return emcCommandWaitReceived(emcCommandSerialNumber);
01389   }
01390   else if (emcWaitType == EMC_WAIT_DONE) {
01391     return emcCommandWaitDone(emcCommandSerialNumber);
01392   }
01393 
01394   return 0;
01395 }
01396 
01397 static int sendAbort()
01398 {
01399   EMC_TASK_ABORT task_abort_msg;
01400 
01401   task_abort_msg.serial_number = ++emcCommandSerialNumber;
01402   emcCommandBuffer->write(task_abort_msg);
01403   if (emcWaitType == EMC_WAIT_RECEIVED) {
01404     return emcCommandWaitReceived(emcCommandSerialNumber);
01405   }
01406   else if (emcWaitType == EMC_WAIT_DONE) {
01407     return emcCommandWaitDone(emcCommandSerialNumber);
01408   }
01409 
01410   return 0;
01411 }
01412 
01413 static int sendHome(int axis)
01414 {
01415   EMC_AXIS_HOME emc_axis_home_msg;
01416 
01417   emc_axis_home_msg.serial_number = ++emcCommandSerialNumber;
01418   emc_axis_home_msg.axis = axis;
01419   emcCommandBuffer->write(emc_axis_home_msg);
01420   if (emcWaitType == EMC_WAIT_RECEIVED) {
01421     return emcCommandWaitReceived(emcCommandSerialNumber);
01422   }
01423   else if (emcWaitType == EMC_WAIT_DONE) {
01424     return emcCommandWaitDone(emcCommandSerialNumber);
01425   }
01426 
01427   return 0;
01428 }
01429 
01430 static int sendFeedOverride(double override)
01431 {
01432   EMC_TRAJ_SET_SCALE emc_traj_set_scale_msg;
01433 
01434   if (override < 0.0) {
01435     override = 0.0;
01436   }
01437 
01438   emc_traj_set_scale_msg.serial_number = ++emcCommandSerialNumber;
01439   emc_traj_set_scale_msg.scale = override;
01440   emcCommandBuffer->write(emc_traj_set_scale_msg);
01441   if (emcWaitType == EMC_WAIT_RECEIVED) {
01442     return emcCommandWaitReceived(emcCommandSerialNumber);
01443   }
01444   else if (emcWaitType == EMC_WAIT_DONE) {
01445     return emcCommandWaitDone(emcCommandSerialNumber);
01446   }
01447 
01448   return 0;
01449 }
01450 
01451 static int sendTaskPlanInit()
01452 {
01453   EMC_TASK_PLAN_INIT task_plan_init_msg;
01454 
01455   task_plan_init_msg.serial_number = ++emcCommandSerialNumber;
01456   emcCommandBuffer->write(task_plan_init_msg);
01457   if (emcWaitType == EMC_WAIT_RECEIVED) {
01458     return emcCommandWaitReceived(emcCommandSerialNumber);
01459   }
01460   else if (emcWaitType == EMC_WAIT_DONE) {
01461     return emcCommandWaitDone(emcCommandSerialNumber);
01462   }
01463 
01464   return 0;
01465 }
01466 
01467 // saved value of last program opened
01468 static char lastProgramFile[EMC_TASK_FILENAME_LEN] = "";
01469 
01470 static int sendProgramOpen(char *program)
01471 {
01472   EMC_TASK_PLAN_OPEN emc_task_plan_open_msg;
01473 
01474   // save this to run again
01475   strcpy(lastProgramFile, program);
01476 
01477   emc_task_plan_open_msg.serial_number = ++emcCommandSerialNumber;
01478   strcpy(emc_task_plan_open_msg.file, program);
01479   emcCommandBuffer->write(emc_task_plan_open_msg);
01480   if (emcWaitType == EMC_WAIT_RECEIVED) {
01481     return emcCommandWaitReceived(emcCommandSerialNumber);
01482   }
01483   else if (emcWaitType == EMC_WAIT_DONE) {
01484     return emcCommandWaitDone(emcCommandSerialNumber);
01485   }
01486 
01487   return 0;
01488 }
01489 
01490 // programStartLine is the saved valued of the line that
01491 // sendProgramRun(int line) sent
01492 static int programStartLine = 0;
01493 
01494 static int sendProgramRun(int line)
01495 {
01496   EMC_TASK_PLAN_RUN emc_task_plan_run_msg;
01497 
01498   if (emcUpdateType == EMC_UPDATE_AUTO) {
01499     updateStatus();
01500   }
01501 
01502   // first reopen program if it's not open
01503   if (0 == emcStatus->task.file[0]) {
01504     // send a request to open last one
01505     sendProgramOpen(lastProgramFile);
01506   }
01507 
01508   // save the start line, to compare against active line later
01509   programStartLine = line;
01510 
01511   emc_task_plan_run_msg.serial_number = ++emcCommandSerialNumber;
01512   emc_task_plan_run_msg.line = line;
01513   emcCommandBuffer->write(emc_task_plan_run_msg);
01514   if (emcWaitType == EMC_WAIT_RECEIVED) {
01515     return emcCommandWaitReceived(emcCommandSerialNumber);
01516   }
01517   else if (emcWaitType == EMC_WAIT_DONE) {
01518     return emcCommandWaitDone(emcCommandSerialNumber);
01519   }
01520 
01521   return 0;
01522 }
01523 
01524 static int sendProgramPause()
01525 {
01526   EMC_TASK_PLAN_PAUSE emc_task_plan_pause_msg;
01527 
01528   emc_task_plan_pause_msg.serial_number = ++emcCommandSerialNumber;
01529   emcCommandBuffer->write(emc_task_plan_pause_msg);
01530   if (emcWaitType == EMC_WAIT_RECEIVED) {
01531     return emcCommandWaitReceived(emcCommandSerialNumber);
01532   }
01533   else if (emcWaitType == EMC_WAIT_DONE) {
01534     return emcCommandWaitDone(emcCommandSerialNumber);
01535   }
01536 
01537   return 0;
01538 }
01539 
01540 static int sendProgramResume()
01541 {
01542   EMC_TASK_PLAN_RESUME emc_task_plan_resume_msg;
01543 
01544   emc_task_plan_resume_msg.serial_number = ++emcCommandSerialNumber;
01545   emcCommandBuffer->write(emc_task_plan_resume_msg);
01546   if (emcWaitType == EMC_WAIT_RECEIVED) {
01547     return emcCommandWaitReceived(emcCommandSerialNumber);
01548   }
01549   else if (emcWaitType == EMC_WAIT_DONE) {
01550     return emcCommandWaitDone(emcCommandSerialNumber);
01551   }
01552 
01553   return 0;
01554 }
01555 
01556 static int sendProgramStep()
01557 {
01558   EMC_TASK_PLAN_STEP emc_task_plan_step_msg;
01559 
01560   emc_task_plan_step_msg.serial_number = ++emcCommandSerialNumber;
01561   emcCommandBuffer->write(emc_task_plan_step_msg);
01562   if (emcWaitType == EMC_WAIT_RECEIVED) {
01563     return emcCommandWaitReceived(emcCommandSerialNumber);
01564   }
01565   else if (emcWaitType == EMC_WAIT_DONE) {
01566     return emcCommandWaitDone(emcCommandSerialNumber);
01567   }
01568 
01569   return 0;
01570 }
01571 
01572 static int sendMdiCmd(char *mdi)
01573 {
01574   EMC_TASK_PLAN_EXECUTE emc_task_plan_execute_msg;
01575 
01576   strcpy(emc_task_plan_execute_msg.command, mdi);
01577   emc_task_plan_execute_msg.serial_number = ++emcCommandSerialNumber;
01578   emcCommandBuffer->write(emc_task_plan_execute_msg);
01579   if (emcWaitType == EMC_WAIT_RECEIVED) {
01580     return emcCommandWaitReceived(emcCommandSerialNumber);
01581   }
01582   else if (emcWaitType == EMC_WAIT_DONE) {
01583     return emcCommandWaitDone(emcCommandSerialNumber);
01584   }
01585 
01586   return 0;
01587 }
01588 
01589 static int sendLoadToolTable(const char *file)
01590 {
01591   EMC_TOOL_LOAD_TOOL_TABLE emc_tool_load_tool_table_msg;
01592 
01593   strcpy(emc_tool_load_tool_table_msg.file, file);
01594   emc_tool_load_tool_table_msg.serial_number = ++emcCommandSerialNumber;
01595   emcCommandBuffer->write(emc_tool_load_tool_table_msg);
01596   if (emcWaitType == EMC_WAIT_RECEIVED) {
01597     return emcCommandWaitReceived(emcCommandSerialNumber);
01598   }
01599   else if (emcWaitType == EMC_WAIT_DONE) {
01600     return emcCommandWaitDone(emcCommandSerialNumber);
01601   }
01602 
01603   return 0;
01604 }
01605 
01606 static int sendToolSetOffset(int tool, double length, double diameter)
01607 {
01608   EMC_TOOL_SET_OFFSET emc_tool_set_offset_msg;
01609 
01610   emc_tool_set_offset_msg.tool = tool;
01611   emc_tool_set_offset_msg.length = length;
01612   emc_tool_set_offset_msg.diameter = diameter;
01613   emc_tool_set_offset_msg.serial_number = ++emcCommandSerialNumber;
01614   emcCommandBuffer->write(emc_tool_set_offset_msg);
01615   if (emcWaitType == EMC_WAIT_RECEIVED) {
01616     return emcCommandWaitReceived(emcCommandSerialNumber);
01617   }
01618   else if (emcWaitType == EMC_WAIT_DONE) {
01619     return emcCommandWaitDone(emcCommandSerialNumber);
01620   }
01621 
01622   return 0;
01623 }
01624 
01625 static int sendAxisSetGains(int axis, double p, double i, double d, double ff0, double ff1, double ff2, double backlash, double bias, double maxError, double deadband)
01626 {
01627   EMC_AXIS_SET_GAINS emc_axis_set_gains_msg;
01628 
01629   emc_axis_set_gains_msg.axis = axis;
01630   emc_axis_set_gains_msg.p = p;
01631   emc_axis_set_gains_msg.i = i;
01632   emc_axis_set_gains_msg.d = d;
01633   emc_axis_set_gains_msg.ff0 = ff0;
01634   emc_axis_set_gains_msg.ff1 = ff1;
01635   emc_axis_set_gains_msg.ff2 = ff2;
01636   emc_axis_set_gains_msg.backlash = backlash;
01637   emc_axis_set_gains_msg.bias = bias;
01638   emc_axis_set_gains_msg.maxError = maxError;
01639   emc_axis_set_gains_msg.deadband = deadband;
01640   emc_axis_set_gains_msg.serial_number = ++emcCommandSerialNumber;
01641   emcCommandBuffer->write(emc_axis_set_gains_msg);
01642   if (emcWaitType == EMC_WAIT_RECEIVED) {
01643     return emcCommandWaitReceived(emcCommandSerialNumber);
01644   }
01645   else if (emcWaitType == EMC_WAIT_DONE) {
01646     return emcCommandWaitDone(emcCommandSerialNumber);
01647   }
01648 
01649   return 0;
01650 }
01651 
01652 static int sendAxisSetOutput(int axis, double output)
01653 {
01654   EMC_AXIS_SET_OUTPUT emc_axis_set_output_msg;
01655 
01656   emc_axis_set_output_msg.axis = axis;
01657   emc_axis_set_output_msg.output = output;
01658   emc_axis_set_output_msg.serial_number = ++emcCommandSerialNumber;
01659   emcCommandBuffer->write(emc_axis_set_output_msg);
01660   if (emcWaitType == EMC_WAIT_RECEIVED) {
01661     return emcCommandWaitReceived(emcCommandSerialNumber);
01662   }
01663   else if (emcWaitType == EMC_WAIT_DONE) {
01664     return emcCommandWaitDone(emcCommandSerialNumber);
01665   }
01666 
01667   return 0;
01668 }
01669 
01670 static int sendAxisEnable(int axis, int val)
01671 {
01672   EMC_AXIS_ENABLE emc_axis_enable_msg;
01673   EMC_AXIS_DISABLE emc_axis_disable_msg;
01674 
01675   if (val) {
01676     emc_axis_enable_msg.axis = axis;
01677     emc_axis_enable_msg.serial_number = ++emcCommandSerialNumber;
01678     emcCommandBuffer->write(emc_axis_enable_msg);
01679   }
01680   else {
01681     emc_axis_disable_msg.axis = axis;
01682     emc_axis_disable_msg.serial_number = ++emcCommandSerialNumber;
01683     emcCommandBuffer->write(emc_axis_disable_msg);
01684   }
01685   if (emcWaitType == EMC_WAIT_RECEIVED) {
01686     return emcCommandWaitReceived(emcCommandSerialNumber);
01687   }
01688   else if (emcWaitType == EMC_WAIT_DONE) {
01689     return emcCommandWaitDone(emcCommandSerialNumber);
01690   }
01691 
01692   return 0;
01693 }
01694 
01695 static int sendAxisLoadComp(int axis, const char *file)
01696 {
01697   EMC_AXIS_LOAD_COMP emc_axis_load_comp_msg;
01698 
01699   strcpy(emc_axis_load_comp_msg.file, file);
01700   emc_axis_load_comp_msg.serial_number = ++emcCommandSerialNumber;
01701   emcCommandBuffer->write(emc_axis_load_comp_msg);
01702   if (emcWaitType == EMC_WAIT_RECEIVED) {
01703     return emcCommandWaitReceived(emcCommandSerialNumber);
01704   }
01705   else if (emcWaitType == EMC_WAIT_DONE) {
01706     return emcCommandWaitDone(emcCommandSerialNumber);
01707   }
01708 
01709   return 0;
01710 }
01711 
01712 static int sendAxisAlter(int axis, double alter)
01713 {
01714   EMC_AXIS_ALTER emc_axis_alter_msg;
01715 
01716   emc_axis_alter_msg.alter = alter;
01717   emc_axis_alter_msg.serial_number = ++emcCommandSerialNumber;
01718   emcCommandBuffer->write(emc_axis_alter_msg);
01719   if (emcWaitType == EMC_WAIT_RECEIVED) {
01720     return emcCommandWaitReceived(emcCommandSerialNumber);
01721   }
01722   else if (emcWaitType == EMC_WAIT_DONE) {
01723     return emcCommandWaitDone(emcCommandSerialNumber);
01724   }
01725 
01726   return 0;
01727 }
01728 
01729 static int sendLogOpen(char *file, int type, int size, int skip, enum EMCLOG_TRIGGER_TYPE triggerType, enum EMCLOG_TRIGGER_VAR triggerVar, double triggerThreshold, int which)
01730 {
01731   EMC_LOG_OPEN emc_log_open_msg;
01732 
01733   strcpy(emc_log_open_msg.file, file);
01734   emc_log_open_msg.type = type;
01735   emc_log_open_msg.size = size;
01736   emc_log_open_msg.skip = skip;
01737   emc_log_open_msg.which = which;
01738   emc_log_open_msg.triggerType = triggerType;
01739   emc_log_open_msg.triggerVar = triggerVar;
01740   emc_log_open_msg.triggerThreshold = triggerThreshold;
01741   emc_log_open_msg.serial_number = ++emcCommandSerialNumber;
01742   emcCommandBuffer->write(emc_log_open_msg);
01743   if (emcWaitType == EMC_WAIT_RECEIVED) {
01744     return emcCommandWaitReceived(emcCommandSerialNumber);
01745   }
01746   else if (emcWaitType == EMC_WAIT_DONE) {
01747     return emcCommandWaitDone(emcCommandSerialNumber);
01748   }
01749 
01750   return 0;
01751 }
01752 
01753 static int sendLogStart()
01754 {
01755   EMC_LOG_START emc_log_start_msg;
01756 
01757   emc_log_start_msg.serial_number = ++emcCommandSerialNumber;
01758   emcCommandBuffer->write(emc_log_start_msg);
01759   if (emcWaitType == EMC_WAIT_RECEIVED) {
01760     return emcCommandWaitReceived(emcCommandSerialNumber);
01761   }
01762   else if (emcWaitType == EMC_WAIT_DONE) {
01763     return emcCommandWaitDone(emcCommandSerialNumber);
01764   }
01765 
01766   return 0;
01767 }
01768 
01769 static int sendLogStop()
01770 {
01771   EMC_LOG_STOP emc_log_stop_msg;
01772 
01773   emc_log_stop_msg.serial_number = ++emcCommandSerialNumber;
01774   emcCommandBuffer->write(emc_log_stop_msg);
01775   if (emcWaitType == EMC_WAIT_RECEIVED) {
01776     return emcCommandWaitReceived(emcCommandSerialNumber);
01777   }
01778   else if (emcWaitType == EMC_WAIT_DONE) {
01779     return emcCommandWaitDone(emcCommandSerialNumber);
01780   }
01781 
01782   return 0;
01783 };
01784 
01785 static int sendLogClose()
01786 {
01787   EMC_LOG_CLOSE emc_log_close_msg;
01788 
01789   emc_log_close_msg.serial_number = ++emcCommandSerialNumber;
01790   emcCommandBuffer->write(emc_log_close_msg);
01791   if (emcWaitType == EMC_WAIT_RECEIVED) {
01792     return emcCommandWaitReceived(emcCommandSerialNumber);
01793   }
01794   else if (emcWaitType == EMC_WAIT_DONE) {
01795     return emcCommandWaitDone(emcCommandSerialNumber);
01796   }
01797 
01798   return 0;
01799 }
01800 
01801 static int sendSetTeleopEnable(int enable)
01802 {
01803   EMC_TRAJ_SET_TELEOP_ENABLE emc_set_teleop_enable_msg;
01804 
01805   emc_set_teleop_enable_msg.enable = enable;
01806   emc_set_teleop_enable_msg.serial_number = ++emcCommandSerialNumber;
01807   emcCommandBuffer->write(emc_set_teleop_enable_msg);
01808   if (emcWaitType == EMC_WAIT_RECEIVED) {
01809     return emcCommandWaitReceived(emcCommandSerialNumber);
01810   }
01811   else if (emcWaitType == EMC_WAIT_DONE) {
01812     return emcCommandWaitDone(emcCommandSerialNumber);
01813   }
01814 
01815   return 0;
01816 }
01817 
01818 static int sendSetProbeIndex(int index)
01819 {
01820   EMC_TRAJ_SET_PROBE_INDEX emc_set_probe_index_msg;
01821 
01822   emc_set_probe_index_msg.index = index;
01823   emc_set_probe_index_msg.serial_number = ++emcCommandSerialNumber;
01824   emcCommandBuffer->write(emc_set_probe_index_msg);
01825   if (emcWaitType == EMC_WAIT_RECEIVED) {
01826     return emcCommandWaitReceived(emcCommandSerialNumber);
01827   }
01828   else if (emcWaitType == EMC_WAIT_DONE) {
01829     return emcCommandWaitDone(emcCommandSerialNumber);
01830   }
01831 
01832   return 0;
01833 }
01834 
01835 static int sendSetProbePolarity(int polarity)
01836 {
01837   EMC_TRAJ_SET_PROBE_POLARITY emc_set_probe_polarity_msg;
01838 
01839   emc_set_probe_polarity_msg.serial_number = ++emcCommandSerialNumber;
01840   emc_set_probe_polarity_msg.polarity = polarity;
01841   emcCommandBuffer->write(emc_set_probe_polarity_msg);
01842   if (emcWaitType == EMC_WAIT_RECEIVED) {
01843     return emcCommandWaitReceived(emcCommandSerialNumber);
01844   }
01845   else if (emcWaitType == EMC_WAIT_DONE) {
01846     return emcCommandWaitDone(emcCommandSerialNumber);
01847   }
01848 
01849   return 0;
01850 }
01851 
01852 static int sendClearProbeTrippedFlag()
01853 {
01854   EMC_TRAJ_CLEAR_PROBE_TRIPPED_FLAG emc_clear_probe_tripped_flag_msg;
01855 
01856   emc_clear_probe_tripped_flag_msg.serial_number = ++emcCommandSerialNumber;
01857   emcCommandBuffer->write(emc_clear_probe_tripped_flag_msg);
01858   if (emcWaitType == EMC_WAIT_RECEIVED) {
01859     return emcCommandWaitReceived(emcCommandSerialNumber);
01860   }
01861   else if (emcWaitType == EMC_WAIT_DONE) {
01862     return emcCommandWaitDone(emcCommandSerialNumber);
01863   }
01864 
01865   return 0;
01866 }
01867 
01868 static int sendProbe(double x, double y, double z)
01869 {
01870   EMC_TRAJ_PROBE emc_probe_msg;
01871 
01872   emc_probe_msg.pos.tran.x = x;
01873   emc_probe_msg.pos.tran.y = y;
01874   emc_probe_msg.pos.tran.z = z;
01875 
01876   emc_probe_msg.serial_number = ++emcCommandSerialNumber;
01877   emcCommandBuffer->write(emc_probe_msg);
01878   if (emcWaitType == EMC_WAIT_RECEIVED) {
01879     return emcCommandWaitReceived(emcCommandSerialNumber);
01880   }
01881   else if (emcWaitType == EMC_WAIT_DONE) {
01882     return emcCommandWaitDone(emcCommandSerialNumber);
01883   }
01884 
01885   return 0;
01886 }
01887 
01888 /* EMC command functions */
01889 
01890 static int emc_plat(ClientData clientdata,
01891                     Tcl_Interp *interp,
01892                     int objc,
01893                     Tcl_Obj *CONST objv[])
01894 {
01895   if (objc == 1) {
01896     Tcl_SetResult(interp, PLATNAME, TCL_VOLATILE);
01897     return TCL_OK;
01898   }
01899 
01900   Tcl_SetResult(interp, "emc_plat: need no args", TCL_VOLATILE);
01901   return TCL_ERROR;
01902 }
01903 
01904 static int emc_ini(ClientData clientdata,
01905                    Tcl_Interp *interp,
01906                    int objc,
01907                    Tcl_Obj *CONST objv[])
01908 {
01909   INIFILE inifile;
01910   const char *inistring;
01911   const char *varstr, *secstr, *defaultstr;
01912   defaultstr = 0;
01913 
01914   if (objc != 3 && objc != 4 ) {
01915     Tcl_SetResult(interp, "emc_ini: need 'var' and 'section'", TCL_VOLATILE);
01916     return TCL_ERROR;
01917   }
01918 
01919   
01920   // open it
01921   if (-1 == inifile.open(EMC_INIFILE)) {
01922     return TCL_OK;
01923   }
01924 
01925   varstr = Tcl_GetStringFromObj(objv[1], 0);
01926   secstr = Tcl_GetStringFromObj(objv[2], 0);
01927 
01928   if(objc == 4) {
01929   defaultstr = Tcl_GetStringFromObj(objv[3], 0);
01930   }
01931 
01932   if (NULL == (inistring = inifile.find(varstr, secstr))) {
01933     if(defaultstr != 0)
01934       {
01935         Tcl_SetResult(interp, (char *) defaultstr, TCL_VOLATILE);
01936       }
01937     return TCL_OK;
01938   }
01939 
01940   Tcl_SetResult(interp, (char *) inistring, TCL_VOLATILE);
01941 
01942   // close it
01943   inifile.close();
01944 
01945   return TCL_OK;
01946 }
01947 
01948 static int emc_debug(ClientData clientdata,
01949                      Tcl_Interp *interp,
01950                      int objc,
01951                      Tcl_Obj *CONST objv[])
01952 {
01953   Tcl_Obj *debug_obj;
01954   int debug;
01955 
01956   if (emcUpdateType == EMC_UPDATE_AUTO) {
01957     updateStatus();
01958   }
01959 
01960   if (objc == 1) {
01961     // no arg-- return current value
01962     debug_obj = Tcl_NewIntObj(emcStatus->debug);
01963     Tcl_SetObjResult(interp, debug_obj);
01964     return TCL_OK;
01965   }
01966 
01967   if (objc == 2) {
01968     if (0 != Tcl_GetIntFromObj(0, objv[1], &debug)) {
01969       Tcl_SetResult(interp, "emc_debug: need debug level as integer", TCL_VOLATILE);
01970       return TCL_ERROR;
01971   }
01972     sendDebug(debug);
01973     EMC_DEBUG = debug;
01974     return TCL_OK;
01975   }
01976 
01977   // wrong number of args
01978   Tcl_SetResult(interp, "emc_debug: need zero or one arg", TCL_VOLATILE);
01979   return TCL_ERROR;
01980 }
01981 
01982 static int emc_set_wait(ClientData clientdata,
01983                         Tcl_Interp *interp,
01984                         int objc,
01985                         Tcl_Obj *CONST objv[])
01986 {
01987   char *objstr;
01988 
01989   if (objc == 1) {
01990     switch(emcWaitType)
01991       {
01992       case EMC_WAIT_NONE:
01993         Tcl_SetResult(interp,"none",TCL_VOLATILE);
01994         break;
01995       case EMC_WAIT_RECEIVED:
01996         Tcl_SetResult(interp,"received",TCL_VOLATILE);
01997         break;
01998       case EMC_WAIT_DONE:
01999         Tcl_SetResult(interp,"done",TCL_VOLATILE);
02000         break;
02001       default:
02002         Tcl_SetResult(interp,"(invalid)",TCL_VOLATILE);
02003         break;
02004       }
02005     return TCL_OK;
02006   }
02007 
02008   if (objc == 2) {
02009     objstr = Tcl_GetStringFromObj(objv[1], 0);
02010     if (! strcmp(objstr, "none")) {
02011       emcWaitType = EMC_WAIT_NONE;
02012       return TCL_OK;
02013     }
02014     if (! strcmp(objstr, "received")) {
02015       emcWaitType = EMC_WAIT_RECEIVED;
02016       return TCL_OK;
02017     }
02018     if (! strcmp(objstr, "done")) {
02019       emcWaitType = EMC_WAIT_DONE;
02020       return TCL_OK;
02021     }
02022   }
02023 
02024   Tcl_SetResult(interp, "emc_set_wait: need 'none', 'received', 'done', or no args", TCL_VOLATILE);
02025   return TCL_ERROR;
02026 }
02027 
02028 static int emc_wait(ClientData clientdata,
02029                     Tcl_Interp *interp,
02030                     int objc,
02031                     Tcl_Obj *CONST objv[])
02032 {
02033   char *objstr;
02034 
02035   if (objc == 2) {
02036     objstr = Tcl_GetStringFromObj(objv[1], 0);
02037     if (! strcmp(objstr, "received")) {
02038       if (0 != emcCommandWaitReceived(emcCommandSerialNumber)) {
02039         Tcl_SetResult(interp, "timeout", TCL_VOLATILE);
02040       }
02041       return TCL_OK;
02042     }
02043     if (! strcmp(objstr, "done")) {
02044       if (0 != emcCommandWaitDone(emcCommandSerialNumber)) {
02045         Tcl_SetResult(interp, "timeout", TCL_VOLATILE);
02046       }
02047       return TCL_OK;
02048     }
02049   }
02050 
02051   Tcl_SetResult(interp, "emc_wait: need 'received' or 'done'", TCL_VOLATILE);
02052   return TCL_ERROR;
02053 }
02054 
02055 static int emc_set_timeout(ClientData clientdata,
02056                            Tcl_Interp *interp,
02057                            int objc,
02058                            Tcl_Obj *CONST objv[])
02059 {
02060   double timeout;
02061   Tcl_Obj *timeout_obj;
02062 
02063   if (objc == 1) {
02064     timeout_obj = Tcl_NewDoubleObj(emcTimeout);
02065     Tcl_SetObjResult(interp, timeout_obj);
02066     return TCL_OK;
02067   }
02068 
02069   if (objc == 2) {
02070     if (TCL_OK == Tcl_GetDoubleFromObj(0, objv[1], &timeout)) {
02071       emcTimeout = timeout;
02072       return TCL_OK;
02073     }
02074   }
02075 
02076   Tcl_SetResult(interp, "emc_set_timeout: need time as real number", TCL_VOLATILE);
02077   return TCL_ERROR;
02078 }
02079 
02080 static int emc_update(ClientData clientdata,
02081                       Tcl_Interp *interp,
02082                       int objc,
02083                       Tcl_Obj *CONST objv[])
02084 {
02085   char *objstr;
02086 
02087   if (objc == 1) {
02088     // no arg-- return status
02089     updateStatus();
02090     return TCL_OK;
02091   }
02092 
02093   if (objc == 2) {
02094     objstr = Tcl_GetStringFromObj(objv[1], 0);
02095     if (! strcmp(objstr, "none")) {
02096       emcUpdateType = EMC_UPDATE_NONE;
02097       return TCL_OK;
02098     }
02099     if (! strcmp(objstr, "auto")) {
02100       emcUpdateType = EMC_UPDATE_AUTO;
02101       return TCL_OK;
02102     }
02103   }
02104 
02105   return TCL_OK;
02106 }
02107 
02108 static int emc_time(ClientData clientdata,
02109                     Tcl_Interp *interp,
02110                     int objc,
02111                     Tcl_Obj *CONST objv[])
02112 {
02113   if (objc == 1) {
02114 #if defined(LINUX_KERNEL_2_2)
02115     // FIXME-- Linux 2.2 has gettimeofday() bug, so return 0
02116     Tcl_SetObjResult(interp, Tcl_NewDoubleObj(0.0));
02117 #else
02118     Tcl_SetObjResult(interp, Tcl_NewDoubleObj(etime()));
02119 #endif
02120     return TCL_OK;
02121   }
02122 
02123   Tcl_SetResult(interp, "emc_time: needs no arguments", TCL_VOLATILE);
02124   return TCL_ERROR;
02125 }
02126 
02127 static int emc_error(ClientData clientdata,
02128                      Tcl_Interp *interp,
02129                      int objc,
02130                      Tcl_Obj *CONST objv[])
02131 {
02132 
02133   if (objc == 1) {
02134     // get any new error, it's saved in global error_string[]
02135     if (0 != updateError()) {
02136       Tcl_SetResult(interp, "emc_error: bad status from EMC", TCL_VOLATILE);
02137       return TCL_ERROR;
02138     }
02139     // put error on result list
02140     if (error_string[0] == 0) {
02141       Tcl_SetResult(interp, "ok", TCL_VOLATILE);
02142     }
02143     else {
02144       Tcl_SetResult(interp, error_string, TCL_VOLATILE);
02145       error_string[0] = 0;
02146     }
02147     return TCL_OK;
02148   }
02149 
02150   Tcl_SetResult(interp, "emc_error: need no args", TCL_VOLATILE);
02151   return TCL_ERROR;
02152 }
02153 
02154 static int emc_operator_text(ClientData clientdata,
02155                              Tcl_Interp *interp,
02156                              int objc,
02157                              Tcl_Obj *CONST objv[])
02158 {
02159 
02160   if (objc == 1) {
02161     // get any new string, it's saved in global operator_text_string[]
02162     if (0 != updateError()) {
02163       Tcl_SetResult(interp, "emc_operator_text: bad status from EMC", TCL_VOLATILE);
02164       return TCL_ERROR;
02165     }
02166     // put error on result list
02167     if (operator_text_string[0] == 0) {
02168       Tcl_SetResult(interp, "ok", TCL_VOLATILE);
02169       operator_text_string[0] = 0;
02170     }
02171     else {
02172       Tcl_SetResult(interp, operator_text_string, TCL_VOLATILE);
02173     }
02174     return TCL_OK;
02175   }
02176 
02177   Tcl_SetResult(interp, "emc_operator_text: need no args", TCL_VOLATILE);
02178   return TCL_ERROR;
02179 }
02180 
02181 static int emc_operator_display(ClientData clientdata,
02182                                 Tcl_Interp *interp,
02183                                 int objc,
02184                                 Tcl_Obj *CONST objv[])
02185 {
02186 
02187   if (objc == 1) {
02188     // get any new string, it's saved in global operator_display_string[]
02189     if (0 != updateError()) {
02190       Tcl_SetResult(interp, "emc_operator_display: bad status from EMC", TCL_VOLATILE);
02191       return TCL_ERROR;
02192     }
02193     // put error on result list
02194     if (operator_display_string[0] == 0) {
02195       Tcl_SetResult(interp, "ok", TCL_VOLATILE);
02196     }
02197     else {
02198       Tcl_SetResult(interp, operator_display_string, TCL_VOLATILE);
02199       operator_display_string[0] = 0;
02200     }
02201     return TCL_OK;
02202   }
02203 
02204   Tcl_SetResult(interp, "emc_operator_display: need no args", TCL_VOLATILE);
02205   return TCL_ERROR;
02206 }
02207 
02208 static int emc_estop(ClientData clientdata,
02209                      Tcl_Interp *interp,
02210                      int objc,
02211                      Tcl_Obj *CONST objv[])
02212 {
02213   char *objstr;
02214 
02215   if (objc == 1) {
02216     // no arg-- return status
02217     if (emcUpdateType == EMC_UPDATE_AUTO) {
02218       updateStatus();
02219     }
02220     if (emcStatus->task.state == EMC_TASK_STATE_ESTOP) {
02221       Tcl_SetResult(interp,"on",TCL_VOLATILE);
02222     }
02223     else {
02224       Tcl_SetResult(interp,"off",TCL_VOLATILE);
02225     }
02226     return TCL_OK;
02227   }
02228 
02229   if (objc == 2) {
02230     objstr = Tcl_GetStringFromObj(objv[1], 0);
02231     if (! strcmp(objstr, "on")) {
02232       sendEstop();
02233       return TCL_OK;
02234     }
02235     if (! strcmp(objstr, "off")) {
02236       sendEstopReset();
02237       return TCL_OK;
02238     }
02239   }
02240 
02241   Tcl_SetResult(interp, "emc_estop: need 'on', 'off', or no args", TCL_VOLATILE);
02242   return TCL_ERROR;
02243 }
02244 
02245 static int emc_estop_in(ClientData clientdata,
02246                         Tcl_Interp *interp,
02247                         int objc,
02248                         Tcl_Obj *CONST objv[])
02249 {
02250   if (objc == 1) {
02251     // no arg-- return status
02252     if (emcUpdateType == EMC_UPDATE_AUTO) {
02253       updateStatus();
02254     }
02255     if(emcStatus->io.aux.estopIn == 0)
02256       {
02257         Tcl_SetResult(interp,"off",TCL_VOLATILE);
02258       }
02259     else
02260       {
02261         Tcl_SetResult(interp,"on",TCL_VOLATILE);
02262       }
02263     return TCL_OK;
02264   }
02265 
02266   Tcl_SetResult(interp, "emc_estop_in: need no args", TCL_VOLATILE);
02267   return TCL_ERROR;
02268 }
02269 
02270 static int emc_machine(ClientData clientdata,
02271                        Tcl_Interp *interp,
02272                        int objc,
02273                        Tcl_Obj *CONST objv[])
02274 {
02275   char *objstr;
02276 
02277   if (objc == 1) {
02278     // no arg-- return status
02279     if (emcUpdateType == EMC_UPDATE_AUTO) {
02280       updateStatus();
02281     }
02282     if(emcStatus->task.state == EMC_TASK_STATE_ON)
02283       {
02284         Tcl_SetResult(interp,"on",TCL_VOLATILE);
02285       }
02286     else
02287       {
02288         Tcl_SetResult(interp,"off",TCL_VOLATILE);
02289       }
02290     return TCL_OK;
02291   }
02292 
02293   if (objc == 2) {
02294     objstr = Tcl_GetStringFromObj(objv[1], 0);
02295     if (! strcmp(objstr, "on")) {
02296       sendMachineOn();
02297       return TCL_OK;
02298     }
02299     if (! strcmp(objstr, "off")) {
02300       sendMachineOff();
02301       return TCL_OK;
02302     }
02303   }
02304 
02305   Tcl_SetResult(interp, "emc_machine: need 'on', 'off', or no args", TCL_VOLATILE);
02306   return TCL_ERROR;
02307 }
02308 
02309 static int emc_mode(ClientData clientdata,
02310                     Tcl_Interp *interp,
02311                     int objc,
02312                     Tcl_Obj *CONST objv[])
02313 {
02314   char *objstr;
02315 
02316   if (objc == 1) {
02317     // no arg-- return status
02318     if (emcUpdateType == EMC_UPDATE_AUTO) {
02319       updateStatus();
02320     }
02321     switch(emcStatus->task.mode)
02322       {
02323       case EMC_TASK_MODE_MANUAL:
02324         Tcl_SetResult(interp,"manual",TCL_VOLATILE);
02325         break;
02326       case EMC_TASK_MODE_AUTO:
02327         Tcl_SetResult(interp,"auto",TCL_VOLATILE);
02328         break;
02329       case EMC_TASK_MODE_MDI:
02330         Tcl_SetResult(interp,"mdi",TCL_VOLATILE);
02331         break;
02332       default:
02333         Tcl_SetResult(interp,"?",TCL_VOLATILE);
02334         break;
02335       }
02336     return TCL_OK;
02337   }
02338 
02339   if (objc == 2) {
02340     objstr = Tcl_GetStringFromObj(objv[1], 0);
02341     if (! strcmp(objstr, "manual")) {
02342       sendManual();
02343       return TCL_OK;
02344     }
02345     if (! strcmp(objstr, "auto")) {
02346       sendAuto();
02347       return TCL_OK;
02348     }
02349     if (! strcmp(objstr, "mdi")) {
02350       sendMdi();
02351       return TCL_OK;
02352     }
02353   }
02354 
02355   Tcl_SetResult(interp, "emc_mode: need 'manual', 'auto', 'mdi', or no args", TCL_VOLATILE);
02356   return TCL_ERROR;
02357 }
02358 
02359 static int emc_mist(ClientData clientdata,
02360                     Tcl_Interp *interp,
02361                     int objc,
02362                     Tcl_Obj *CONST objv[])
02363 {
02364   char *objstr;
02365 
02366   if (objc == 1) {
02367     // no arg-- return status
02368     if (emcUpdateType == EMC_UPDATE_AUTO) {
02369       updateStatus();
02370     }
02371     if(emcStatus->io.coolant.mist == 1)
02372       {
02373         Tcl_SetResult(interp,"on",TCL_VOLATILE);
02374       }
02375     else
02376       {
02377         Tcl_SetResult(interp,"off",TCL_VOLATILE);
02378       }
02379     return TCL_OK;
02380   }
02381 
02382   if (objc == 2) {
02383     objstr = Tcl_GetStringFromObj(objv[1], 0);
02384     if (! strcmp(objstr, "on")) {
02385       sendMistOn();
02386       return TCL_OK;
02387     }
02388     if (! strcmp(objstr, "off")) {
02389       sendMistOff();
02390       return TCL_OK;
02391     }
02392   }
02393 
02394   Tcl_SetResult(interp, "emc_mist: need 'on', 'off', or no args", TCL_VOLATILE);
02395   return TCL_ERROR;
02396 }
02397 
02398 static int emc_flood(ClientData clientdata,
02399                      Tcl_Interp *interp,
02400                      int objc,
02401                      Tcl_Obj *CONST objv[])
02402 {
02403   char *objstr;
02404 
02405   if (objc == 1) {
02406     // no arg-- return status
02407     if (emcUpdateType == EMC_UPDATE_AUTO) {
02408       updateStatus();
02409     }
02410     if(emcStatus->io.coolant.flood == 1)
02411       {
02412         Tcl_SetResult(interp,"on",TCL_VOLATILE);
02413       }
02414     else
02415       {
02416         Tcl_SetResult(interp,"off",TCL_VOLATILE);
02417       }
02418     return TCL_OK;
02419   }
02420 
02421   if (objc == 2) {
02422     objstr = Tcl_GetStringFromObj(objv[1], 0);
02423     if (! strcmp(objstr, "on")) {
02424       sendFloodOn();
02425       return TCL_OK;
02426     }
02427     if (! strcmp(objstr, "off")) {
02428       sendFloodOff();
02429       return TCL_OK;
02430     }
02431   }
02432 
02433   Tcl_SetResult(interp, "emc_flood: need 'on', 'off', or no args", TCL_VOLATILE);
02434   return TCL_ERROR;
02435 }
02436 
02437 static int emc_lube(ClientData clientdata,
02438                     Tcl_Interp *interp,
02439                     int objc,
02440                     Tcl_Obj *CONST objv[])
02441 {
02442   char *objstr;
02443 
02444   if (objc == 1) {
02445     // no arg-- return status
02446     if (emcUpdateType == EMC_UPDATE_AUTO) {
02447       updateStatus();
02448     }
02449     if(emcStatus->io.lube.on == 0)
02450       {
02451         Tcl_SetResult(interp,"off",TCL_VOLATILE);
02452       }
02453     else
02454       {
02455         Tcl_SetResult(interp,"on",TCL_VOLATILE);
02456       }
02457     return TCL_OK;
02458   }
02459 
02460   if (objc == 2) {
02461     objstr = Tcl_GetStringFromObj(objv[1], 0);
02462     if (! strcmp(objstr, "on")) {
02463       sendLubeOn();
02464       return TCL_OK;
02465     }
02466     if (! strcmp(objstr, "off")) {
02467       sendLubeOff();
02468       return TCL_OK;
02469     }
02470   }
02471 
02472   Tcl_SetResult(interp, "emc_flood: need 'on', 'off', or no args", TCL_VOLATILE);
02473   return TCL_ERROR;
02474 }
02475 
02476 static int emc_lube_level(ClientData clientdata,
02477                           Tcl_Interp *interp,
02478                           int objc,
02479                           Tcl_Obj *CONST objv[])
02480 {
02481   if (objc == 1) {
02482     // no arg-- return status
02483     if (emcUpdateType == EMC_UPDATE_AUTO) {
02484       updateStatus();
02485     }
02486     if(emcStatus->io.lube.level == 0)
02487       {
02488         Tcl_SetResult(interp,"low",TCL_VOLATILE);
02489       }
02490     else
02491       {
02492         Tcl_SetResult(interp,"ok",TCL_VOLATILE);
02493       }
02494     return TCL_OK;
02495   }
02496 
02497   Tcl_SetResult(interp, "emc_lube_level: need no args", TCL_VOLATILE);
02498   return TCL_ERROR;
02499 }
02500 
02501 static int emc_spindle(ClientData clientdata,
02502                        Tcl_Interp *interp,
02503                        int objc,
02504                        Tcl_Obj *CONST objv[])
02505 {
02506   char *objstr;
02507 
02508   if (objc == 1) {
02509     // no arg-- return status
02510     if (emcUpdateType == EMC_UPDATE_AUTO) {
02511       updateStatus();
02512     }
02513     if(emcStatus->io.spindle.increasing > 0)
02514       {
02515         Tcl_SetResult(interp,"increase",TCL_VOLATILE);
02516       }
02517     else if( emcStatus->io.spindle.increasing < 0)
02518       {
02519         Tcl_SetResult(interp,"decrease",TCL_VOLATILE);
02520       }
02521     else if(emcStatus->io.spindle.direction > 0)
02522       {
02523         Tcl_SetResult(interp,"forward",TCL_VOLATILE);
02524       }
02525     else if(emcStatus->io.spindle.direction < 0)
02526       {
02527         Tcl_SetResult(interp,"reverse",TCL_VOLATILE);
02528       }
02529     else
02530       {
02531         Tcl_SetResult(interp,"off",TCL_VOLATILE);
02532       }
02533     return TCL_OK;
02534   }
02535 
02536   if (objc == 2) {
02537     objstr = Tcl_GetStringFromObj(objv[1], 0);
02538     if (! strcmp(objstr, "forward")) {
02539       sendSpindleForward();
02540       return TCL_OK;
02541     }
02542     if (! strcmp(objstr, "reverse")) {
02543       sendSpindleReverse();
02544       return TCL_OK;
02545     }
02546     if (! strcmp(objstr, "increase")) {
02547       sendSpindleIncrease();
02548       return TCL_OK;
02549     }
02550     if (! strcmp(objstr, "decrease")) {
02551       sendSpindleDecrease();
02552       return TCL_OK;
02553     }
02554     if (! strcmp(objstr, "constant")) {
02555       sendSpindleConstant();
02556       return TCL_OK;
02557     }
02558     if (! strcmp(objstr, "off")) {
02559       sendSpindleOff();
02560       return TCL_OK;
02561     }
02562   }
02563 
02564   Tcl_SetResult(interp, "emc_spindle: need 'on', 'off', or no args", TCL_VOLATILE);
02565   return TCL_ERROR;
02566 }
02567 
02568 static int emc_brake(ClientData clientdata,
02569                      Tcl_Interp *interp,
02570                      int objc,
02571                      Tcl_Obj *CONST objv[])
02572 {
02573   char *objstr;
02574 
02575   if (objc == 1) {
02576     // no arg-- return status
02577     if (emcUpdateType == EMC_UPDATE_AUTO) {
02578       updateStatus();
02579     }
02580     if(emcStatus->io.spindle.brake == 1)
02581       {
02582         Tcl_SetResult(interp,"on",TCL_VOLATILE);
02583       }
02584     else
02585       {
02586         Tcl_SetResult(interp,"off",TCL_VOLATILE);
02587       }
02588     return TCL_OK;
02589   }
02590 
02591   if (objc == 2) {
02592     objstr = Tcl_GetStringFromObj(objv[1], 0);
02593     if (! strcmp(objstr, "on")) {
02594       sendBrakeEngage();
02595       return TCL_OK;
02596     }
02597     if (! strcmp(objstr, "off")) {
02598       sendBrakeRelease();
02599       return TCL_OK;
02600     }
02601   }
02602 
02603   Tcl_SetResult(interp, "emc_brake: need 'on', 'off', or no args", TCL_VOLATILE);
02604   return TCL_ERROR;
02605 }
02606 
02607 static int emc_tool(ClientData clientdata,
02608                     Tcl_Interp *interp,
02609                     int objc,
02610                     Tcl_Obj *CONST objv[])
02611 {
02612   Tcl_Obj *toolobj;
02613 
02614   if (objc != 1) {
02615     Tcl_SetResult(interp, "emc_tool: need no args", TCL_VOLATILE);
02616     return TCL_ERROR;
02617   }
02618 
02619   if (emcUpdateType == EMC_UPDATE_AUTO) {
02620     updateStatus();
02621   }
02622 
02623   toolobj = Tcl_NewIntObj(emcStatus->io.tool.toolInSpindle);
02624 
02625   Tcl_SetObjResult(interp, toolobj);
02626   return TCL_OK;
02627 }
02628 
02629 static int emc_tool_offset(ClientData clientdata,
02630                            Tcl_Interp *interp,
02631                            int objc,
02632                            Tcl_Obj *CONST objv[])
02633 {
02634   Tcl_Obj *tlobj;
02635 
02636   if (objc != 1) {
02637     Tcl_SetResult(interp, "emc_tool_offset: need no args", TCL_VOLATILE);
02638     return TCL_ERROR;
02639   }
02640 
02641   if (emcUpdateType == EMC_UPDATE_AUTO) {
02642     updateStatus();
02643   }
02644 
02645   tlobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->task.toolOffset.tran.z));
02646 
02647   Tcl_SetObjResult(interp, tlobj);
02648   return TCL_OK;
02649 }
02650 
02651 static int emc_load_tool_table(ClientData clientdata,
02652                                Tcl_Interp *interp,
02653                                int objc,
02654                                Tcl_Obj *CONST objv[])
02655 {
02656   if (objc != 2) {
02657     Tcl_SetResult(interp, "emc_load_tool_table: need file", TCL_VOLATILE);
02658     return TCL_ERROR;
02659   }
02660 
02661   if (0 != sendLoadToolTable(Tcl_GetStringFromObj(objv[1], 0))) {
02662     Tcl_SetResult(interp, "emc_load_tool_table: can't open file", TCL_VOLATILE);
02663     return TCL_OK;
02664   }
02665 
02666   return TCL_OK;
02667 }
02668 
02669 static int emc_set_tool_offset(ClientData clientdata,
02670                                Tcl_Interp *interp,
02671                                int objc,
02672                                Tcl_Obj *CONST objv[])
02673 {
02674   int tool;
02675   double length;
02676   double diameter;
02677 
02678   if (objc != 4) {
02679     Tcl_SetResult(interp, "emc_set_tool_offset: need <tool> <length> <diameter>", TCL_VOLATILE);
02680     return TCL_ERROR;
02681   }
02682 
02683   if (0 != Tcl_GetIntFromObj(0, objv[1], &tool)) {
02684     Tcl_SetResult(interp, "emc_set_tool_offset: need tool as integer, 0..", TCL_VOLATILE);
02685     return TCL_ERROR;
02686   }
02687   if (0 != Tcl_GetDoubleFromObj(0, objv[2], &length)) {
02688     Tcl_SetResult(interp, "emc_set_tool_offset: need length as real number", TCL_VOLATILE);
02689     return TCL_ERROR;
02690   }
02691   if (0 != Tcl_GetDoubleFromObj(0, objv[3], &diameter)) {
02692     Tcl_SetResult(interp, "emc_set_tool_offset: need diameter as real number", TCL_VOLATILE);
02693     return TCL_ERROR;
02694   }
02695 
02696   if (0 != sendToolSetOffset(tool, length, diameter)) {
02697     Tcl_SetResult(interp, "emc_set_tool_offset: can't set it", TCL_VOLATILE);
02698     return TCL_OK;
02699   }
02700 
02701   return TCL_OK;
02702 }
02703 
02704 static int emc_abs_cmd_pos(ClientData clientdata,
02705                            Tcl_Interp *interp,
02706                            int objc,
02707                            Tcl_Obj *CONST objv[])
02708 {
02709   int axis;
02710   Tcl_Obj *posobj;
02711 
02712   if (objc != 2) {
02713     Tcl_SetResult(interp,
02714                   "emc_abs_cmd_pos: need exactly 1 non-negative integer",
02715                   TCL_VOLATILE);
02716     return TCL_ERROR;
02717   }
02718 
02719   if (emcUpdateType == EMC_UPDATE_AUTO) {
02720     updateStatus();
02721   }
02722 
02723   if (TCL_OK == Tcl_GetIntFromObj(0, objv[1], &axis)) {
02724     if (axis == 0) {
02725       posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.position.tran.x));
02726     }
02727     else if (axis == 1) {
02728       posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.position.tran.y));
02729     }
02730     else if (axis == 2) {
02731       posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.position.tran.z));
02732     }
02733     else {
02734       if (axis == 3) {
02735         posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.position.a));
02736       }
02737       else if (axis == 4) {
02738         posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.position.b));
02739       }
02740       else if (axis == 5) {
02741         posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.position.c));
02742       }
02743       else {
02744         posobj = Tcl_NewDoubleObj(0.0);
02745       }
02746     }
02747   }
02748   else {
02749     Tcl_SetResult(interp, "emc_abs_cmd_pos: bad integer argument", TCL_VOLATILE);
02750     return TCL_ERROR;
02751   }
02752 
02753   Tcl_SetObjResult(interp, posobj);
02754   return TCL_OK;
02755 }
02756 
02757 static int emc_abs_act_pos(ClientData clientdata,
02758                            Tcl_Interp *interp,
02759                            int objc,
02760                            Tcl_Obj *CONST objv[])
02761 {
02762   int axis;
02763   Tcl_Obj *posobj;
02764 
02765   if (objc != 2) {
02766     Tcl_SetResult(interp,
02767                   "emc_abs_act_pos: need exactly 1 non-negative integer",
02768                   TCL_VOLATILE);
02769     return TCL_ERROR;
02770   }
02771 
02772   if (emcUpdateType == EMC_UPDATE_AUTO) {
02773     updateStatus();
02774   }
02775 
02776   if (TCL_OK == Tcl_GetIntFromObj(0, objv[1], &axis)) {
02777     if (axis == 0) {
02778       posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.actualPosition.tran.x));
02779     }
02780     else if (axis == 1) {
02781       posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.actualPosition.tran.y));
02782     }
02783     else if (axis == 2) {
02784       posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.actualPosition.tran.z));
02785     }
02786     else {
02787       if (axis == 3) {
02788         posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.actualPosition.a));
02789       }
02790       else if (axis == 4) {
02791         posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.actualPosition.b));
02792       }
02793       else if (axis == 5) {
02794         posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.actualPosition.c));
02795       }
02796       else {
02797         posobj = Tcl_NewDoubleObj(0.0);
02798       }
02799     }
02800   }
02801   else {
02802     Tcl_SetResult(interp, "emc_abs_act_pos: bad integer argument", TCL_VOLATILE);
02803     return TCL_ERROR;
02804   }
02805 
02806   Tcl_SetObjResult(interp, posobj);
02807   return TCL_OK;
02808 }
02809 
02810 static int emc_rel_cmd_pos(ClientData clientdata,
02811                            Tcl_Interp *interp,
02812                            int objc,
02813                            Tcl_Obj *CONST objv[])
02814 {
02815   int axis;
02816   Tcl_Obj *posobj;
02817 
02818   if (objc != 2) {
02819     Tcl_SetResult(interp,
02820                   "emc_rel_cmd_pos: need exactly 1 non-negative integer",
02821                   TCL_VOLATILE);
02822     return TCL_ERROR;
02823   }
02824 
02825   if (emcUpdateType == EMC_UPDATE_AUTO) {
02826     updateStatus();
02827   }
02828 
02829   if (TCL_OK == Tcl_GetIntFromObj(0, objv[1], &axis)) {
02830     if (axis == 0) {
02831       posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.position.tran.x -
02832                                 emcStatus->task.origin.tran.x));
02833     }
02834     else if (axis == 1) {
02835       posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.position.tran.y -
02836                                 emcStatus->task.origin.tran.y));
02837     }
02838     else if (axis == 2) {
02839       posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.position.tran.z -
02840                                 emcStatus->task.origin.tran.z -
02841                                 emcStatus->task.toolOffset.tran.z));
02842     }
02843     else {
02844       // FIXME-- no rotational offsets yet
02845       if (axis == 3) {
02846         posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.position.a -
02847                                   emcStatus->task.origin.a));
02848       }
02849       else if (axis == 4) {
02850         posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.position.b -
02851                                   emcStatus->task.origin.b));
02852       }
02853       else if (axis == 5) {
02854         posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.position.c -
02855                                   emcStatus->task.origin.c));
02856       }
02857       else {
02858         posobj = Tcl_NewDoubleObj(0.0);
02859       }
02860     }
02861   }
02862   else {
02863     Tcl_SetResult(interp, "emc_rel_cmd_pos: bad integer argument", TCL_VOLATILE);
02864     return TCL_ERROR;
02865   }
02866 
02867   Tcl_SetObjResult(interp, posobj);
02868   return TCL_OK;
02869 }
02870 
02871 static int emc_rel_act_pos(ClientData clientdata,
02872                            Tcl_Interp *interp,
02873                            int objc,
02874                            Tcl_Obj *CONST objv[])
02875 {
02876   int axis;
02877   Tcl_Obj *posobj;
02878 
02879   if (objc != 2) {
02880     Tcl_SetResult(interp,
02881                   "emc_rel_act_pos: need exactly 1 non-negative integer",
02882                   TCL_VOLATILE);
02883     return TCL_ERROR;
02884   }
02885 
02886   if (emcUpdateType == EMC_UPDATE_AUTO) {
02887     updateStatus();
02888   }
02889 
02890   if (TCL_OK == Tcl_GetIntFromObj(0, objv[1], &axis)) {
02891     if (axis == 0) {
02892       posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.actualPosition.tran.x -
02893                                 emcStatus->task.origin.tran.x));
02894     }
02895     else if (axis == 1) {
02896       posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.actualPosition.tran.y -
02897                                 emcStatus->task.origin.tran.y));
02898     }
02899     else if (axis == 2) {
02900       posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.actualPosition.tran.z -
02901                                 emcStatus->task.origin.tran.z -
02902                                 emcStatus->task.toolOffset.tran.z));
02903     }
02904     else {
02905       if (axis == 3) {
02906         posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.actualPosition.a -
02907                                 emcStatus->task.origin.a));
02908       }
02909       else if (axis == 4) {
02910         posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.actualPosition.b -
02911                                 emcStatus->task.origin.b));
02912       }
02913       else if (axis == 5) {
02914         posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.actualPosition.c -
02915                                 emcStatus->task.origin.c));
02916       }
02917       else {
02918         posobj = Tcl_NewDoubleObj(0.0);
02919       }
02920     }
02921   }
02922   else {
02923     Tcl_SetResult(interp, "emc_rel_act_pos: bad integer argument", TCL_VOLATILE);
02924     return TCL_ERROR;
02925   }
02926 
02927   Tcl_SetObjResult(interp, posobj);
02928   return TCL_OK;
02929 }
02930 
02931 static int emc_joint_pos(ClientData clientdata,
02932                            Tcl_Interp *interp,
02933                            int objc,
02934                            Tcl_Obj *CONST objv[])
02935 {
02936   int axis;
02937   Tcl_Obj *posobj;
02938 
02939   if (objc != 2) {
02940     Tcl_SetResult(interp,
02941                   "emc_rel_act_pos: need exactly 1 non-negative integer",
02942                   TCL_VOLATILE);
02943     return TCL_ERROR;
02944   }
02945 
02946   if (emcUpdateType == EMC_UPDATE_AUTO) {
02947     updateStatus();
02948   }
02949 
02950   if (TCL_OK == Tcl_GetIntFromObj(0, objv[1], &axis)) {
02951     posobj = Tcl_NewDoubleObj(emcStatus->motion.axis[axis].input);
02952   }
02953   else {
02954     Tcl_SetResult(interp, "emc_joint_pos: bad integer argument", TCL_VOLATILE);
02955     return TCL_ERROR;
02956   }
02957 
02958   Tcl_SetObjResult(interp, posobj);
02959   return TCL_OK;
02960 }
02961 
02962 static int emc_pos_offset(ClientData clientdata,
02963                           Tcl_Interp *interp,
02964                           int objc,
02965                           Tcl_Obj *CONST objv[])
02966 {
02967   char string[256];
02968   Tcl_Obj *posobj;
02969 
02970   if (objc != 2) {
02971     Tcl_SetResult(interp,
02972                   "emc_pos_offset: need exactly 1 non-negative integer",
02973                   TCL_VOLATILE);
02974     return TCL_ERROR;
02975   }
02976 
02977   if (emcUpdateType == EMC_UPDATE_AUTO) {
02978     updateStatus();
02979   }
02980 
02981   strcpy(string, Tcl_GetStringFromObj(objv[1], 0));
02982 
02983   if (string[0] == 'X') {
02984     posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->task.origin.tran.x));
02985   }
02986   else if (string[0] == 'Y') {
02987     posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->task.origin.tran.y));
02988   }
02989   else if (string[0] == 'Z') {
02990     posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->task.origin.tran.z));
02991   }
02992   else if (string[0] == 'A') {
02993     posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->task.origin.a));
02994   }
02995   else if (string[0] == 'B') {
02996     posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->task.origin.b));
02997   }
02998   else if (string[0] == 'C') {
02999     posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->task.origin.c));
03000   }
03001   else {
03002     Tcl_SetResult(interp, "emc_pos_offset: bad integer argument", TCL_VOLATILE);
03003     return TCL_ERROR;
03004   }
03005 
03006   Tcl_SetObjResult(interp, posobj);
03007   return TCL_OK;
03008 }
03009 
03010 static int emc_joint_limit(ClientData clientdata,
03011                            Tcl_Interp *interp,
03012                            int objc,
03013                            Tcl_Obj *CONST objv[])
03014 {
03015   int joint;
03016 
03017   if (objc != 2) {
03018     Tcl_SetResult(interp,
03019                   "emc_joint_limit: need exactly 1 non-negative integer",
03020                   TCL_VOLATILE);
03021     return TCL_ERROR;
03022   }
03023 
03024   if (emcUpdateType == EMC_UPDATE_AUTO) {
03025     updateStatus();
03026   }
03027 
03028   if (TCL_OK == Tcl_GetIntFromObj(0, objv[1], &joint)) {
03029     if (joint < 0 ||
03030         joint >= EMC_AXIS_MAX) {
03031       Tcl_SetResult(interp,
03032                     "emc_joint_limit: joint out of bounds",
03033                     TCL_VOLATILE);
03034       return TCL_ERROR;
03035     }
03036 
03037     if (emcStatus->motion.axis[joint].minHardLimit) {
03038       Tcl_SetResult(interp, "minhard", TCL_VOLATILE);
03039       return TCL_OK;
03040     } else if (emcStatus->motion.axis[joint].minSoftLimit) {
03041       Tcl_SetResult(interp, "minsoft", TCL_VOLATILE);
03042       return TCL_OK;
03043     } else if (emcStatus->motion.axis[joint].maxSoftLimit) {
03044       Tcl_SetResult(interp, "maxsoft", TCL_VOLATILE);
03045       return TCL_OK;
03046     } else if (emcStatus->motion.axis[joint].maxHardLimit) {
03047       Tcl_SetResult(interp, "maxsoft", TCL_VOLATILE);
03048       return TCL_OK;
03049     } else {
03050       Tcl_SetResult(interp, "ok", TCL_VOLATILE);
03051       return TCL_OK;
03052     }
03053   }
03054 
03055   Tcl_SetResult(interp,
03056                 "emc_joint_limit: joint out of bounds",
03057                 TCL_VOLATILE);
03058   return TCL_ERROR;
03059 }
03060 
03061 static int emc_joint_fault(ClientData clientdata,
03062                            Tcl_Interp *interp,
03063                            int objc,
03064                            Tcl_Obj *CONST objv[])
03065 {
03066   int joint;
03067 
03068   if (objc != 2) {
03069     Tcl_SetResult(interp,
03070                   "emc_joint_fault: need exactly 1 non-negative integer",
03071                   TCL_VOLATILE);
03072     return TCL_ERROR;
03073   }
03074 
03075   if (emcUpdateType == EMC_UPDATE_AUTO) {
03076     updateStatus();
03077   }
03078 
03079   if (TCL_OK == Tcl_GetIntFromObj(0, objv[1], &joint)) {
03080     if (joint < 0 ||
03081         joint >= EMC_AXIS_MAX) {
03082       Tcl_SetResult(interp,
03083                     "emc_joint_fault: joint out of bounds",
03084                     TCL_VOLATILE);
03085       return TCL_ERROR;
03086     }
03087 
03088     if (emcStatus->motion.axis[joint].fault) {
03089       Tcl_SetResult(interp, "fault", TCL_VOLATILE);
03090       return TCL_OK;
03091     } else {
03092       Tcl_SetResult(interp, "ok", TCL_VOLATILE);
03093       return TCL_OK;
03094     }
03095   }
03096 
03097   Tcl_SetResult(interp,
03098                 "emc_joint_fault: joint out of bounds",
03099                 TCL_VOLATILE);
03100   return TCL_ERROR;
03101 }
03102 
03103 static int emc_override_limit(ClientData clientdata,
03104                               Tcl_Interp *interp,
03105                               int objc,
03106                               Tcl_Obj *CONST objv[])
03107 {
03108   Tcl_Obj *obj;
03109   int on;
03110 
03111   if (objc == 1) {
03112     // no arg-- return status
03113     if (emcUpdateType == EMC_UPDATE_AUTO) {
03114       updateStatus();
03115     }
03116     // motion overrides all axes at same time, so just reference index 0
03117     obj = Tcl_NewIntObj(emcStatus->motion.axis[0].overrideLimits);
03118     Tcl_SetObjResult(interp, obj);
03119     return TCL_OK;
03120   }
03121 
03122   if (objc == 2) {
03123     if (TCL_OK == Tcl_GetIntFromObj(0, objv[1], &on)) {
03124       if (on) {
03125         if (0 != sendOverrideLimits(0)) {
03126           Tcl_SetResult(interp, "emc_override_limit: can't send command", TCL_VOLATILE);
03127           return TCL_OK;
03128         }
03129       }
03130       else {
03131         if (0 != sendOverrideLimits(-1)) {
03132           Tcl_SetResult(interp, "emc_override_limit: can't send command", TCL_VOLATILE);
03133           return TCL_OK;
03134         }
03135       }
03136       return TCL_OK;
03137     }
03138     else {
03139       Tcl_SetResult(interp, "emc_override_limit: need 0 or 1", TCL_VOLATILE);
03140       return TCL_ERROR;
03141     }
03142   }
03143 
03144   Tcl_SetResult(interp, "emc_override_limit: need no args, 0 or 1", TCL_VOLATILE);
03145   return TCL_ERROR;
03146 }
03147 
03148 static int emc_joint_homed(ClientData clientdata,
03149                            Tcl_Interp *interp,
03150                            int objc,
03151                            Tcl_Obj *CONST objv[])
03152 {
03153   int joint;
03154 
03155   if (objc != 2) {
03156     Tcl_SetResult(interp,
03157                   "emc_joint_homed: need exactly 1 non-negative integer",
03158                   TCL_VOLATILE);
03159     return TCL_ERROR;
03160   }
03161 
03162   if (emcUpdateType == EMC_UPDATE_AUTO) {
03163     updateStatus();
03164   }
03165 
03166   if (TCL_OK == Tcl_GetIntFromObj(0, objv[1], &joint)) {
03167     if (joint < 0 ||
03168         joint >= EMC_AXIS_MAX) {
03169       Tcl_SetResult(interp,
03170                     "emc_joint_homed: joint out of bounds",
03171                     TCL_VOLATILE);
03172       return TCL_ERROR;
03173     }
03174 
03175     if (emcStatus->motion.axis[joint].homed) {
03176       Tcl_SetResult(interp, "homed", TCL_VOLATILE);
03177       return TCL_OK;
03178     } else {
03179       Tcl_SetResult(interp, "not", TCL_VOLATILE);
03180       return TCL_OK;
03181     }
03182   }
03183 
03184   Tcl_SetResult(interp,
03185                 "emc_joint_homed: joint out of bounds",
03186                 TCL_VOLATILE);
03187   return TCL_ERROR;
03188 }
03189 
03190 static int emc_mdi(ClientData clientdata,
03191                    Tcl_Interp *interp,
03192                    int objc,
03193                    Tcl_Obj *CONST objv[])
03194 {
03195   char string[256];
03196   int t;
03197 
03198   if (objc < 2) {
03199     Tcl_SetResult(interp, "emc_mdi: need command", TCL_VOLATILE);
03200     return TCL_ERROR;
03201   }
03202 
03203   // bug-- check for string overflow
03204   strcpy(string, Tcl_GetStringFromObj(objv[1], 0));
03205   for (t = 2; t < objc; t++) {
03206     strcat(string, " ");
03207     strcat(string, Tcl_GetStringFromObj(objv[t], 0));
03208   }
03209 
03210   if (0 != sendMdiCmd(string)) {
03211     Tcl_SetResult(interp, "emc_mdi: error executing command", TCL_VOLATILE);
03212     return TCL_OK;
03213   }
03214 
03215   return TCL_OK;
03216 }
03217 
03218 static int emc_home(ClientData clientdata,
03219                     Tcl_Interp *interp,
03220                     int objc,
03221                     Tcl_Obj *CONST objv[])
03222 {
03223   int axis;
03224 
03225   if (objc != 2) {
03226     Tcl_SetResult(interp, "emc_home: need axis", TCL_VOLATILE);
03227     return TCL_ERROR;
03228   }
03229 
03230   if (TCL_OK == Tcl_GetIntFromObj(0, objv[1], &axis)) {
03231     sendHome(axis);
03232     return TCL_OK;
03233   }
03234 
03235   Tcl_SetResult(interp, "emc_home: need axis as integer, 0..", TCL_VOLATILE);
03236   return TCL_ERROR;
03237 }
03238 
03239 static int emc_jog_stop(ClientData clientdata,
03240                         Tcl_Interp *interp,
03241                         int objc,
03242                         Tcl_Obj *CONST objv[])
03243 {
03244   int axis;
03245 
03246   if (objc != 2) {
03247     Tcl_SetResult(interp, "emc_jog_stop: need axis", TCL_VOLATILE);
03248     return TCL_ERROR;
03249   }
03250 
03251   if (0 != Tcl_GetIntFromObj(0, objv[1], &axis)) {
03252     Tcl_SetResult(interp, "emc_jog_stop: need axis as integer, 0..", TCL_VOLATILE);
03253     return TCL_ERROR;
03254   }
03255 
03256   if (0 != sendJogStop(axis)) {
03257     Tcl_SetResult(interp, "emc_jog_stop: can't send jog stop msg", TCL_VOLATILE);
03258     return TCL_OK;
03259   }
03260 
03261   return TCL_OK;
03262 }
03263 
03264 static int emc_jog(ClientData clientdata,
03265                    Tcl_Interp *interp,
03266                    int objc,
03267                    Tcl_Obj *CONST objv[])
03268 {
03269   int axis;
03270   double speed;
03271 
03272   if (objc != 3) {
03273     Tcl_SetResult(interp, "emc_jog: need axis and speed", TCL_VOLATILE);
03274     return TCL_ERROR;
03275   }
03276 
03277   if (0 != Tcl_GetIntFromObj(0, objv[1], &axis)) {
03278     Tcl_SetResult(interp, "emc_jog: need axis as integer, 0..", TCL_VOLATILE);
03279     return TCL_ERROR;
03280   }
03281   if (0 != Tcl_GetDoubleFromObj(0, objv[2], &speed)) {
03282     Tcl_SetResult(interp, "emc_jog: need speed as real number", TCL_VOLATILE);
03283     return TCL_ERROR;
03284   }
03285 
03286   if (0 != sendJogCont(axis, speed)) {
03287     Tcl_SetResult(interp, "emc_jog: can't jog", TCL_VOLATILE);
03288     return TCL_OK;
03289   }
03290 
03291   return TCL_OK;
03292 }
03293 
03294 static int emc_jog_incr(ClientData clientdata,
03295                         Tcl_Interp *interp,
03296                         int objc,
03297                         Tcl_Obj *CONST objv[])
03298 {
03299   int axis;
03300   double speed;
03301   double incr;
03302 
03303   if (objc != 4) {
03304     Tcl_SetResult(interp, "emc_jog_incr: need axis, speed, and increment", TCL_VOLATILE);
03305     return TCL_ERROR;
03306   }
03307 
03308   if (0 != Tcl_GetIntFromObj(0, objv[1], &axis)) {
03309     Tcl_SetResult(interp, "emc_jog_incr: need axis as integer, 0..", TCL_VOLATILE);
03310     return TCL_ERROR;
03311   }
03312   if (0 != Tcl_GetDoubleFromObj(0, objv[2], &speed)) {
03313     Tcl_SetResult(interp, "emc_jog_incr: need speed as real number", TCL_VOLATILE);
03314     return TCL_ERROR;
03315   }
03316   if (0 != Tcl_GetDoubleFromObj(0, objv[3], &incr)) {
03317     Tcl_SetResult(interp, "emc_jog_incr: need increment as real number", TCL_VOLATILE);
03318     return TCL_ERROR;
03319   }
03320 
03321   if (0 != sendJogIncr(axis, speed, incr)) {
03322     Tcl_SetResult(interp, "emc_jog_incr: can't jog", TCL_VOLATILE);
03323     return TCL_OK;
03324   }
03325 
03326   return TCL_OK;
03327 }
03328 
03329 static int emc_feed_override(ClientData clientdata,
03330                              Tcl_Interp *interp,
03331                              int objc,
03332                              Tcl_Obj *CONST objv[])
03333 {
03334   Tcl_Obj *feedobj;
03335   int percent;
03336 
03337   if (objc == 1) {
03338     // no arg-- return status
03339     if (emcUpdateType == EMC_UPDATE_AUTO) {
03340       updateStatus();
03341     }
03342     feedobj = Tcl_NewIntObj((int) (emcStatus->motion.traj.scale * 100.0 + 0.5));
03343     Tcl_SetObjResult(interp, feedobj);
03344     return TCL_OK;
03345   }
03346 
03347   if (objc != 2) {
03348     Tcl_SetResult(interp, "emc_feed_override: need percent", TCL_VOLATILE);
03349     return TCL_ERROR;
03350   }
03351 
03352   if (TCL_OK == Tcl_GetIntFromObj(0, objv[1], &percent)) {
03353     sendFeedOverride(((double) percent) / 100.0);
03354     return TCL_OK;
03355   }
03356 
03357   Tcl_SetResult(interp, "emc_feed_override: need percent", TCL_VOLATILE);
03358   return TCL_ERROR;
03359 }
03360 
03361 static int emc_task_plan_init(ClientData clientdata,
03362                               Tcl_Interp *interp,
03363                               int objc,
03364                               Tcl_Obj *CONST objv[])
03365 {
03366   if (0 != sendTaskPlanInit()) {
03367     Tcl_SetResult(interp, "emc_task_plan_init: can't init interpreter", TCL_VOLATILE);
03368     return TCL_OK;
03369   }
03370 
03371   return TCL_OK;
03372 }
03373 
03374 static int emc_open(ClientData clientdata,
03375                     Tcl_Interp *interp,
03376                     int objc,
03377                     Tcl_Obj *CONST objv[])
03378 {
03379   if (objc != 2) {
03380     Tcl_SetResult(interp, "emc_open: need file", TCL_VOLATILE);
03381     return TCL_ERROR;
03382   }
03383 
03384   if (0 != sendProgramOpen(Tcl_GetStringFromObj(objv[1], 0))) {
03385     Tcl_SetResult(interp, "emc_open: can't open file", TCL_VOLATILE);
03386     return TCL_OK;
03387   }
03388 
03389   return TCL_OK;
03390 }
03391 
03392 static int emc_run(ClientData clientdata,
03393                    Tcl_Interp *interp,
03394                    int objc,
03395                    Tcl_Obj *CONST objv[])
03396 {
03397   int line;
03398 
03399   if (objc == 1) {
03400     if (0 != sendProgramRun(0)) {
03401       Tcl_SetResult(interp, "emc_run: can't execute program", TCL_VOLATILE);
03402       return TCL_OK;
03403     }
03404   }
03405 
03406   if (objc == 2) {
03407     if (0 != Tcl_GetIntFromObj(0, objv[1], &line)) {
03408       Tcl_SetResult(interp, "emc_run: need integer start line", TCL_VOLATILE);
03409       return TCL_ERROR;
03410     }
03411     if (0 != sendProgramRun(line)) {
03412       Tcl_SetResult(interp, "emc_run: can't execute program", TCL_VOLATILE);
03413       return TCL_OK;
03414     }
03415   }
03416 
03417   return TCL_OK;
03418 }
03419 
03420 static int emc_pause(ClientData clientdata,
03421                      Tcl_Interp *interp,
03422                      int objc,
03423                      Tcl_Obj *CONST objv[])
03424 {
03425   if (0 != sendProgramPause()) {
03426     Tcl_SetResult(interp, "emc_pause: can't pause program", TCL_VOLATILE);
03427     return TCL_OK;
03428   }
03429 
03430   return TCL_OK;
03431 }
03432 
03433 static int emc_resume(ClientData clientdata,
03434                       Tcl_Interp *interp,
03435                       int objc,
03436                       Tcl_Obj *CONST objv[])
03437 {
03438   if (0 != sendProgramResume()) {
03439     Tcl_SetResult(interp, "emc_resume: can't resume program", TCL_VOLATILE);
03440     return TCL_OK;
03441   }
03442 
03443   return TCL_OK;
03444 }
03445 
03446 static int emc_step(ClientData clientdata,
03447                     Tcl_Interp *interp,
03448                     int objc,
03449                     Tcl_Obj *CONST objv[])
03450 {
03451   if (0 != sendProgramStep()) {
03452     Tcl_SetResult(interp, "emc_step: can't step program", TCL_VOLATILE);
03453     return TCL_OK;
03454   }
03455 
03456   return TCL_OK;
03457 }
03458 
03459 static int emc_abort(ClientData clientdata,
03460                      Tcl_Interp *interp,
03461                      int objc,
03462                      Tcl_Obj *CONST objv[])
03463 {
03464   if (0 != sendAbort()) {
03465     Tcl_SetResult(interp, "emc_abort: can't execute program", TCL_VOLATILE);
03466     return TCL_OK;
03467   }
03468 
03469   return TCL_OK;
03470 }
03471 
03472 static int emc_program(ClientData clientdata,
03473                        Tcl_Interp *interp,
03474                        int objc,
03475                        Tcl_Obj *CONST objv[])
03476 {
03477   if (objc != 1) {
03478     Tcl_SetResult(interp, "emc_program: need no args", TCL_VOLATILE);
03479     return TCL_ERROR;
03480   }
03481 
03482   if (emcUpdateType == EMC_UPDATE_AUTO) {
03483     updateStatus();
03484   }
03485 
03486   if (0 != emcStatus->task.file[0]) {
03487     Tcl_SetResult(interp, emcStatus->task.file, TCL_VOLATILE);
03488     return TCL_OK;
03489   }
03490 
03491   Tcl_SetResult(interp, "none", TCL_VOLATILE);
03492   return TCL_OK;
03493 }
03494 
03495 static int emc_program_status(ClientData clientdata,
03496                               Tcl_Interp *interp,
03497                               int objc,
03498                               Tcl_Obj *CONST objv[])
03499 {
03500   if (objc != 1) {
03501     Tcl_SetResult(interp, "emc_program_status: need no args", TCL_VOLATILE);
03502     return TCL_ERROR;
03503   }
03504 
03505   if (emcUpdateType == EMC_UPDATE_AUTO) {
03506     updateStatus();
03507   }
03508 
03509   switch (emcStatus->task.interpState) {
03510   case EMC_TASK_INTERP_READING:
03511   case EMC_TASK_INTERP_WAITING:
03512     Tcl_SetResult(interp, "running", TCL_VOLATILE);
03513     return TCL_OK;
03514     break;
03515 
03516   case EMC_TASK_INTERP_PAUSED:
03517     Tcl_SetResult(interp, "paused", TCL_VOLATILE);
03518     return TCL_OK;
03519     break;
03520 
03521   default:
03522     Tcl_SetResult(interp, "idle", TCL_VOLATILE);
03523     return TCL_OK;
03524   }
03525 
03526   Tcl_SetResult(interp, "idle", TCL_VOLATILE);
03527   return TCL_OK;
03528 }
03529 
03530 static int emc_program_line(ClientData clientdata,
03531                             Tcl_Interp *interp,
03532                             int objc,
03533                             Tcl_Obj *CONST objv[])
03534 {
03535   Tcl_Obj *lineobj;
03536   int programActiveLine = 0;
03537 
03538   if (objc != 1) {
03539     Tcl_SetResult(interp, "emc_program_line: need no args", TCL_VOLATILE);
03540     return TCL_ERROR;
03541   }
03542 
03543   if (emcUpdateType == EMC_UPDATE_AUTO) {
03544     updateStatus();
03545   }
03546 
03547   if (programStartLine < 0 ||
03548       emcStatus->task.readLine < programStartLine) {
03549     // controller is skipping lines
03550     programActiveLine = emcStatus->task.readLine;
03551   }
03552   else {                        // controller is not skipping lines
03553     if (emcStatus->task.currentLine > 0) {
03554       if (emcStatus->task.motionLine > 0 &&
03555           emcStatus->task.motionLine < emcStatus->task.currentLine) {
03556         // active line is the motion line, which lags
03557         programActiveLine = emcStatus->task.motionLine;
03558       }
03559       else {
03560         // active line is the current line-- no motion lag
03561         programActiveLine = emcStatus->task.currentLine;
03562       }
03563     }
03564     else {
03565       // no active line at all
03566       programActiveLine = 0;
03567     }
03568   } // end of else controller is not skipping lines
03569 
03570   lineobj = Tcl_NewIntObj(programActiveLine);
03571 
03572   Tcl_SetObjResult(interp, lineobj);
03573   return TCL_OK;
03574 }
03575 
03576 static int emc_program_codes(ClientData clientdata,
03577                              Tcl_Interp *interp,
03578                              int objc,
03579                              Tcl_Obj *CONST objv[])
03580 {
03581   char codes_string[256];
03582   char string[256];
03583   int t;
03584   int code;
03585 
03586   if (objc != 1) {
03587     Tcl_SetResult(interp, "emc_program_codes: need no args", TCL_VOLATILE);
03588     return TCL_ERROR;
03589   }
03590 
03591   if (emcUpdateType == EMC_UPDATE_AUTO) {
03592     updateStatus();
03593   }
03594 
03595   // fill in the active G codes
03596   codes_string[0] = 0;
03597   for (t = 1; t < EMC_TASK_ACTIVE_G_CODES; t++) {
03598     code = emcStatus->task.activeGCodes[t];
03599     if (code == -1) {
03600       continue;
03601     }
03602     if (code % 10) {
03603       sprintf(string, "G%.1f ", (double) code / 10.0);
03604     }
03605     else {
03606       sprintf(string, "G%d ", code / 10);
03607     }
03608     strcat(codes_string, string);
03609   }
03610 
03611   // fill in the active M codes, settings too
03612   for (t = 1; t < EMC_TASK_ACTIVE_M_CODES; t++) {
03613     code = emcStatus->task.activeMCodes[t];
03614     if (code == -1) {
03615       continue;
03616     }
03617     sprintf(string, "M%d ", code);
03618     strcat(codes_string, string);
03619   }
03620 
03621   // fill in F and S codes also
03622   sprintf(string, "F%.0f ", emcStatus->task.activeSettings[1]);
03623   strcat(codes_string, string);
03624   sprintf(string, "S%.0f", emcStatus->task.activeSettings[2]);
03625   strcat(codes_string, string);
03626 
03627   Tcl_SetResult(interp, codes_string, TCL_VOLATILE);
03628   return TCL_OK;
03629 }
03630 
03631 static int emc_joint_type(ClientData clientdata,
03632                           Tcl_Interp *interp,
03633                           int objc,
03634                           Tcl_Obj *CONST objv[])
03635 {
03636   int joint;
03637 
03638   if (objc != 2) {
03639     Tcl_SetResult(interp,
03640                   "emc_joint_type: need exactly 1 non-negative integer",
03641                   TCL_VOLATILE);
03642     return TCL_ERROR;
03643   }
03644 
03645   if (emcUpdateType == EMC_UPDATE_AUTO) {
03646     updateStatus();
03647   }
03648 
03649   if (TCL_OK == Tcl_GetIntFromObj(0, objv[1], &joint)) {
03650     if (joint < 0 ||
03651         joint >= EMC_AXIS_MAX) {
03652       Tcl_SetResult(interp,
03653                     "emc_joint_type: joint out of bounds",
03654                     TCL_VOLATILE);
03655       return TCL_ERROR;
03656     }
03657 
03658     switch (emcStatus->motion.axis[joint].axisType) {
03659     case EMC_AXIS_LINEAR:
03660       Tcl_SetResult(interp, "linear", TCL_VOLATILE);
03661       break;
03662     case EMC_AXIS_ANGULAR:
03663       Tcl_SetResult(interp, "angular", TCL_VOLATILE);
03664       break;
03665     default:
03666       Tcl_SetResult(interp, "custom", TCL_VOLATILE);
03667       break;
03668     }
03669 
03670   return TCL_OK;
03671   }
03672 
03673   Tcl_SetResult(interp, "emc_joint_type: invalid joint number", TCL_VOLATILE);
03674   return TCL_ERROR;
03675 }
03676 
03677 static int emc_joint_units(ClientData clientdata,
03678                                  Tcl_Interp *interp,
03679                                  int objc,
03680                                  Tcl_Obj *CONST objv[])
03681 {
03682   int joint;
03683 
03684   if (objc != 2) {
03685     Tcl_SetResult(interp,
03686                   "emc_joint_units: need exactly 1 non-negative integer",
03687                   TCL_VOLATILE);
03688     return TCL_ERROR;
03689   }
03690 
03691   if (emcUpdateType == EMC_UPDATE_AUTO) {
03692     updateStatus();
03693   }
03694 
03695   if (TCL_OK == Tcl_GetIntFromObj(0, objv[1], &joint)) {
03696     if (joint < 0 ||
03697         joint >= EMC_AXIS_MAX) {
03698       Tcl_SetResult(interp,
03699                     "emc_joint_type: joint out of bounds",
03700                     TCL_VOLATILE);
03701       return TCL_ERROR;
03702     }
03703 
03704     switch (emcStatus->motion.axis[joint].axisType) {
03705     case EMC_AXIS_LINEAR:
03706       /* try mm */
03707       if (CLOSE(emcStatus->motion.axis[joint].units, 1.0, LINEAR_CLOSENESS)) {
03708         Tcl_SetResult(interp, "mm", TCL_VOLATILE);
03709         return TCL_OK;
03710       }
03711       /* now try inch */
03712       else if (CLOSE(emcStatus->motion.axis[joint].units, INCH_PER_MM, LINEAR_CLOSENESS)) {
03713         Tcl_SetResult(interp, "inch", TCL_VOLATILE);
03714         return TCL_OK;
03715       }
03716       /* now try cm */
03717       else if (CLOSE(emcStatus->motion.axis[joint].units, CM_PER_MM, LINEAR_CLOSENESS)) {
03718         Tcl_SetResult(interp, "cm", TCL_VOLATILE);
03719         return TCL_OK;
03720       }
03721       /* else it's custom */
03722       Tcl_SetResult(interp, "custom", TCL_VOLATILE);
03723       return TCL_OK;
03724       break;
03725 
03726     case EMC_AXIS_ANGULAR:
03727       /* try degrees */
03728       if (CLOSE(emcStatus->motion.axis[joint].units, 1.0, ANGULAR_CLOSENESS)) {
03729         Tcl_SetResult(interp, "deg", TCL_VOLATILE);
03730         return TCL_OK;
03731       }
03732       /* now try radians */
03733       else if (CLOSE(emcStatus->motion.axis[joint].units, RAD_PER_DEG, ANGULAR_CLOSENESS)) {
03734         Tcl_SetResult(interp, "rad", TCL_VOLATILE);
03735         return TCL_OK;
03736       }
03737       /* now try grads */
03738       else if (CLOSE(emcStatus->motion.axis[joint].units, GRAD_PER_DEG, ANGULAR_CLOSENESS)) {
03739         Tcl_SetResult(interp, "grad", TCL_VOLATILE);
03740         return TCL_OK;
03741       }
03742       /* else it's custom */
03743       Tcl_SetResult(interp, "custom", TCL_VOLATILE);
03744       return TCL_OK;
03745       break;
03746 
03747     default:
03748       Tcl_SetResult(interp, "custom", TCL_VOLATILE);
03749       return TCL_OK;
03750       break;
03751     }
03752   }
03753 
03754   Tcl_SetResult(interp, "emc_joint_units: invalid joint number", TCL_VOLATILE);
03755   return TCL_ERROR;
03756 }
03757 
03758 static int emc_program_linear_units(ClientData clientdata,
03759                                     Tcl_Interp *interp,
03760                                     int objc,
03761                                     Tcl_Obj *CONST objv[])
03762 {
03763   if (objc != 1) {
03764     Tcl_SetResult(interp, "emc_program_linear_units: need no args", TCL_VOLATILE);
03765     return TCL_ERROR;
03766   }
03767 
03768   if (emcUpdateType == EMC_UPDATE_AUTO) {
03769     updateStatus();
03770   }
03771 
03772   switch (emcStatus->task.programUnits) {
03773   case CANON_UNITS_INCHES:
03774     Tcl_SetResult(interp, "inch", TCL_VOLATILE);
03775     return TCL_OK;
03776     break;
03777 
03778   case CANON_UNITS_MM:
03779     Tcl_SetResult(interp, "mm", TCL_VOLATILE);
03780     return TCL_OK;
03781     break;
03782 
03783   case CANON_UNITS_CM:
03784     Tcl_SetResult(interp, "cm", TCL_VOLATILE);
03785     return TCL_OK;
03786     break;
03787 
03788   default:
03789     Tcl_SetResult(interp, "custom", TCL_VOLATILE);
03790     return TCL_OK;
03791     break;
03792   }
03793 
03794   Tcl_SetResult(interp, "custom", TCL_VOLATILE);
03795   return TCL_OK;
03796 }
03797 
03798 static int emc_program_angular_units(ClientData clientdata,
03799                                      Tcl_Interp *interp,
03800                                      int objc,
03801                                      Tcl_Obj *CONST objv[])
03802 {
03803   if (objc != 1) {
03804     Tcl_SetResult(interp, "emc_program_angular_units: need no args", TCL_VOLATILE);
03805     return TCL_ERROR;
03806   }
03807 
03808   if (emcUpdateType == EMC_UPDATE_AUTO) {
03809     updateStatus();
03810   }
03811 
03812   // currently the EMC doesn't have separate program angular units, so
03813   // these are simply "deg"
03814   Tcl_SetResult(interp, "deg", TCL_VOLATILE);
03815   return TCL_OK;
03816 }
03817 
03818 static int emc_user_linear_units(ClientData clientdata,
03819                                  Tcl_Interp *interp,
03820                                  int objc,
03821                                  Tcl_Obj *CONST objv[])
03822 {
03823   if (objc != 1) {
03824     Tcl_SetResult(interp, "emc_user_linear_units: need no args", TCL_VOLATILE);
03825     return TCL_ERROR;
03826   }
03827 
03828   if (emcUpdateType == EMC_UPDATE_AUTO) {
03829     updateStatus();
03830   }
03831 
03832   /* try mm */
03833   if (CLOSE(emcStatus->motion.traj.linearUnits, 1.0, LINEAR_CLOSENESS)) {
03834     Tcl_SetResult(interp, "mm", TCL_VOLATILE);
03835     return TCL_OK;
03836   }
03837   /* now try inch */
03838   else if (CLOSE(emcStatus->motion.traj.linearUnits, INCH_PER_MM, LINEAR_CLOSENESS)) {
03839     Tcl_SetResult(interp, "inch", TCL_VOLATILE);
03840     return TCL_OK;
03841   }
03842   /* now try cm */
03843   else if (CLOSE(emcStatus->motion.traj.linearUnits, CM_PER_MM, LINEAR_CLOSENESS)) {
03844     Tcl_SetResult(interp, "cm", TCL_VOLATILE);
03845     return TCL_OK;
03846   }
03847 
03848   /* else it's custom */
03849   Tcl_SetResult(interp, "custom", TCL_VOLATILE);
03850   return TCL_OK;
03851 }
03852 
03853 static int emc_user_angular_units(ClientData clientdata,
03854                                      Tcl_Interp *interp,
03855                                      int objc,
03856                                      Tcl_Obj *CONST objv[])
03857 {
03858   if (objc != 1) {
03859     Tcl_SetResult(interp, "emc_user_angular_units: need no args", TCL_VOLATILE);
03860     return TCL_ERROR;
03861   }
03862 
03863   if (emcUpdateType == EMC_UPDATE_AUTO) {
03864     updateStatus();
03865   }
03866 
03867   /* try degrees */
03868   if (CLOSE(emcStatus->motion.traj.angularUnits, 1.0, ANGULAR_CLOSENESS)) {
03869     Tcl_SetResult(interp, "deg", TCL_VOLATILE);
03870     return TCL_OK;
03871   }
03872   /* now try radians */
03873   else if (CLOSE(emcStatus->motion.traj.angularUnits, RAD_PER_DEG, ANGULAR_CLOSENESS)) {
03874     Tcl_SetResult(interp, "rad", TCL_VOLATILE);
03875     return TCL_OK;
03876   }
03877   /* now try grads */
03878   else if (CLOSE(emcStatus->motion.traj.angularUnits, GRAD_PER_DEG, ANGULAR_CLOSENESS)) {
03879     Tcl_SetResult(interp, "grad", TCL_VOLATILE);
03880     return TCL_OK;
03881   }
03882 
03883   /* else it's an abitrary number, so just return it */
03884   Tcl_SetResult(interp, "custom", TCL_VOLATILE);
03885   return TCL_OK;
03886 }
03887 
03888 static int emc_display_linear_units(ClientData clientdata,
03889                                     Tcl_Interp *interp,
03890                                     int objc,
03891                                     Tcl_Obj *CONST objv[])
03892 {
03893   if (objc != 1) {
03894     Tcl_SetResult(interp, "emc_display_linear_units: need no args", TCL_VOLATILE);
03895     return TCL_ERROR;
03896   }
03897 
03898   if (emcUpdateType == EMC_UPDATE_AUTO) {
03899     updateStatus();
03900   }
03901 
03902   switch (linearUnitConversion) {
03903   case LINEAR_UNITS_INCH:
03904     Tcl_SetResult(interp, "inch", TCL_VOLATILE);
03905     break;
03906   case LINEAR_UNITS_MM:
03907     Tcl_SetResult(interp, "mm", TCL_VOLATILE);
03908     break;
03909   case LINEAR_UNITS_CM:
03910     Tcl_SetResult(interp, "cm", TCL_VOLATILE);
03911     break;
03912   case LINEAR_UNITS_AUTO:
03913     switch (emcStatus->task.programUnits) {
03914     case CANON_UNITS_MM:
03915       Tcl_SetResult(interp, "(mm)", TCL_VOLATILE);
03916       break;
03917     case CANON_UNITS_INCHES:
03918       Tcl_SetResult(interp, "(inch)", TCL_VOLATILE);
03919       break;
03920     case CANON_UNITS_CM:
03921       Tcl_SetResult(interp, "(cm)", TCL_VOLATILE);
03922       break;
03923     }
03924     break;
03925   default:
03926     Tcl_SetResult(interp, "custom", TCL_VOLATILE);
03927     break;
03928   }
03929 
03930   return TCL_OK;
03931 }
03932 
03933 static int emc_display_angular_units(ClientData clientdata,
03934                                      Tcl_Interp *interp,
03935                                      int objc,
03936                                      Tcl_Obj *CONST objv[])
03937 {
03938   if (objc != 1) {
03939     Tcl_SetResult(interp, "emc_display_angular_units: need no args", TCL_VOLATILE);
03940     return TCL_ERROR;
03941   }
03942 
03943   if (emcUpdateType == EMC_UPDATE_AUTO) {
03944     updateStatus();
03945   }
03946 
03947   switch (angularUnitConversion) {
03948   case ANGULAR_UNITS_DEG:
03949     Tcl_SetResult(interp, "deg", TCL_VOLATILE);
03950     break;
03951   case ANGULAR_UNITS_RAD:
03952     Tcl_SetResult(interp, "rad", TCL_VOLATILE);
03953     break;
03954   case ANGULAR_UNITS_GRAD:
03955     Tcl_SetResult(interp, "grad", TCL_VOLATILE);
03956     break;
03957   case ANGULAR_UNITS_AUTO:
03958     Tcl_SetResult(interp, "(deg)", TCL_VOLATILE); // FIXME-- always deg?
03959     break;
03960   default:
03961     Tcl_SetResult(interp, "custom", TCL_VOLATILE);
03962     break;
03963   }
03964 
03965   return TCL_OK;
03966 }
03967 
03968 static int emc_linear_unit_conversion(ClientData clientdata,
03969                                       Tcl_Interp *interp,
03970                                       int objc,
03971                                       Tcl_Obj *CONST objv[])
03972 {
03973   char *objstr;
03974 
03975   if (objc == 1) {
03976     // no arg-- return unit setting
03977     switch (linearUnitConversion) {
03978     case LINEAR_UNITS_INCH:
03979       Tcl_SetResult(interp, "inch", TCL_VOLATILE);
03980       break;
03981     case LINEAR_UNITS_MM:
03982       Tcl_SetResult(interp, "mm", TCL_VOLATILE);
03983       break;
03984     case LINEAR_UNITS_CM:
03985       Tcl_SetResult(interp, "cm", TCL_VOLATILE);
03986       break;
03987     case LINEAR_UNITS_AUTO:
03988       Tcl_SetResult(interp, "auto", TCL_VOLATILE);
03989       break;
03990     default:
03991       Tcl_SetResult(interp, "custom", TCL_VOLATILE);
03992       break;
03993     }
03994     return TCL_OK;
03995   }
03996 
03997   if (objc == 2) {
03998     objstr = Tcl_GetStringFromObj(objv[1], 0);
03999     if (! strcmp(objstr, "inch")) {
04000       linearUnitConversion = LINEAR_UNITS_INCH;
04001       return TCL_OK;
04002     }
04003     if (! strcmp(objstr, "mm")) {
04004       linearUnitConversion = LINEAR_UNITS_MM;
04005       return TCL_OK;
04006     }
04007     if (! strcmp(objstr, "cm")) {
04008       linearUnitConversion = LINEAR_UNITS_CM;
04009       return TCL_OK;
04010     }
04011     if (! strcmp(objstr, "auto")) {
04012       linearUnitConversion = LINEAR_UNITS_AUTO;
04013       return TCL_OK;
04014     }
04015     if (! strcmp(objstr, "custom")) {
04016       linearUnitConversion = LINEAR_UNITS_CUSTOM;
04017       return TCL_OK;
04018     }
04019   }
04020 
04021   Tcl_SetResult(interp, "emc_linear_unit_conversion: need 'inch', 'mm', 'cm', 'auto', 'custom', or no args", TCL_VOLATILE);
04022   return TCL_ERROR;
04023 }
04024 
04025 static int emc_angular_unit_conversion(ClientData clientdata,
04026                                        Tcl_Interp *interp,
04027                                        int objc,
04028                                        Tcl_Obj *CONST objv[])
04029 {
04030   char *objstr;
04031 
04032   if (objc == 1) {
04033     // no arg-- return unit setting
04034     switch (angularUnitConversion) {
04035     case ANGULAR_UNITS_DEG:
04036       Tcl_SetResult(interp, "deg", TCL_VOLATILE);
04037       break;
04038     case ANGULAR_UNITS_RAD:
04039       Tcl_SetResult(interp, "rad", TCL_VOLATILE);
04040       break;
04041     case ANGULAR_UNITS_GRAD:
04042       Tcl_SetResult(interp, "grad", TCL_VOLATILE);
04043       break;
04044     case ANGULAR_UNITS_AUTO:
04045       Tcl_SetResult(interp, "auto", TCL_VOLATILE);
04046       break;
04047     default:
04048       Tcl_SetResult(interp, "custom", TCL_VOLATILE);
04049       break;
04050     }
04051     return TCL_OK;
04052   }
04053 
04054   if (objc == 2) {
04055     objstr = Tcl_GetStringFromObj(objv[1], 0);
04056     if (! strcmp(objstr, "deg")) {
04057       angularUnitConversion = ANGULAR_UNITS_DEG;
04058       return TCL_OK;
04059     }
04060     if (! strcmp(objstr, "rad")) {
04061       angularUnitConversion = ANGULAR_UNITS_RAD;
04062       return TCL_OK;
04063     }
04064     if (! strcmp(objstr, "grad")) {
04065       angularUnitConversion = ANGULAR_UNITS_GRAD;
04066       return TCL_OK;
04067     }
04068     if (! strcmp(objstr, "auto")) {
04069       angularUnitConversion = ANGULAR_UNITS_AUTO;
04070       return TCL_OK;
04071     }
04072     if (! strcmp(objstr, "custom")) {
04073       angularUnitConversion = ANGULAR_UNITS_CUSTOM;
04074       return TCL_OK;
04075     }
04076   }
04077 
04078   Tcl_SetResult(interp, "emc_angular_unit_conversion: need 'deg', 'rad', 'grad', 'auto', 'custom', or no args", TCL_VOLATILE);
04079   return TCL_ERROR;
04080 }
04081 
04082 static int emc_task_heartbeat(ClientData clientdata,
04083                               Tcl_Interp *interp,
04084                               int objc,
04085                               Tcl_Obj *CONST objv[])
04086 {
04087   Tcl_Obj *hbobj;
04088 
04089   if (objc != 1) {
04090     Tcl_SetResult(interp, "emc_task_heartbeat: need no args", TCL_VOLATILE);
04091     return TCL_ERROR;
04092   }
04093 
04094   if (emcUpdateType == EMC_UPDATE_AUTO) {
04095     updateStatus();
04096   }
04097 
04098   hbobj = Tcl_NewIntObj(emcStatus->task.heartbeat);
04099 
04100   Tcl_SetObjResult(interp, hbobj);
04101   return TCL_OK;
04102 }
04103 
04104 static int emc_task_command(ClientData clientdata,
04105                             Tcl_Interp *interp,
04106                             int objc,
04107                             Tcl_Obj *CONST objv[])
04108 {
04109   Tcl_Obj *commandobj;
04110 
04111   if (objc != 1) {
04112     Tcl_SetResult(interp, "emc_task_command: need no args", TCL_VOLATILE);
04113     return TCL_ERROR;
04114   }
04115 
04116   if (emcUpdateType == EMC_UPDATE_AUTO) {
04117     updateStatus();
04118   }
04119 
04120   commandobj = Tcl_NewIntObj(emcStatus->task.command_type);
04121 
04122   Tcl_SetObjResult(interp, commandobj);
04123   return TCL_OK;
04124 }
04125 
04126 static int emc_task_command_number(ClientData clientdata,
04127                                    Tcl_Interp *interp,
04128                                    int objc,
04129                                    Tcl_Obj *CONST objv[])
04130 {
04131   Tcl_Obj *commandnumber;
04132 
04133   if (objc != 1) {
04134     Tcl_SetResult(interp, "emc_task_command_number: need no args", TCL_VOLATILE);
04135     return TCL_ERROR;
04136   }
04137 
04138   if (emcUpdateType == EMC_UPDATE_AUTO) {
04139     updateStatus();
04140   }
04141 
04142   commandnumber = Tcl_NewIntObj(emcStatus->task.echo_serial_number);
04143 
04144   Tcl_SetObjResult(interp, commandnumber);
04145   return TCL_OK;
04146 }
04147 
04148 static int emc_task_command_status(ClientData clientdata,
04149                                    Tcl_Interp *interp,
04150                                    int objc,
04151                                    Tcl_Obj *CONST objv[])
04152 {
04153   Tcl_Obj *commandstatus;
04154 
04155   if (objc != 1) {
04156     Tcl_SetResult(interp, "emc_task_command_status: need no args", TCL_VOLATILE);
04157     return TCL_ERROR;
04158   }
04159 
04160   if (emcUpdateType == EMC_UPDATE_AUTO) {
04161     updateStatus();
04162   }
04163 
04164   commandstatus = Tcl_NewIntObj(emcStatus->task.status);
04165 
04166   Tcl_SetObjResult(interp, commandstatus);
04167   return TCL_OK;
04168 }
04169 
04170 static int emc_io_heartbeat(ClientData clientdata,
04171                             Tcl_Interp *interp,
04172                             int objc,
04173                             Tcl_Obj *CONST objv[])
04174 {
04175   Tcl_Obj *hbobj;
04176 
04177   if (objc != 1) {
04178     Tcl_SetResult(interp, "emc_io_heartbeat: need no args", TCL_VOLATILE);
04179     return TCL_ERROR;
04180   }
04181 
04182   if (emcUpdateType == EMC_UPDATE_AUTO) {
04183     updateStatus();
04184   }
04185 
04186   hbobj = Tcl_NewIntObj(emcStatus->io.heartbeat);
04187 
04188   Tcl_SetObjResult(interp, hbobj);
04189   return TCL_OK;
04190 }
04191 
04192 static int emc_io_command(ClientData clientdata,
04193                           Tcl_Interp *interp,
04194                           int objc,
04195                           Tcl_Obj *CONST objv[])
04196 {
04197   Tcl_Obj *commandobj;
04198 
04199   if (objc != 1) {
04200     Tcl_SetResult(interp, "emc_io_command: need no args", TCL_VOLATILE);
04201     return TCL_ERROR;
04202   }
04203 
04204   if (emcUpdateType == EMC_UPDATE_AUTO) {
04205     updateStatus();
04206   }
04207 
04208   commandobj = Tcl_NewIntObj(emcStatus->io.command_type);
04209 
04210   Tcl_SetObjResult(interp, commandobj);
04211   return TCL_OK;
04212 }
04213 
04214 static int emc_io_command_number(ClientData clientdata,
04215                                  Tcl_Interp *interp,
04216                                  int objc,
04217                                  Tcl_Obj *CONST objv[])
04218 {
04219   Tcl_Obj *commandnumber;
04220 
04221   if (objc != 1) {
04222     Tcl_SetResult(interp, "emc_io_command_number: need no args", TCL_VOLATILE);
04223     return TCL_ERROR;
04224   }
04225 
04226   if (emcUpdateType == EMC_UPDATE_AUTO) {
04227     updateStatus();
04228   }
04229 
04230   commandnumber = Tcl_NewIntObj(emcStatus->io.echo_serial_number);
04231 
04232   Tcl_SetObjResult(interp, commandnumber);
04233   return TCL_OK;
04234 }
04235 
04236 static int emc_io_command_status(ClientData clientdata,
04237                                  Tcl_Interp *interp,
04238                                  int objc,
04239                                  Tcl_Obj *CONST objv[])
04240 {
04241   Tcl_Obj *commandstatus;
04242 
04243   if (objc != 1) {
04244     Tcl_SetResult(interp, "emc_io_command_status: need no args", TCL_VOLATILE);
04245     return TCL_ERROR;
04246   }
04247 
04248   if (emcUpdateType == EMC_UPDATE_AUTO) {
04249     updateStatus();
04250   }
04251 
04252   commandstatus = Tcl_NewIntObj(emcStatus->io.status);
04253 
04254   Tcl_SetObjResult(interp, commandstatus);
04255   return TCL_OK;
04256 }
04257 
04258 static int emc_motion_heartbeat(ClientData clientdata,
04259                                 Tcl_Interp *interp,
04260                                 int objc,
04261                                 Tcl_Obj *CONST objv[])
04262 {
04263   Tcl_Obj *hbobj;
04264 
04265   if (objc != 1) {
04266     Tcl_SetResult(interp, "emc_motion_heartbeat: need no args", TCL_VOLATILE);
04267     return TCL_ERROR;
04268   }
04269 
04270   if (emcUpdateType == EMC_UPDATE_AUTO) {
04271     updateStatus();
04272   }
04273 
04274   hbobj = Tcl_NewIntObj(emcStatus->motion.heartbeat);
04275 
04276   Tcl_SetObjResult(interp, hbobj);
04277   return TCL_OK;
04278 }
04279 
04280 static int emc_motion_command(ClientData clientdata,
04281                               Tcl_Interp *interp,
04282                               int objc,
04283                               Tcl_Obj *CONST objv[])
04284 {
04285   Tcl_Obj *commandobj;
04286 
04287   if (objc != 1) {
04288     Tcl_SetResult(interp, "emc_motion_command: need no args", TCL_VOLATILE);
04289     return TCL_ERROR;
04290   }
04291 
04292   if (emcUpdateType == EMC_UPDATE_AUTO) {
04293     updateStatus();
04294   }
04295 
04296   commandobj = Tcl_NewIntObj(emcStatus->motion.command_type);
04297 
04298   Tcl_SetObjResult(interp, commandobj);
04299   return TCL_OK;
04300 }
04301 
04302 static int emc_motion_command_number(ClientData clientdata,
04303                                      Tcl_Interp *interp,
04304                                      int objc,
04305                                      Tcl_Obj *CONST objv[])
04306 {
04307   Tcl_Obj *commandnumber;
04308 
04309   if (objc != 1) {
04310     Tcl_SetResult(interp, "emc_motion_command_number: need no args", TCL_VOLATILE);
04311     return TCL_ERROR;
04312   }
04313 
04314   if (emcUpdateType == EMC_UPDATE_AUTO) {
04315     updateStatus();
04316   }
04317 
04318   commandnumber = Tcl_NewIntObj(emcStatus->motion.echo_serial_number);
04319 
04320   Tcl_SetObjResult(interp, commandnumber);
04321   return TCL_OK;
04322 }
04323 
04324 static int emc_motion_command_status(ClientData clientdata,
04325                                      Tcl_Interp *interp,
04326                                      int objc,
04327                                      Tcl_Obj *CONST objv[])
04328 {
04329   Tcl_Obj *commandstatus;
04330 
04331   if (objc != 1) {
04332     Tcl_SetResult(interp, "emc_motion_command_status: need no args", TCL_VOLATILE);
04333     return TCL_ERROR;
04334   }
04335 
04336   if (emcUpdateType == EMC_UPDATE_AUTO) {
04337     updateStatus();
04338   }
04339 
04340   commandstatus = Tcl_NewIntObj(emcStatus->motion.status);
04341 
04342   Tcl_SetObjResult(interp, commandstatus);
04343   return TCL_OK;
04344 }
04345 
04346 static int emc_axis_gains(ClientData clientdata,
04347                           Tcl_Interp *interp,
04348                           int objc,
04349                           Tcl_Obj *CONST objv[])
04350 {
04351   Tcl_Obj *valobj;
04352   int axis;
04353   const char *varstr;
04354   double val;
04355   double p, i, d, ff0, ff1, ff2, backlash, bias, maxError, deadband;
04356 
04357   // syntax is emc_axis_gains <axis> <var> {<val>}
04358   // or        emc_axis_gains <axis> all <p> <i> ... <deadband>
04359   // without <val> only, returns value of <var> for <axis>
04360   // otherwise sets <var> to <value> for <axis>
04361   // <axis> is 0,1,...
04362   // <var> is p i d ff0 ff1 ff2 backlash bias maxerror deadband
04363   // <val> is floating point number
04364 
04365   if (objc < 3) {
04366     Tcl_SetResult(interp, "emc_axis_gains: need <axis> <var> {<val>}", TCL_VOLATILE);
04367     return TCL_ERROR;
04368   }
04369 
04370   if (emcUpdateType == EMC_UPDATE_AUTO) {
04371     updateStatus();
04372   }
04373 
04374   if (0 != Tcl_GetIntFromObj(0, objv[1], &axis) ||
04375       axis < 0 ||
04376       axis >= EMC_AXIS_MAX) {
04377     Tcl_SetResult(interp, "emc_axis_gains: need axis as integer, 0..EMC_AXIS_MAX-1", TCL_VOLATILE);
04378     return TCL_ERROR;
04379   }
04380 
04381   varstr = Tcl_GetStringFromObj(objv[2], 0);
04382 
04383   if (objc == 3) {
04384     if (! strcmp(varstr, "p")) {
04385       valobj = Tcl_NewDoubleObj(emcStatus->motion.axis[axis].p);
04386       Tcl_SetObjResult(interp, valobj);
04387       return TCL_OK;
04388     }
04389     else if (! strcmp(varstr, "i")) {
04390       valobj = Tcl_NewDoubleObj(emcStatus->motion.axis[axis].i);
04391       Tcl_SetObjResult(interp, valobj);
04392       return TCL_OK;
04393     }
04394     else if (! strcmp(varstr, "d")) {
04395       valobj = Tcl_NewDoubleObj(emcStatus->motion.axis[axis].d);
04396       Tcl_SetObjResult(interp, valobj);
04397       return TCL_OK;
04398     }
04399     else if (! strcmp(varstr, "ff0")) {
04400       valobj = Tcl_NewDoubleObj(emcStatus->motion.axis[axis].ff0);
04401       Tcl_SetObjResult(interp, valobj);
04402       return TCL_OK;
04403     }
04404     else if (! strcmp(varstr, "ff1")) {
04405       valobj = Tcl_NewDoubleObj(emcStatus->motion.axis[axis].ff1);
04406       Tcl_SetObjResult(interp, valobj);
04407       return TCL_OK;
04408     }
04409     else if (! strcmp(varstr, "ff2")) {
04410       valobj = Tcl_NewDoubleObj(emcStatus->motion.axis[axis].ff2);
04411       Tcl_SetObjResult(interp, valobj);
04412       return TCL_OK;
04413     }
04414     else if (! strcmp(varstr, "backlash")) {
04415       valobj = Tcl_NewDoubleObj(emcStatus->motion.axis[axis].backlash);
04416       Tcl_SetObjResult(interp, valobj);
04417       return TCL_OK;
04418     }
04419     else if (! strcmp(varstr, "bias")) {
04420       valobj = Tcl_NewDoubleObj(emcStatus->motion.axis[axis].bias);
04421       Tcl_SetObjResult(interp, valobj);
04422       return TCL_OK;
04423     }
04424     else if (! strcmp(varstr, "maxerror")) {
04425       valobj = Tcl_NewDoubleObj(emcStatus->motion.axis[axis].maxError);
04426       Tcl_SetObjResult(interp, valobj);
04427       return TCL_OK;
04428     }
04429     else if (! strcmp(varstr, "deadband")) {
04430       valobj = Tcl_NewDoubleObj(emcStatus->motion.axis[axis].deadband);
04431       Tcl_SetObjResult(interp, valobj);
04432       return TCL_OK;
04433     }
04434     else {
04435       Tcl_SetResult(interp, "emc_axis_gains: bad value for <val>", TCL_VOLATILE);
04436       return TCL_ERROR;
04437     }
04438   }
04439   else {
04440     // <val> is provided, so set it and use current values for others
04441     if (0 != Tcl_GetDoubleFromObj(0, objv[3], &val)) {
04442       Tcl_SetResult(interp, "emc_axis_gains: need value as floating point number", TCL_VOLATILE);
04443       return TCL_ERROR;
04444     }
04445 
04446     p = emcStatus->motion.axis[axis].p;
04447     i = emcStatus->motion.axis[axis].i;
04448     d = emcStatus->motion.axis[axis].d;
04449     ff0 = emcStatus->motion.axis[axis].ff0;
04450     ff1 = emcStatus->motion.axis[axis].ff1;
04451     ff2 = emcStatus->motion.axis[axis].ff2;
04452     backlash = emcStatus->motion.axis[axis].backlash;
04453     bias = emcStatus->motion.axis[axis].bias;
04454     maxError = emcStatus->motion.axis[axis].maxError;
04455     deadband = emcStatus->motion.axis[axis].deadband;
04456 
04457     if (! strcmp(varstr, "p")) {
04458       p = val;
04459     }
04460     else if (! strcmp(varstr, "i")) {
04461       i = val;
04462     }
04463     else if (! strcmp(varstr, "d")) {
04464       d = val;
04465     }
04466     else if (! strcmp(varstr, "ff0")) {
04467       ff0 = val;
04468     }
04469     else if (! strcmp(varstr, "ff1")) {
04470       ff1 = val;
04471     }
04472     else if (! strcmp(varstr, "ff2")) {
04473       ff2 = val;
04474     }
04475     else if (! strcmp(varstr, "backlash")) {
04476       backlash = val;
04477     }
04478     else if (! strcmp(varstr, "bias")) {
04479       bias = val;
04480     }
04481     else if (! strcmp(varstr, "maxerror")) {
04482       maxError = val;
04483     }
04484     else if (! strcmp(varstr, "deadband")) {
04485       deadband = val;
04486     }
04487     else if (! strcmp(varstr, "all") &&
04488              objc == 13) {
04489       // it's "emc_axis_gains axis all p i d ff0 ff1 ff2 backlash bias maxerror deadband"
04490 
04491       // P
04492       if (0 != Tcl_GetDoubleFromObj(0, objv[3], &val)) {
04493         Tcl_SetResult(interp, "emc_axis_gains: need P gain as floating point number", TCL_VOLATILE);
04494         return TCL_ERROR;
04495       }
04496       p = val;
04497       // I
04498       if (0 != Tcl_GetDoubleFromObj(0, objv[4], &val)) {
04499         Tcl_SetResult(interp, "emc_axis_gains: need I gain as floating point number", TCL_VOLATILE);
04500         return TCL_ERROR;
04501       }
04502       i = val;
04503       // D
04504       if (0 != Tcl_GetDoubleFromObj(0, objv[5], &val)) {
04505         Tcl_SetResult(interp, "emc_axis_gains: need D gain as floating point number", TCL_VOLATILE);
04506         return TCL_ERROR;
04507       }
04508       d = val;
04509       // FF0
04510       if (0 != Tcl_GetDoubleFromObj(0, objv[6], &val)) {
04511         Tcl_SetResult(interp, "emc_axis_gains: need FF0 gain as floating point number", TCL_VOLATILE);
04512         return TCL_ERROR;
04513       }
04514       ff0 = val;
04515       // FF1
04516       if (0 != Tcl_GetDoubleFromObj(0, objv[7], &val)) {
04517         Tcl_SetResult(interp, "emc_axis_gains: need FF1 gain as floating point number", TCL_VOLATILE);
04518         return TCL_ERROR;
04519       }
04520       ff1 = val;
04521       // FF2
04522       if (0 != Tcl_GetDoubleFromObj(0, objv[8], &val)) {
04523         Tcl_SetResult(interp, "emc_axis_gains: need FF2 gain as floating point number", TCL_VOLATILE);
04524         return TCL_ERROR;
04525       }
04526       ff2 = val;
04527       // backlash
04528       if (0 != Tcl_GetDoubleFromObj(0, objv[9], &val)) {
04529         Tcl_SetResult(interp, "emc_axis_gains: need backlash as floating point number", TCL_VOLATILE);
04530         return TCL_ERROR;
04531       }
04532       backlash = val;
04533       // bias
04534       if (0 != Tcl_GetDoubleFromObj(0, objv[10], &val)) {
04535         Tcl_SetResult(interp, "emc_axis_gains: need bias as floating point number", TCL_VOLATILE);
04536         return TCL_ERROR;
04537       }
04538       bias = val;
04539       // maxerror
04540       if (0 != Tcl_GetDoubleFromObj(0, objv[11], &val)) {
04541         Tcl_SetResult(interp, "emc_axis_gains: need maxerror as floating point number", TCL_VOLATILE);
04542         return TCL_ERROR;
04543       }
04544       maxError = val;
04545       // deadband
04546       if (0 != Tcl_GetDoubleFromObj(0, objv[12], &val)) {
04547         Tcl_SetResult(interp, "emc_axis_gains: need deadband as floating point number", TCL_VOLATILE);
04548         return TCL_ERROR;
04549       }
04550       deadband = val;
04551     }
04552     else {
04553       Tcl_SetResult(interp, "emc_axis_gains: not enough values for all gains", TCL_VOLATILE);
04554       return TCL_ERROR;
04555     }
04556 
04557     // now write it out
04558     sendAxisSetGains(axis, p, i, d, ff0, ff1, ff2, backlash, bias, maxError, deadband);
04559     return TCL_OK;
04560   }
04561 
04562   return TCL_OK;
04563 }
04564 
04565 static int emc_axis_set_output(ClientData clientdata,
04566                                Tcl_Interp *interp,
04567                                int objc,
04568                                Tcl_Obj *CONST objv[])
04569 {
04570   int axis;
04571   double output;
04572 
04573   // syntax is emc_axis_output <axis> <output>
04574 
04575   if (objc != 3) {
04576     Tcl_SetResult(interp, "emc_axis_set_output: need <axis> <output>", TCL_VOLATILE);
04577     return TCL_ERROR;
04578   }
04579 
04580   if (0 != Tcl_GetIntFromObj(0, objv[1], &axis) ||
04581       axis < 0 ||
04582       axis >= EMC_AXIS_MAX) {
04583     Tcl_SetResult(interp, "emc_axis_set_output: need axis as integer, 0..EMC_AXIS_MAX-1", TCL_VOLATILE);
04584     return TCL_ERROR;
04585   }
04586 
04587   if (0 != Tcl_GetDoubleFromObj(0, objv[2], &output)) {
04588     Tcl_SetResult(interp, "emc_axis_set_output: need output as real number", TCL_VOLATILE);
04589     return TCL_ERROR;
04590   }
04591 
04592   // now write it out
04593   sendAxisSetOutput(axis, output);
04594   return TCL_OK;
04595 }
04596 
04597 static int emc_axis_enable(ClientData clientdata,
04598                            Tcl_Interp *interp,
04599                            int objc,
04600                            Tcl_Obj *CONST objv[])
04601 {
04602   int axis;
04603   int val;
04604   Tcl_Obj *enobj;
04605 
04606   // syntax is emc_axis_output <axis> {0 | 1}
04607 
04608   if (objc < 2) {
04609     Tcl_SetResult(interp, "emc_axis_enable: need <axis>", TCL_VOLATILE);
04610     return TCL_ERROR;
04611   }
04612 
04613   if (0 != Tcl_GetIntFromObj(0, objv[1], &axis) ||
04614       axis < 0 ||
04615       axis >= EMC_AXIS_MAX) {
04616     Tcl_SetResult(interp, "emc_axis_enable: need axis as integer, 0..EMC_AXIS_MAX-1", TCL_VOLATILE);
04617     return TCL_ERROR;
04618   }
04619 
04620   if (objc == 2) {
04621     if (emcUpdateType == EMC_UPDATE_AUTO) {
04622       updateStatus();
04623     }
04624     enobj = Tcl_NewIntObj(emcStatus->motion.axis[axis].enabled);
04625     Tcl_SetObjResult(interp, enobj);
04626     return TCL_OK;
04627   }
04628 
04629   // else we were given 0 or 1 to enable/disable it
04630   if (0 != Tcl_GetIntFromObj(0, objv[2], &val)) {
04631     Tcl_SetResult(interp, "emc_axis_enable: need 0, 1 for disable, enable", TCL_VOLATILE);
04632     return TCL_ERROR;
04633   }
04634 
04635   sendAxisEnable(axis, val);
04636   return TCL_OK;
04637 }
04638 
04639 static int emc_axis_load_comp(ClientData clientdata,
04640                               Tcl_Interp *interp,
04641                               int objc,
04642                               Tcl_Obj *CONST objv[])
04643 {
04644   int axis;
04645   char file[256];
04646 
04647   // syntax is emc_axis_load_comp <axis> <file>
04648 
04649   if (objc != 3) {
04650     Tcl_SetResult(interp, "emc_axis_load_comp: need <axis> <file>", TCL_VOLATILE);
04651     return TCL_ERROR;
04652   }
04653 
04654   if (0 != Tcl_GetIntFromObj(0, objv[1], &axis) ||
04655       axis < 0 ||
04656       axis >= EMC_AXIS_MAX) {
04657     Tcl_SetResult(interp, "emc_axis_load_comp: need axis as integer, 0..EMC_AXIS_MAX-1", TCL_VOLATILE);
04658     return TCL_ERROR;
04659   }
04660 
04661   // copy objv[1] to file arg, to make sure it's not modified
04662   strcpy(file, Tcl_GetStringFromObj(objv[2], 0));
04663 
04664   // now write it out
04665   sendAxisLoadComp(axis, file);
04666   return TCL_OK;
04667 }
04668 
04669 static int emc_axis_alter(ClientData clientdata,
04670                           Tcl_Interp *interp,
04671                           int objc,
04672                           Tcl_Obj *CONST objv[])
04673 {
04674   int axis;
04675   double alter;
04676   Tcl_Obj *alterobj;
04677 
04678   if (objc > 1) {
04679     // try for first arg as axis value
04680     if (0 != Tcl_GetIntFromObj(0, objv[1], &axis) ||
04681         axis < 0 ||
04682         axis >= EMC_AXIS_MAX) {
04683       Tcl_SetResult(interp, "emc_axis_alter: need axis as integer, 0..EMC_AXIS_MAX-1", TCL_VOLATILE);
04684       return TCL_ERROR;
04685     }
04686 
04687     // axis arg OK, so try for next arg, returning current alter val if
04688     // it's not there, setting it if it is
04689 
04690     if (objc == 2) {
04691       if (emcUpdateType == EMC_UPDATE_AUTO) {
04692         updateStatus();
04693       }
04694       alterobj = Tcl_NewDoubleObj(emcStatus->motion.axis[axis].alter);
04695       Tcl_SetObjResult(interp, alterobj);
04696       return TCL_OK;
04697     }
04698 
04699     if (objc == 3) {
04700       if (0 != Tcl_GetDoubleFromObj(0, objv[2], &alter)) {
04701         Tcl_SetResult(interp, "emc_axis_alter: need alter as real number", TCL_VOLATILE);
04702         return TCL_ERROR;
04703       }
04704       if (0 != sendAxisAlter(axis, alter)) {
04705         Tcl_SetResult(interp, "emc_axis_alter: can't set alter value", TCL_VOLATILE);
04706         return TCL_OK;          // no TCL_ERROR, which is a syntax error
04707       }
04708 
04709       // sent it OK
04710       return TCL_OK;
04711     }
04712   }
04713 
04714   // else no args, or more than 2 args, so syntax error
04715   Tcl_SetResult(interp, "emc_axis_alter: need axis, optional alter value", TCL_VOLATILE);
04716   return TCL_ERROR;
04717 }
04718 
04719 int emc_log_open(ClientData clientdata,
04720                  Tcl_Interp *interp,
04721                  int objc,
04722                  Tcl_Obj *CONST objv[])
04723 {
04724   char file[256];
04725   const char *typestr;
04726   const char *triggerTypeStr;
04727   const char *triggerVarStr;
04728   int type;
04729   int size;
04730   int skip;
04731   int which = 0;
04732   enum EMCLOG_TRIGGER_TYPE triggerType;
04733   enum EMCLOG_TRIGGER_VAR triggerVar;
04734   double triggerThreshold;
04735   
04736   // need at least cmd and 4 args, file-type-size-skip, and maybe one more
04737   if (objc < 8) {
04738     Tcl_SetResult(interp, "emc_log_open: need <file> <type> <size> <skip> <triggerType> <triggerVar> <triggerThreshold> {<which>}", TCL_VOLATILE);
04739     return TCL_ERROR;
04740   }
04741 
04742   // copy objv[1] to file arg, to make sure it's not modified
04743   strcpy(file, Tcl_GetStringFromObj(objv[1], 0));
04744 
04745   // fill in the type number by string
04746   typestr = Tcl_GetStringFromObj(objv[2], 0);
04747   if (! strcmp(typestr, "axis_pos")) {
04748     type = EMC_LOG_TYPE_AXIS_POS;
04749   }
04750   else if (! strcmp(typestr, "axis_vel")) {
04751     type = EMC_LOG_TYPE_AXIS_VEL;
04752   }
04753   else if (! strcmp(typestr, "axes_inpos")) {
04754     type = EMC_LOG_TYPE_AXES_INPOS;
04755   }
04756   else if (! strcmp(typestr, "axes_outpos")) {
04757     type = EMC_LOG_TYPE_AXES_OUTPOS;
04758   }
04759   else if (! strcmp(typestr, "axes_ferror")) {
04760     type = EMC_LOG_TYPE_AXES_FERROR;
04761   }
04762   else if (! strcmp(typestr, "traj_pos")) {
04763     type = EMC_LOG_TYPE_TRAJ_POS;
04764   }
04765   else if (! strcmp(typestr, "traj_vel")) {
04766     type = EMC_LOG_TYPE_TRAJ_VEL;
04767   }
04768   else if (! strcmp(typestr, "traj_acc")) {
04769     type = EMC_LOG_TYPE_TRAJ_ACC;
04770   }
04771   else if (! strcmp(typestr, "pos_voltage")) {
04772     type = EMC_LOG_TYPE_POS_VOLTAGE;
04773   }
04774 
04775 
04776   if (0 != Tcl_GetIntFromObj(0, objv[3], &size) ||
04777       size <= 0) {
04778     Tcl_SetResult(interp, "emc_log_open: <size> must be greater than zero", TCL_VOLATILE);
04779     return TCL_ERROR;
04780   }
04781 
04782   if (0 != Tcl_GetIntFromObj(0, objv[4], &skip) ||
04783       skip < 0) {
04784     Tcl_SetResult(interp, "emc_log_open: <skip> must not be negative", TCL_VOLATILE);
04785     return TCL_ERROR;
04786   }
04787 
04788   // fill in the type number by string
04789   triggerTypeStr = Tcl_GetStringFromObj(objv[5], 0);
04790   if (! strcmp(triggerTypeStr, "manual")) {
04791     triggerType = EMCLOG_MANUAL_TRIGGER;
04792   }
04793   else if (! strcmp(triggerTypeStr, "delta")) {
04794     triggerType = EMCLOG_DELTA_TRIGGER;
04795   }
04796   else if (! strcmp(triggerTypeStr, "over")) {
04797     triggerType = EMCLOG_OVER_TRIGGER;
04798   }
04799   else if (! strcmp(triggerTypeStr, "under")) {
04800     triggerType = EMCLOG_UNDER_TRIGGER;
04801   }
04802 
04803   // fill in the type number by string
04804   triggerVarStr = Tcl_GetStringFromObj(objv[6], 0);
04805   if (! strcmp(triggerVarStr, "ferror")) {
04806     triggerVar = EMCLOG_TRIGGER_ON_FERROR;
04807   }
04808   else if (! strcmp(triggerVarStr, "volt")) {
04809     triggerVar = EMCLOG_TRIGGER_ON_VOLT;
04810   }
04811   else if (! strcmp(triggerVarStr, "pos")) {
04812     triggerVar = EMCLOG_TRIGGER_ON_POS;
04813   }
04814   else if (! strcmp(triggerVarStr, "vel")) {
04815     triggerVar = EMCLOG_TRIGGER_ON_VEL;
04816   }
04817 
04818   if (0 != Tcl_GetDoubleFromObj(0, objv[7], &triggerThreshold)) {
04819     Tcl_SetResult(interp, "emc_log_open: <triggerThreshold> must be a double", TCL_VOLATILE);
04820   }
04821   if (objc > 8) {
04822     if (0 != Tcl_GetIntFromObj(0, objv[8], &which)) {
04823       Tcl_SetResult(interp, "emc_log_open: <which> must be an integer", TCL_VOLATILE);
04824       return TCL_ERROR;
04825     }
04826   }
04827 
04828   sendLogOpen(file, type, size, skip, triggerType, triggerVar, triggerThreshold, which);
04829   return TCL_OK;
04830 }
04831 
04832 int emc_log_start(ClientData clientdata,
04833                   Tcl_Interp *interp,
04834                   int objc,
04835                   Tcl_Obj *CONST objv[])
04836 {
04837   if (objc != 1) {
04838     Tcl_SetResult(interp, "emc_log_start: needs no args", TCL_VOLATILE);
04839     return TCL_ERROR;
04840   }
04841 
04842   sendLogStart();
04843 
04844   return TCL_OK;
04845 }
04846 
04847 int emc_log_stop(ClientData clientdata,
04848                  Tcl_Interp *interp,
04849                  int objc,
04850                  Tcl_Obj *CONST objv[])
04851 {
04852   if (objc != 1) {
04853     Tcl_SetResult(interp, "emc_log_stop: needs no args", TCL_VOLATILE);
04854     return TCL_ERROR;
04855   }
04856 
04857   sendLogStop();
04858 
04859   return TCL_OK;
04860 }
04861 
04862 int emc_log_close(ClientData clientdata,
04863                   Tcl_Interp *interp,
04864                   int objc,
04865                   Tcl_Obj *CONST objv[])
04866 {
04867   if (objc != 1) {
04868     Tcl_SetResult(interp, "emc_log_close: needs no args", TCL_VOLATILE);
04869     return TCL_ERROR;
04870   }
04871 
04872   sendLogClose();
04873 
04874   return TCL_OK;
04875 }
04876 
04877 int emc_log_isopen(ClientData clientdata,
04878                    Tcl_Interp *interp,
04879                    int objc,
04880                    Tcl_Obj *CONST objv[])
04881 {
04882   if (objc != 1) {
04883     Tcl_SetResult(interp, "emc_log_isopen: needs no args", TCL_VOLATILE);
04884     return TCL_ERROR;
04885   }
04886 
04887   if (emcUpdateType == EMC_UPDATE_AUTO) {
04888     updateStatus();
04889   }
04890 
04891   Tcl_SetObjResult(interp, Tcl_NewIntObj(emcStatus->logOpen));
04892   return TCL_OK;
04893 }
04894 
04895 int emc_log_islogging(ClientData clientdata,
04896                       Tcl_Interp *interp,
04897                       int objc,
04898                       Tcl_Obj *CONST objv[])
04899 {
04900   if (objc != 1) {
04901     Tcl_SetResult(interp, "emc_log_islogging: needs no args", TCL_VOLATILE);
04902     return TCL_ERROR;
04903   }
04904 
04905   if (emcUpdateType == EMC_UPDATE_AUTO) {
04906     updateStatus();
04907   }
04908 
04909   Tcl_SetObjResult(interp, Tcl_NewIntObj(emcStatus->logStarted));
04910   return TCL_OK;
04911 }
04912 
04913 int emc_log_numpoints(ClientData clientdata,
04914                    Tcl_Interp *interp,
04915                    int objc,
04916                    Tcl_Obj *CONST objv[])
04917 {
04918   if (objc != 1) {
04919     Tcl_SetResult(interp, "emc_log_numpoints: needs no args", TCL_VOLATILE);
04920     return TCL_ERROR;
04921   }
04922 
04923   if (emcUpdateType == EMC_UPDATE_AUTO) {
04924     updateStatus();
04925   }
04926 
04927   Tcl_SetObjResult(interp, Tcl_NewIntObj(emcStatus->logPoints));
04928   return TCL_OK;
04929 }
04930 
04931 
04932 int emc_teleop_enable(ClientData clientdata,
04933                    Tcl_Interp *interp,
04934                    int objc,
04935                    Tcl_Obj *CONST objv[])
04936 {
04937   int enable;
04938 
04939   if (objc != 1) {
04940     if (0 != Tcl_GetIntFromObj(0, objv[1], &enable)) {
04941       Tcl_SetResult(interp, "emc_teleop_enable: <enable> must be an integer", TCL_VOLATILE);
04942       return TCL_ERROR;
04943     }
04944     sendSetTeleopEnable(enable);
04945   }
04946 
04947   if (emcUpdateType == EMC_UPDATE_AUTO) {
04948     updateStatus();
04949   }
04950 
04951   Tcl_SetObjResult(interp, Tcl_NewIntObj(emcStatus->motion.traj.mode == EMC_TRAJ_MODE_TELEOP));
04952   // FIXME
04953   if (EMC_DEBUG & EMC_DEBUG_TRAJ) {
04954     printf("emcStatus->motion.traj.mode = %d\n", emcStatus->motion.traj.mode);
04955   }
04956   return TCL_OK;
04957 }
04958 
04959 
04960 int emc_kinematics_type(ClientData clientdata,
04961                    Tcl_Interp *interp,
04962                    int objc,
04963                    Tcl_Obj *CONST objv[])
04964 {
04965 
04966   if (emcUpdateType == EMC_UPDATE_AUTO) {
04967     updateStatus();
04968   }
04969 
04970   Tcl_SetObjResult(interp, Tcl_NewIntObj(emcStatus->motion.traj.kinematics_type));
04971   // FIXME
04972   if (EMC_DEBUG & EMC_DEBUG_TRAJ) {
04973     printf("emcStatus->motion.traj.mode = %d\n", emcStatus->motion.traj.mode);
04974   }
04975   return TCL_OK;
04976 }
04977 
04978 
04979 
04980 int emc_probe_index(ClientData clientdata,
04981                    Tcl_Interp *interp,
04982                    int objc,
04983                    Tcl_Obj *CONST objv[])
04984 {
04985   int index;
04986 
04987   if (objc != 1) {
04988     if (0 != Tcl_GetIntFromObj(0, objv[1], &index)) {
04989       Tcl_SetResult(interp, "emc_probe_index: <index> must be an integer", TCL_VOLATILE);
04990       return TCL_ERROR;
04991     }
04992     sendSetProbeIndex(index);
04993   }
04994 
04995   if (emcUpdateType == EMC_UPDATE_AUTO) {
04996     updateStatus();
04997   }
04998 
04999   Tcl_SetObjResult(interp, Tcl_NewIntObj(emcStatus->motion.traj.probe_index));
05000   return TCL_OK;
05001 }
05002 
05003 int emc_probe_polarity(ClientData clientdata,
05004                    Tcl_Interp *interp,
05005                    int objc,
05006                    Tcl_Obj *CONST objv[])
05007 {
05008   int polarity;
05009 
05010   if (objc != 1) {
05011     if (0 != Tcl_GetIntFromObj(0, objv[1], &polarity)) {
05012       Tcl_SetResult(interp, "emc_probe_polarity: <which> must be an integer", TCL_VOLATILE);
05013       return TCL_ERROR;
05014     }
05015     sendSetProbePolarity(polarity);
05016   }
05017 
05018   if (emcUpdateType == EMC_UPDATE_AUTO) {
05019     updateStatus();
05020   }
05021 
05022   Tcl_SetObjResult(interp, Tcl_NewIntObj(emcStatus->motion.traj.probe_polarity));
05023   return TCL_OK;
05024 }
05025 
05026 int emc_probe_clear(ClientData clientdata,
05027                    Tcl_Interp *interp,
05028                    int objc,
05029                    Tcl_Obj *CONST objv[])
05030 {
05031   if (objc != 1) {
05032     Tcl_SetResult(interp, "emc_probe_clear: needs no args", TCL_VOLATILE);
05033     return TCL_ERROR;
05034   }
05035 
05036   if (emcUpdateType == EMC_UPDATE_AUTO) {
05037     updateStatus();
05038   }
05039 
05040   Tcl_SetObjResult(interp, Tcl_NewIntObj(sendClearProbeTrippedFlag()));
05041   return TCL_OK;
05042 }
05043 
05044 int emc_probe_value(ClientData clientdata,
05045                    Tcl_Interp *interp,
05046                    int objc,
05047                    Tcl_Obj *CONST objv[])
05048 {
05049   if (objc != 1) {
05050     Tcl_SetResult(interp, "emc_probe_value: needs no args", TCL_VOLATILE);
05051     return TCL_ERROR;
05052   }
05053 
05054   if (emcUpdateType == EMC_UPDATE_AUTO) {
05055     updateStatus();
05056   }
05057 
05058   Tcl_SetObjResult(interp, Tcl_NewIntObj(emcStatus->motion.traj.probeval));
05059   return TCL_OK;
05060 }
05061 
05062 int emc_probe_tripped(ClientData clientdata,
05063                    Tcl_Interp *interp,
05064                    int objc,
05065                    Tcl_Obj *CONST objv[])
05066 {
05067   if (objc != 1) {
05068     Tcl_SetResult(interp, "emc_probe_tripped: needs no args", TCL_VOLATILE);
05069     return TCL_ERROR;
05070   }
05071 
05072   if (emcUpdateType == EMC_UPDATE_AUTO) {
05073     updateStatus();
05074   }
05075 
05076   Tcl_SetObjResult(interp, Tcl_NewIntObj(emcStatus->motion.traj.probe_tripped));
05077   return TCL_OK;
05078 }
05079 
05080 int emc_probe_move(ClientData clientdata,
05081                    Tcl_Interp *interp,
05082                    int objc,
05083                    Tcl_Obj *CONST objv[])
05084 {
05085   double x,y,z;
05086 
05087   if (objc != 4) {
05088     Tcl_SetResult(interp, "emc_probe_move: <x> <y> <z>", TCL_VOLATILE);
05089     return TCL_ERROR;
05090   }
05091 
05092   if (0 != Tcl_GetDoubleFromObj(0, objv[1], &x)) {
05093       Tcl_SetResult(interp, "emc_probe_move: <x> must be a double", TCL_VOLATILE);
05094   }
05095   if (0 != Tcl_GetDoubleFromObj(0, objv[2], &y)) {
05096       Tcl_SetResult(interp, "emc_probe_move: <y> must be a double", TCL_VOLATILE);
05097   }
05098   if (0 != Tcl_GetDoubleFromObj(0, objv[3], &z)) {
05099       Tcl_SetResult(interp, "emc_probe_move: <z> must be a double", TCL_VOLATILE);
05100   }
05101 
05102   Tcl_SetObjResult(interp, Tcl_NewIntObj(sendProbe(x,y,z)));
05103   return TCL_OK;
05104 }
05105 
05106 static int emc_probed_pos(ClientData clientdata,
05107                            Tcl_Interp *interp,
05108                            int objc,
05109                            Tcl_Obj *CONST objv[])
05110 {
05111   int axis;
05112   Tcl_Obj *posobj;
05113 
05114   if (objc != 2) {
05115     Tcl_SetResult(interp,
05116                   "emc_probed_pos: need exactly 1 non-negative integer",
05117                   TCL_VOLATILE);
05118     return TCL_ERROR;
05119   }
05120 
05121   if (emcUpdateType == EMC_UPDATE_AUTO) {
05122     updateStatus();
05123   }
05124 
05125   if (TCL_OK == Tcl_GetIntFromObj(0, objv[1], &axis)) {
05126     if (axis == 0) {
05127       posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.probedPosition.tran.x));
05128     }
05129     else if (axis == 1) {
05130       posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.probedPosition.tran.y));
05131     }
05132     else if (axis == 2) {
05133       posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.probedPosition.tran.z));
05134     }
05135     else {
05136       if (axis == 3) {
05137         posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.probedPosition.a));
05138       }
05139       else if (axis == 4) {
05140         posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.probedPosition.b));
05141       }
05142       else if (axis == 5) {
05143         posobj = Tcl_NewDoubleObj(convertLinearUnits(emcStatus->motion.traj.probedPosition.c));
05144       }
05145       else {
05146         posobj = Tcl_NewDoubleObj(0.0);
05147       }
05148     }
05149   }
05150   else {
05151     Tcl_SetResult(interp, "emc_probed_pos: bad integer argument", TCL_VOLATILE);
05152     return TCL_ERROR;
05153   }
05154 
05155   Tcl_SetObjResult(interp, posobj);
05156   return TCL_OK;
05157 }
05158 // ********************************************************************
05159 //      Pendant read routine from /dev/psaux, /dev/ttyS0, or /dev/ttyS1
05160 // *********************************************************************
05161 
05162 static int emc_pendant(ClientData clientdata,
05163                      Tcl_Interp *interp,
05164                      int objc,
05165                      Tcl_Obj *CONST objv[])
05166 {
05167    FILE *inFile;
05168 
05169    char inBytes[5];
05170    const char *port;
05171    
05172    inBytes[0] = 0;
05173    inBytes[1] = 0;
05174    inBytes[2] = 0;
05175    inBytes[3] = 0;
05176    inBytes[4] = 0;
05177 
05178   if (objc == 2) {
05179     port = Tcl_GetStringFromObj(objv[1], 0);
05180     if ((! strcmp(port, "/dev/psaux")) | (! strcmp(port, "/dev/ttyS0")) | (! strcmp(port, "/dev/ttyS1")))
05181     {
05182    inFile = fopen(port, "r+b");
05183 
05184 if (inFile)
05185     {
05186     if (strcmp(port, "/dev/psaux")) {           // For Serial mice
05187                 inBytes[1] = fgetc(inFile);     // read the first Byte
05188                 if (inBytes[1] != 77) {         // If first byte not "M"
05189                 fputc(77, inFile);              // Request data resent
05190                 fflush(inFile);
05191                 inBytes[1] = fgetc(inFile);     // and hope it is correct
05192                  }
05193                 }
05194                 inBytes[4] = fgetc(inFile);     // Status byte
05195                 inBytes[2] = fgetc(inFile);     // Horizontal movement
05196                 inBytes[3] = fgetc(inFile);     // Vertical Movement
05197     }
05198     fclose(inFile);
05199 
05200     if (! strcmp(port, "/dev/psaux")) {         // For PS/2
05201     inBytes[0] = (inBytes[4] & 0x01);           // Left button
05202     inBytes[1] = (inBytes[4] & 0x02) >> 1;      // Right button
05203         }
05204     else {                                      // For serial mice
05205     inBytes[0] = (inBytes[4] & 0x20) >> 5;      // Left button
05206     inBytes[1] = (inBytes[4] & 0x10) >> 4;      // Right button
05207     if (inBytes[4] & 0x02) {
05208       inBytes[2] = inBytes[2] | 0xc0;
05209       }
05210     if (inBytes[4] & 0x08) {
05211       inBytes[3] = inBytes[3] | 0xc0;
05212       }
05213     }
05214 
05215         
05216     sprintf(interp->result, "%i %i %d %d %i",inBytes[0], inBytes[1], inBytes[2], inBytes[3], inBytes[4]);
05217     return TCL_OK;
05218     }
05219   }
05220   Tcl_SetResult(interp, "Need /dev/psaux, /dev/ttyS0 or /dev/ttyS1 as Arg", TCL_VOLATILE);
05221   return TCL_ERROR;
05222 }
05223 
05224 // *******************************************************************
05225 
05226 // provide some of the extended Tcl builtins not available for various plats
05227 
05228 #ifndef HAVE_TCL_EXTEND
05229 
05230 // "int", as in "int 3.9" which returns 3
05231 static int localint(ClientData clientdata,
05232                     Tcl_Interp *interp,
05233                     int objc,
05234                     Tcl_Obj *CONST objv[])
05235 {
05236   double val;
05237   char resstring[80];
05238 
05239   if (objc != 2) {
05240     // need exactly one arg
05241     Tcl_SetResult(interp, "wrong # args: should be \"int value\"", TCL_VOLATILE);
05242     return TCL_ERROR;
05243   }
05244 
05245   if (0 != Tcl_GetDoubleFromObj(0, objv[1], &val)) {
05246     resstring[0] = 0;
05247     strcat(resstring, "expected number but got \"");
05248     strncat(resstring, Tcl_GetStringFromObj(objv[1], 0),
05249             sizeof(resstring) - strlen(resstring) - 2);
05250     strcat(resstring, "\"");
05251     Tcl_SetResult(interp, resstring, TCL_VOLATILE);
05252     return TCL_ERROR;
05253   }
05254 
05255   Tcl_SetObjResult(interp, Tcl_NewIntObj((int) val));
05256   return TCL_OK;
05257 }
05258 
05259 // "round", as in "round 3.9" which returns 4
05260 static int localround(ClientData clientdata,
05261                       Tcl_Interp *interp,
05262                       int objc,
05263                       Tcl_Obj *CONST objv[])
05264 {
05265   double val;
05266   char resstring[80];
05267 
05268   if (objc != 2) {
05269     // need exactly one arg
05270     Tcl_SetResult(interp, "wrong # args: should be \"round value\"", TCL_VOLATILE);
05271     return TCL_ERROR;
05272   }
05273 
05274   if (0 != Tcl_GetDoubleFromObj(0, objv[1], &val)) {
05275     resstring[0] = 0;
05276     strcat(resstring, "expected number but got \"");
05277     strncat(resstring, Tcl_GetStringFromObj(objv[1], 0),
05278             sizeof(resstring) - strlen(resstring) - 2);
05279     strcat(resstring, "\"");
05280     Tcl_SetResult(interp, resstring, TCL_VOLATILE);
05281     return TCL_ERROR;
05282   }
05283 
05284   Tcl_SetObjResult(interp, Tcl_NewIntObj(val < 0.0 ? (int) (val - 0.5) : (int) (val + 0.5)));
05285   return TCL_OK;
05286 }
05287 
05288 
05289 
05290 #endif // WIN32
05291 
05292 int Tcl_AppInit(Tcl_Interp *interp)
05293 {
05294   /*
05295    * Call the init procedures for included packages.  Each call should
05296    * look like this:
05297    *
05298    * if (Mod_Init(interp) == TCL_ERROR) {
05299    *     return TCL_ERROR;
05300    * }
05301    *
05302    * where "Mod" is the name of the module.
05303    */
05304 
05305   if (Tcl_Init(interp) == TCL_ERROR) {
05306     return TCL_ERROR;
05307   }
05308 
05309 #ifdef HAVE_TCL_EXTEND
05310   if (Tclx_Init(interp) == TCL_ERROR) {
05311     return TCL_ERROR;
05312   }
05313 #endif
05314 
05315   if (Tk_Init(interp) == TCL_ERROR) {
05316       return TCL_ERROR;
05317   }
05318 
05319   /*
05320    * Call Tcl_CreateCommand for application-specific commands, if
05321    * they weren't already created by the init procedures called above.
05322    */
05323 
05324   Tcl_CreateObjCommand(interp, "emc_plat", emc_plat, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05325 
05326   Tcl_CreateObjCommand(interp, "emc_ini", emc_ini, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05327 
05328   Tcl_CreateObjCommand(interp, "emc_debug", emc_debug, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05329 
05330   Tcl_CreateObjCommand(interp, "emc_set_wait", emc_set_wait, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05331 
05332   Tcl_CreateObjCommand(interp, "emc_wait", emc_wait, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05333 
05334   Tcl_CreateObjCommand(interp, "emc_set_timeout", emc_set_timeout, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05335 
05336   Tcl_CreateObjCommand(interp, "emc_update", emc_update, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05337 
05338   Tcl_CreateObjCommand(interp, "emc_time", emc_time, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05339 
05340   Tcl_CreateObjCommand(interp, "emc_error", emc_error, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05341 
05342   Tcl_CreateObjCommand(interp, "emc_operator_text", emc_operator_text, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05343 
05344   Tcl_CreateObjCommand(interp, "emc_operator_display", emc_operator_display, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05345 
05346   Tcl_CreateObjCommand(interp, "emc_estop", emc_estop, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05347 
05348   Tcl_CreateObjCommand(interp, "emc_estop_in", emc_estop_in, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05349 
05350   Tcl_CreateObjCommand(interp, "emc_machine", emc_machine, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05351 
05352   Tcl_CreateObjCommand(interp, "emc_mode", emc_mode, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05353 
05354   Tcl_CreateObjCommand(interp, "emc_mist", emc_mist, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05355 
05356   Tcl_CreateObjCommand(interp, "emc_flood", emc_flood, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05357 
05358   Tcl_CreateObjCommand(interp, "emc_lube", emc_lube, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05359 
05360   Tcl_CreateObjCommand(interp, "emc_lube_level", emc_lube_level, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05361 
05362   Tcl_CreateObjCommand(interp, "emc_spindle", emc_spindle, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05363 
05364   Tcl_CreateObjCommand(interp, "emc_brake", emc_brake, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05365 
05366   Tcl_CreateObjCommand(interp, "emc_tool", emc_tool, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05367 
05368   Tcl_CreateObjCommand(interp, "emc_tool_offset", emc_tool_offset, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05369 
05370   Tcl_CreateObjCommand(interp, "emc_load_tool_table", emc_load_tool_table, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05371 
05372   Tcl_CreateObjCommand(interp, "emc_set_tool_offset", emc_set_tool_offset, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05373 
05374   Tcl_CreateObjCommand(interp, "emc_abs_cmd_pos", emc_abs_cmd_pos, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05375 
05376   Tcl_CreateObjCommand(interp, "emc_abs_act_pos", emc_abs_act_pos, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05377 
05378   Tcl_CreateObjCommand(interp, "emc_rel_cmd_pos", emc_rel_cmd_pos, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05379 
05380   Tcl_CreateObjCommand(interp, "emc_rel_act_pos", emc_rel_act_pos, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05381 
05382   Tcl_CreateObjCommand(interp, "emc_joint_pos", emc_joint_pos, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05383 
05384   Tcl_CreateObjCommand(interp, "emc_pos_offset", emc_pos_offset, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05385 
05386   Tcl_CreateObjCommand(interp, "emc_joint_limit", emc_joint_limit, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05387 
05388   Tcl_CreateObjCommand(interp, "emc_joint_fault", emc_joint_fault, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05389 
05390   Tcl_CreateObjCommand(interp, "emc_override_limit", emc_override_limit, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05391 
05392   Tcl_CreateObjCommand(interp, "emc_joint_homed", emc_joint_homed, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05393 
05394   Tcl_CreateObjCommand(interp, "emc_mdi", emc_mdi, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05395 
05396   Tcl_CreateObjCommand(interp, "emc_home", emc_home, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05397 
05398   Tcl_CreateObjCommand(interp, "emc_jog_stop", emc_jog_stop, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05399 
05400   Tcl_CreateObjCommand(interp, "emc_jog", emc_jog, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05401 
05402   Tcl_CreateObjCommand(interp, "emc_jog_incr", emc_jog_incr, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05403 
05404   Tcl_CreateObjCommand(interp, "emc_feed_override", emc_feed_override, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05405 
05406   Tcl_CreateObjCommand(interp, "emc_task_plan_init", emc_task_plan_init, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05407 
05408   Tcl_CreateObjCommand(interp, "emc_open", emc_open, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05409 
05410   Tcl_CreateObjCommand(interp, "emc_run", emc_run, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05411 
05412   Tcl_CreateObjCommand(interp, "emc_pause", emc_pause, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05413 
05414   Tcl_CreateObjCommand(interp, "emc_resume", emc_resume, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05415 
05416   Tcl_CreateObjCommand(interp, "emc_step", emc_step, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05417 
05418   Tcl_CreateObjCommand(interp, "emc_abort", emc_abort, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05419 
05420   Tcl_CreateObjCommand(interp, "emc_program", emc_program, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05421 
05422   Tcl_CreateObjCommand(interp, "emc_program_line", emc_program_line, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05423 
05424   Tcl_CreateObjCommand(interp, "emc_program_status", emc_program_status, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05425 
05426   Tcl_CreateObjCommand(interp, "emc_program_codes", emc_program_codes, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05427 
05428   Tcl_CreateObjCommand(interp, "emc_joint_type", emc_joint_type, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05429 
05430   Tcl_CreateObjCommand(interp, "emc_joint_units", emc_joint_units, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05431 
05432   Tcl_CreateObjCommand(interp, "emc_program_linear_units", emc_program_linear_units, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05433 
05434   Tcl_CreateObjCommand(interp, "emc_program_angular_units", emc_program_angular_units, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05435 
05436   Tcl_CreateObjCommand(interp, "emc_user_linear_units", emc_user_linear_units, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05437 
05438   Tcl_CreateObjCommand(interp, "emc_user_angular_units", emc_user_angular_units, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05439 
05440   Tcl_CreateObjCommand(interp, "emc_display_linear_units", emc_display_linear_units, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05441 
05442   Tcl_CreateObjCommand(interp, "emc_display_angular_units", emc_display_angular_units, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05443 
05444   Tcl_CreateObjCommand(interp, "emc_linear_unit_conversion", emc_linear_unit_conversion, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05445 
05446   Tcl_CreateObjCommand(interp, "emc_angular_unit_conversion", emc_angular_unit_conversion, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05447 
05448   Tcl_CreateObjCommand(interp, "emc_task_heartbeat", emc_task_heartbeat, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05449 
05450   Tcl_CreateObjCommand(interp, "emc_task_command", emc_task_command, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05451 
05452   Tcl_CreateObjCommand(interp, "emc_task_command_number", emc_task_command_number, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05453 
05454   Tcl_CreateObjCommand(interp, "emc_task_command_status", emc_task_command_status, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05455 
05456   Tcl_CreateObjCommand(interp, "emc_io_heartbeat", emc_io_heartbeat, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05457 
05458   Tcl_CreateObjCommand(interp, "emc_io_command", emc_io_command, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05459 
05460   Tcl_CreateObjCommand(interp, "emc_io_command_number", emc_io_command_number, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05461 
05462   Tcl_CreateObjCommand(interp, "emc_io_command_status", emc_io_command_status, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05463 
05464   Tcl_CreateObjCommand(interp, "emc_motion_heartbeat", emc_motion_heartbeat, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05465 
05466   Tcl_CreateObjCommand(interp, "emc_motion_command", emc_motion_command, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05467 
05468   Tcl_CreateObjCommand(interp, "emc_motion_command_number", emc_motion_command_number, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05469 
05470   Tcl_CreateObjCommand(interp, "emc_motion_command_status", emc_motion_command_status, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05471 
05472   Tcl_CreateObjCommand(interp, "emc_axis_gains", emc_axis_gains, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05473 
05474   Tcl_CreateObjCommand(interp, "emc_axis_set_output", emc_axis_set_output, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05475 
05476   Tcl_CreateObjCommand(interp, "emc_axis_load_comp", emc_axis_load_comp, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05477 
05478   Tcl_CreateObjCommand(interp, "emc_axis_alter", emc_axis_alter, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05479 
05480   Tcl_CreateObjCommand(interp, "emc_axis_enable", emc_axis_enable, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05481 
05482   Tcl_CreateObjCommand(interp, "emc_log_open", emc_log_open, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05483 
05484   Tcl_CreateObjCommand(interp, "emc_log_start", emc_log_start, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05485 
05486   Tcl_CreateObjCommand(interp, "emc_log_stop", emc_log_stop, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05487 
05488   Tcl_CreateObjCommand(interp, "emc_log_close", emc_log_close, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05489 
05490   Tcl_CreateObjCommand(interp, "emc_log_isopen", emc_log_isopen, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05491 
05492   Tcl_CreateObjCommand(interp, "emc_log_islogging", emc_log_islogging, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05493 
05494   Tcl_CreateObjCommand(interp, "emc_log_numpoints", emc_log_numpoints, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05495 
05496   Tcl_CreateObjCommand(interp, "emc_teleop_enable", emc_teleop_enable, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05497 
05498   Tcl_CreateObjCommand(interp, "emc_kinematics_type", emc_kinematics_type, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05499 
05500   Tcl_CreateObjCommand(interp, "emc_probe_index", emc_probe_index, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05501 
05502   Tcl_CreateObjCommand(interp, "emc_probe_polarity", emc_probe_polarity, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05503 
05504   Tcl_CreateObjCommand(interp, "emc_probe_clear", emc_probe_clear, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05505   Tcl_CreateObjCommand(interp, "emc_probe_value", emc_probe_value, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05506   Tcl_CreateObjCommand(interp, "emc_probe_tripped", emc_probe_tripped, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05507   Tcl_CreateObjCommand(interp, "emc_probe_move", emc_probe_move, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05508   Tcl_CreateObjCommand(interp, "emc_probed_pos", emc_probed_pos, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05509 
05510   Tcl_CreateObjCommand(interp, "emc_pendant", emc_pendant, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05511 
05512   // provide builtins that may have been left out
05513 
05514 #ifndef HAVE_TCL_EXTEND
05515   Tcl_CreateObjCommand(interp, "int", localint, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05516 
05517   Tcl_CreateObjCommand(interp, "round", localround, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
05518 #endif // WIN32
05519 
05520   /*
05521    * Specify a user-specific startup file to invoke if the application
05522    * is run interactively.  Typically the startup file is "~/.apprc"
05523    * where "app" is the name of the application.  If this line is deleted
05524    * then no user-specific startup file will be run under any conditions.
05525    */
05526 
05527   Tcl_SetVar(interp, "tcl_rcFileName", "~/.emcshrc", TCL_GLOBAL_ONLY);
05528 
05529   // set app-specific global variables
05530   Tcl_SetVar(interp, "EMC_INIFILE", EMC_INIFILE, TCL_GLOBAL_ONLY);
05531 
05532   return TCL_OK;
05533 }
05534 
05535 static int iniLoad(const char *filename)
05536 {
05537   INIFILE inifile;
05538   const char *inistring;
05539   char displayString[INIFILE_MAX_LINELEN] = "";
05540   int t;
05541   int i;
05542 
05543   // open it
05544   if (-1 == inifile.open(filename)) {
05545     return -1;
05546   }
05547 
05548   if (NULL != (inistring = inifile.find("DEBUG", "EMC"))) {
05549     // copy to global
05550     if (1 != sscanf(inistring, "%i", &EMC_DEBUG)) {
05551       EMC_DEBUG = 0;
05552     }
05553   }
05554   else {
05555     // not found, use default
05556     EMC_DEBUG = 0;
05557   }
05558 
05559   if (NULL != (inistring = inifile.find("NML_FILE", "EMC"))) {
05560     // copy to global
05561     strcpy(EMC_NMLFILE, inistring);
05562   }
05563   else {
05564     // not found, use default
05565   }
05566 
05567   for (t = 0; t < EMC_AXIS_MAX; t++) {
05568     jogPol[t] = 1;              // set to default
05569     sprintf(displayString, "AXIS_%d", t);
05570     if (NULL != (inistring = 
05571                  inifile.find("JOGGING_POLARITY", displayString)) &&
05572         1 == sscanf(inistring, "%d", &i) &&
05573         i == 0) {
05574       // it read as 0, so override default
05575       jogPol[t] = 0;
05576     }
05577   }
05578 
05579   if (NULL != (inistring = inifile.find("LINEAR_UNITS", "DISPLAY"))) {
05580     if (! strcmp(inistring, "AUTO")) {
05581       linearUnitConversion = LINEAR_UNITS_AUTO;
05582     }
05583     else if (! strcmp(inistring, "INCH")) {
05584       linearUnitConversion = LINEAR_UNITS_INCH;
05585     }
05586     else if (! strcmp(inistring, "MM")) {
05587       linearUnitConversion = LINEAR_UNITS_MM;
05588     }
05589     else if (! strcmp(inistring, "CM")) {
05590       linearUnitConversion = LINEAR_UNITS_CM;
05591     }
05592   }
05593   else {
05594     // not found, leave default alone
05595   }
05596 
05597   if (NULL != (inistring = inifile.find("ANGULAR_UNITS", "DISPLAY"))) {
05598     if (! strcmp(inistring, "AUTO")) {
05599       angularUnitConversion = ANGULAR_UNITS_AUTO;
05600     }
05601     else if (! strcmp(inistring, "DEG")) {
05602       angularUnitConversion = ANGULAR_UNITS_DEG;
05603     }
05604     else if (! strcmp(inistring, "RAD")) {
05605       angularUnitConversion = ANGULAR_UNITS_RAD;
05606     }
05607     else if (! strcmp(inistring, "GRAD")) {
05608       angularUnitConversion = ANGULAR_UNITS_GRAD;
05609     }
05610   }
05611   else {
05612     // not found, leave default alone
05613   }
05614 
05615   // close it
05616   inifile.close();
05617 
05618   return 0;
05619 }
05620 
05621 static void sigQuit(int sig)
05622 {
05623   thisQuit((ClientData) 0);
05624 }
05625 
05626 int main(int argc, char *argv[])
05627 {
05628   // blank out the annoying RCS version message
05629   rcs_version_printed = 1;
05630 
05631   // process command line args
05632   if (0 != emcGetArgs(argc, argv)) {
05633     rcs_print_error("error in argument list\n");
05634     exit(1);
05635   }
05636 
05637   // get configuration information
05638   iniLoad(EMC_INIFILE);
05639 
05640   // init NML
05641   if (0 != tryNml()) {
05642     rcs_print_error("can't connect to emc\n");
05643     thisQuit(NULL);
05644     exit(1);
05645   }
05646 
05647   // get current serial number, and save it for restoring when we quit
05648   // so as not to interfere with real operator interface
05649   updateStatus();
05650   emcCommandSerialNumber = emcStatus->echo_serial_number;
05651   saveEmcCommandSerialNumber = emcStatus->echo_serial_number;
05652 
05653   // attach our quit function to exit
05654   Tcl_CreateExitHandler(thisQuit, (ClientData) 0);
05655 
05656   // attach our quit function to SIGINT
05657   signal(SIGINT, sigQuit);
05658 
05659   //  TclX_Main(argc, argv, Tcl_AppInit);
05660   Tk_Main(argc, argv, Tcl_AppInit);
05661 
05662   return 0;
05663 }

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