/ src / emc / task / taskclass.cc
taskclass.cc
  1  // this is a slide-in replacement for the functions in iotaskintf.cc
  2  // iotaskintf functions are made into class methods and are the default
  3  // methods of TaskClass which may be overridden by Python methods
  4  
  5  
  6  /********************************************************************
  7  * Description: iotaskintf.cc
  8  *   NML interface functions for IO
  9  *
 10  *   Based on a work by Fred Proctor & Will Shackleford
 11  *
 12  * Author:
 13  * License: GPL Version 2
 14  * System: Linux
 15  *
 16  * Copyright (c) 2004 All rights reserved.
 17  *
 18  * Last change:
 19  ********************************************************************/
 20  
 21  #include <math.h>		// fabs()
 22  #include <float.h>		// DBL_MAX
 23  #include <string.h>		// memcpy() strncpy()
 24  #include <stdlib.h>		// malloc()
 25  #include <sys/wait.h>
 26  
 27  #include "rcs.hh"		// RCS_CMD_CHANNEL, etc.
 28  #include "rcs_print.hh"
 29  #include "timer.hh"             // esleep, etc.
 30  #include "emc.hh"		// EMC NML
 31  #include "emc_nml.hh"
 32  #include "emcglb.h"		// EMC_INIFILE
 33  
 34  #include "initool.hh"
 35  
 36  #include "python_plugin.hh"
 37  #include "taskclass.hh"
 38  
 39  #define BOOST_PYTHON_MAX_ARITY 4
 40  #include <boost/python/dict.hpp>
 41  #include <boost/python/extract.hpp>
 42  #include <boost/python/object.hpp>
 43  #include <boost/python/tuple.hpp>
 44  namespace bp = boost::python;
 45  
 46  // Python plugin interface
 47  #define TASK_MODULE "task"
 48  #define TASK_VAR "pytask"
 49  #define PLUGIN_CALL "plugin_call"
 50  
 51  extern PythonPlugin *python_plugin;  // exported by python_plugin.cc
 52  #define PYUSABLE (((python_plugin) != NULL) && (python_plugin->usable()))
 53  extern int return_int(const char *funcname, bp::object &retval);
 54  Task *task_methods;
 55  
 56  // IO INTERFACE
 57  
 58  // the NML channels to the EMCIO controller
 59  static RCS_CMD_CHANNEL *emcIoCommandBuffer = 0;
 60  static RCS_STAT_CHANNEL *emcIoStatusBuffer = 0;
 61  
 62  // global status structure
 63  EMC_IO_STAT *emcIoStatus = 0;
 64  
 65  // serial number for communication
 66  static int emcIoCommandSerialNumber = 0;
 67  static double EMCIO_BUFFER_GET_TIMEOUT = 5.0;
 68  
 69  static int forceCommand(RCS_CMD_MSG *msg);
 70  
 71  static int emcioNmlGet()
 72  {
 73      int retval = 0;
 74      double start_time;
 75      RCS_PRINT_DESTINATION_TYPE orig_dest;
 76      if (emcIoCommandBuffer == 0) {
 77  	orig_dest = get_rcs_print_destination();
 78  	set_rcs_print_destination(RCS_PRINT_TO_NULL);
 79  	start_time = etime();
 80  	while (start_time - etime() < EMCIO_BUFFER_GET_TIMEOUT) {
 81  	    emcIoCommandBuffer =
 82  		new RCS_CMD_CHANNEL(emcFormat, "toolCmd", "emc",
 83  				    emc_nmlfile);
 84  	    if (!emcIoCommandBuffer->valid()) {
 85  		delete emcIoCommandBuffer;
 86  		emcIoCommandBuffer = 0;
 87  	    } else {
 88  		break;
 89  	    }
 90  	    esleep(0.1);
 91  	}
 92  	set_rcs_print_destination(orig_dest);
 93      }
 94  
 95      if (emcIoCommandBuffer == 0) {
 96  	emcIoCommandBuffer =
 97  	    new RCS_CMD_CHANNEL(emcFormat, "toolCmd", "emc", emc_nmlfile);
 98  	if (!emcIoCommandBuffer->valid()) {
 99  	    delete emcIoCommandBuffer;
100  	    emcIoCommandBuffer = 0;
101  	    retval = -1;
102  	}
103      }
104  
105      if (emcIoStatusBuffer == 0) {
106  	orig_dest = get_rcs_print_destination();
107  	set_rcs_print_destination(RCS_PRINT_TO_NULL);
108  	start_time = etime();
109  	while (start_time - etime() < EMCIO_BUFFER_GET_TIMEOUT) {
110  	    emcIoStatusBuffer =
111  		new RCS_STAT_CHANNEL(emcFormat, "toolSts", "emc",
112  				     emc_nmlfile);
113  	    if (!emcIoStatusBuffer->valid()) {
114  		delete emcIoStatusBuffer;
115  		emcIoStatusBuffer = 0;
116  	    } else {
117  		emcIoStatus =
118  		    (EMC_IO_STAT *) emcIoStatusBuffer->get_address();
119  		// capture serial number for next send
120  		emcIoCommandSerialNumber = emcIoStatus->echo_serial_number;
121  		break;
122  	    }
123  	    esleep(0.1);
124  	}
125  	set_rcs_print_destination(orig_dest);
126      }
127  
128      if (emcIoStatusBuffer == 0) {
129  	emcIoStatusBuffer =
130  	    new RCS_STAT_CHANNEL(emcFormat, "toolSts", "emc", emc_nmlfile);
131  	if (!emcIoStatusBuffer->valid()
132  	    || EMC_IO_STAT_TYPE != emcIoStatusBuffer->peek()) {
133  	    delete emcIoStatusBuffer;
134  	    emcIoStatusBuffer = 0;
135  	    emcIoStatus = 0;
136  	    retval = -1;
137  	} else {
138  	    emcIoStatus = (EMC_IO_STAT *) emcIoStatusBuffer->get_address();
139  	    // capture serial number for next send
140  	    emcIoCommandSerialNumber = emcIoStatus->echo_serial_number;
141  	}
142      }
143  
144      return retval;
145  }
146  
147  static RCS_CMD_MSG *last_io_command = 0;
148  static long largest_io_command_size = 0;
149  
150  /*
151    sendCommand() waits until any currently executing command has finished,
152    then writes the given command.*/
153  /*! \todo
154    FIXME: Not very RCS-like to wait for status done here. (wps)
155  */
156  static int sendCommand(RCS_CMD_MSG * msg)
157  {
158      // need command buffer to be there
159      if (0 == emcIoCommandBuffer) {
160  	return -1;
161      }
162      // need status buffer also, to check for command received
163      if (0 == emcIoStatusBuffer || !emcIoStatusBuffer->valid()) {
164  	return -1;
165      }
166  
167      // always force-queue an abort
168      if (msg->type == EMC_TOOL_ABORT_TYPE) {
169  	// just queue the abort and call it a day
170  	int rc = forceCommand(msg);
171  	if (rc) {
172  	    rcs_print_error("forceCommand(EMC_TOOL_ABORT) returned %d\n", rc);
173  	}
174  	return 0;
175      }
176  
177      double send_command_timeout = etime() + 5.0;
178  
179      // check if we're executing, and wait until we're done
180      while (etime() < send_command_timeout) {
181  	emcIoStatusBuffer->peek();
182  	if (emcIoStatus->echo_serial_number != emcIoCommandSerialNumber ||
183  	    emcIoStatus->status == RCS_EXEC) {
184  	    esleep(0.001);
185  	    continue;
186  	} else {
187  	    break;
188  	}
189      }
190  
191      if (emcIoStatus->echo_serial_number != emcIoCommandSerialNumber ||
192  	emcIoStatus->status == RCS_EXEC) {
193  	// Still not done, must have timed out.
194  	rcs_print_error
195  	    ("Command to IO level (%s:%s) timed out waiting for last command done. \n",
196  	     emcSymbolLookup(msg->type), emcIoCommandBuffer->msg2str(msg));
197  	rcs_print_error
198  	    ("emcIoStatus->echo_serial_number=%d, emcIoCommandSerialNumber=%d, emcIoStatus->status=%d\n",
199  	     emcIoStatus->echo_serial_number, emcIoCommandSerialNumber,
200  	     emcIoStatus->status);
201  	if (0 != last_io_command) {
202  	    rcs_print_error("Last command sent to IO level was (%s:%s)\n",
203  			    emcSymbolLookup(last_io_command->type),
204  			    emcIoCommandBuffer->msg2str(last_io_command));
205  	}
206  	return -1;
207      }
208      // now we can send
209      msg->serial_number = ++emcIoCommandSerialNumber;
210      if (0 != emcIoCommandBuffer->write(msg)) {
211  	rcs_print_error("Failed to send command to  IO level (%s:%s)\n",
212  			emcSymbolLookup(msg->type),
213  			emcIoCommandBuffer->msg2str(msg));
214  	return -1;
215      }
216  
217      if (largest_io_command_size < msg->size) {
218  	largest_io_command_size = std::max<long>(msg->size, 4096);
219  	last_io_command = (RCS_CMD_MSG *) realloc(last_io_command, largest_io_command_size);
220      }
221  
222      if (0 != last_io_command) {
223  	memcpy(last_io_command, msg, msg->size);
224      }
225  
226      return 0;
227  }
228  
229  /*
230    forceCommand() writes the given command regardless of the executing
231    status of any previous command.
232  */
233  static int forceCommand(RCS_CMD_MSG * msg)
234  {
235      // need command buffer to be there
236      if (0 == emcIoCommandBuffer) {
237  	return -1;
238      }
239      // need status buffer also, to check for command received
240      if (0 == emcIoStatusBuffer || !emcIoStatusBuffer->valid()) {
241  	return -1;
242      }
243      // send it immediately
244      msg->serial_number = ++emcIoCommandSerialNumber;
245      if (0 != emcIoCommandBuffer->write(msg)) {
246  	rcs_print_error("Failed to send command to  IO level (%s:%s)\n",
247  			emcSymbolLookup(msg->type),
248  			emcIoCommandBuffer->msg2str(msg));
249  	return -1;
250      }
251  
252      if (largest_io_command_size < msg->size) {
253  	largest_io_command_size = std::max<long>(msg->size, 4096);
254  	last_io_command = (RCS_CMD_MSG *) realloc(last_io_command, largest_io_command_size);
255      }
256  
257      if (0 != last_io_command) {
258  	memcpy(last_io_command, msg, msg->size);
259      }
260  
261      return 0;
262  }
263  
264  // glue
265  
266  int emcIoInit() { return task_methods->emcIoInit(); }
267  
268  int emcIoHalt() {
269      try {
270  	return task_methods->emcIoHalt();
271      } catch( bp::error_already_set ) {
272  	std::string msg = handle_pyerror();
273  	rcs_print("emcIoHalt(): %s\n", msg.c_str());
274  	PyErr_Clear();
275  	return -1;
276      }
277  }
278  
279  
280  int emcIoAbort(int reason) { return task_methods->emcIoAbort(reason); }
281  int emcIoSetDebug(int debug) { return task_methods->emcIoSetDebug(debug); }
282  int emcAuxEstopOn()  { return task_methods->emcAuxEstopOn(); }
283  int emcAuxEstopOff() { return task_methods->emcAuxEstopOff(); }
284  int emcCoolantMistOn() { return task_methods->emcCoolantMistOn(); }
285  int emcCoolantMistOff() { return task_methods->emcCoolantMistOff(); }
286  int emcCoolantFloodOn() { return task_methods->emcCoolantFloodOn(); }
287  int emcCoolantFloodOff() { return task_methods->emcCoolantFloodOff(); }
288  int emcLubeOn() { return task_methods->emcLubeOn(); }
289  int emcLubeOff() { return task_methods->emcLubeOff(); }
290  int emcToolPrepare(int p, int tool) { return task_methods->emcToolPrepare(p, tool); }
291  int emcToolStartChange() { return task_methods->emcToolStartChange(); }
292  int emcToolLoad() { return task_methods->emcToolLoad(); }
293  int emcToolUnload()  { return task_methods->emcToolUnload(); }
294  int emcToolLoadToolTable(const char *file) { return task_methods->emcToolLoadToolTable(file); }
295  int emcToolSetOffset(int pocket, int toolno, EmcPose offset, double diameter,
296                       double frontangle, double backangle, int orientation) {
297      return task_methods->emcToolSetOffset( pocket,  toolno,  offset,  diameter,
298  					   frontangle,  backangle,  orientation); }
299  int emcToolSetNumber(int number) { return task_methods->emcToolSetNumber(number); }
300  int emcIoUpdate(EMC_IO_STAT * stat) { return task_methods->emcIoUpdate(stat); }
301  int emcIoPluginCall(EMC_IO_PLUGIN_CALL *call_msg) { return task_methods->emcIoPluginCall(call_msg->len,
302  											   call_msg->call); }
303  static const char *instance_name = "task_instance";
304  
305  int emcTaskOnce(const char *filename)
306  {
307      bp::object retval;
308      bp::tuple arg;
309      bp::dict kwarg;
310  
311      // initialize the Python plugin singleton
312      // Interp is already instantiated but not yet fully configured
313      // both Task and Interp use it - first to call configure() instantiates the Python part
314      // NB: the interpreter.this global will appear only after Interp.init()
315  
316      extern struct _inittab builtin_modules[];
317      if (!PythonPlugin::instantiate(builtin_modules)) {
318  	rcs_print("emcTaskOnce: cant instantiate Python plugin\n");
319  	goto no_pytask;
320      }
321      if (python_plugin->configure(filename, "PYTHON") == PLUGIN_OK) {
322  	if (emc_debug & EMC_DEBUG_PYTHON_TASK) {
323  	    rcs_print("emcTaskOnce: Python plugin configured\n");
324  	}
325      } else {
326  	goto no_pytask;
327      }
328      if (PYUSABLE) {
329  	// extract the instance of Python Task()
330  	try {
331  	    bp::object task_namespace =  python_plugin->main_namespace[TASK_MODULE].attr("__dict__");;
332  	    bp::object result = task_namespace[TASK_VAR];
333  	    bp::extract<Task *> typetest(result);
334  	    if (typetest.check()) {
335  		task_methods = bp::extract< Task * >(result);
336  	    } else {
337  		rcs_print("cant extract a Task instance out of '%s'\n", instance_name);
338  		task_methods = NULL;
339  	    }
340  	} catch( bp::error_already_set ) {
341  	    std::string msg = handle_pyerror();
342  	    if (emc_debug & EMC_DEBUG_PYTHON_TASK) {
343  		// this really just means the task python backend wasnt configured.
344  		rcs_print("emcTaskOnce: extract(%s): %s\n", instance_name, msg.c_str());
345  	    }
346  	    PyErr_Clear();
347  	}
348      }
349   no_pytask:
350      if (task_methods == NULL) {
351  	if (emc_debug & EMC_DEBUG_PYTHON_TASK) {
352  	    rcs_print("emcTaskOnce: no Python Task() instance available, using default iocontrol-based task methods\n");
353  	}
354  	task_methods = new Task();
355      }
356      return 0;
357  }
358  
359  // if using a Python-based HAL module in task, normal HAL_FILE's are run too early.
360  // execute those here if specified via POSTTASK_HALFILE in ini ,
361  int emcRunHalFiles(const char *filename)
362  {
363      IniFile inifile;
364      const char *inistring;
365      int lineno,status;
366      int n = 1;
367      pid_t pid;
368  
369      if (inifile.Open(filename) == false) {
370  	return -1;
371      }
372      while (NULL != (inistring = inifile.Find("POSTTASK_HALFILE", "HAL",
373  					     n, &lineno))) {
374  	if ((pid = vfork()) < 0)
375  	    perror("vfork()");
376  	else if (pid == 0) {
377  	    execlp("halcmd", "halcmd","-i",filename,"-f",inistring, NULL);
378  	    perror("execlp halcmd");
379  	} else {
380  	    if ((waitpid (pid, &status, 0) == pid) &&  WEXITSTATUS(status))
381  		rcs_print("'halcmd -i %s -f %s' exited with  %d\n",
382  		       filename, inistring, WEXITSTATUS(status));
383  	}
384  	n++;
385      }
386      return 0;
387  }
388  
389  // task callables are expected to return an int.
390  // extract it, and return that
391  // else complain.
392  // Also fail with an operator error if we caused an exception.
393  int return_int(const char *funcname, PyObject *retval)
394  {
395      int status = python_plugin->plugin_status();
396  
397      if (status == PLUGIN_EXCEPTION) {
398  	emcOperatorError(status,"return_int(%s): %s",
399  			 funcname, python_plugin->last_exception().c_str());
400  	return -1;
401      }
402      if ((retval != Py_None) &&
403  	(PyInt_Check(retval))) {
404  	return PyInt_AS_LONG(retval);
405      } else {
406  	emcOperatorError(0, "return_int(%s): expected int return value, got '%s' (%s)",
407  			 funcname,
408  			 PyString_AsString(retval),
409  			 retval->ob_type->tp_name);
410  	Py_XDECREF(retval);
411  	return -1;
412      }
413  }
414  
415  int emcPluginCall(EMC_EXEC_PLUGIN_CALL *call_msg)
416  {
417      if (PYUSABLE) {
418  	bp::object retval;
419  	bp::object arg = bp::make_tuple(bp::object(call_msg->call));
420  	bp::dict kwarg;
421  
422  	python_plugin->call(TASK_MODULE, PLUGIN_CALL, arg, kwarg, retval);
423  	return return_int(PLUGIN_CALL, retval.ptr());
424  
425      } else {
426  	emcOperatorError(0, "emcPluginCall: Python plugin not initialized");
427  	return -1;
428      }
429  }
430  
431  // int emcAbortCleanup(int reason, const char *message)
432  // {
433  //     int status = interp.on_abort(reason,message);
434  //     if (status > INTERP_MIN_ERROR)
435  // 	print_interp_error(status);
436  //     return status;
437  // }
438  
439  extern "C" void initemctask();
440  extern "C" void initinterpreter();
441  extern "C" void initemccanon();
442  struct _inittab builtin_modules[] = {
443      { (char *) "interpreter", initinterpreter },
444      { (char *) "emccanon", initemccanon },
445      { (char *) "emctask", initemctask },
446      // any others...
447      { NULL, NULL }
448  };
449  
450  
451  
452  Task::Task() : use_iocontrol(0), random_toolchanger(0) {
453  
454      IniFile inifile;
455  
456      ini_filename = emc_inifile;
457  
458      if (inifile.Open(ini_filename)) {
459  	use_iocontrol = (inifile.Find("EMCIO", "EMCIO") != NULL);
460  	inifile.Find(&random_toolchanger, "RANDOM_TOOLCHANGER", "EMCIO");
461  	const char *t;
462  	if ((t = inifile.Find("TOOL_TABLE", "EMCIO")) != NULL)
463  	    tooltable_filename = strdup(t);
464      }
465      if (!use_iocontrol) {
466  	for(int i = 0; i < CANON_POCKETS_MAX; i++) {
467  	    ttcomments[i] = (char *)malloc(CANON_TOOL_ENTRY_LEN);
468  	}
469      }
470  
471  };
472  
473  
474  Task::~Task() {};
475  
476  // NML commands
477  
478  int Task::emcIoInit()
479  {
480      EMC_TOOL_INIT ioInitMsg;
481  
482      // get NML buffer to emcio
483      if (0 != emcioNmlGet()) {
484  	rcs_print_error("emcioNmlGet() failed.\n");
485  	return -1;
486      }
487  
488      if (0 != iniTool(emc_inifile)) {
489  	return -1;
490      }
491      // send init command to emcio
492      if (forceCommand(&ioInitMsg)) {
493  	rcs_print_error("Can't forceCommand(ioInitMsg)\n");
494  	return -1;
495      }
496  
497      return 0;
498  }
499  
500  int Task::emcIoHalt()
501  {
502      EMC_TOOL_HALT ioHaltMsg;
503  
504      // send halt command to emcio
505      if (emcIoCommandBuffer != 0) {
506  	forceCommand(&ioHaltMsg);
507      }
508      // clear out the buffers
509  
510      if (emcIoStatusBuffer != 0) {
511  	delete emcIoStatusBuffer;
512  	emcIoStatusBuffer = 0;
513  	emcIoStatus = 0;
514      }
515  
516      if (emcIoCommandBuffer != 0) {
517  	delete emcIoCommandBuffer;
518  	emcIoCommandBuffer = 0;
519      }
520  
521      if (last_io_command) {
522          free(last_io_command);
523          last_io_command = 0;
524      }
525  
526      return 0;
527  }
528  
529  int Task::emcIoAbort(int reason)
530  {
531      EMC_TOOL_ABORT ioAbortMsg;
532  
533      ioAbortMsg.reason = reason;
534      // send abort command to emcio
535      sendCommand(&ioAbortMsg);
536  
537      return 0;
538  }
539  
540  int Task::emcIoSetDebug(int debug)
541  {
542      EMC_SET_DEBUG ioDebugMsg;
543  
544      ioDebugMsg.debug = debug;
545  
546      return sendCommand(&ioDebugMsg);
547  }
548  
549  int Task::emcAuxEstopOn()
550  {
551      EMC_AUX_ESTOP_ON estopOnMsg;
552  
553      return forceCommand(&estopOnMsg);
554  }
555  
556  int Task::emcAuxEstopOff()
557  {
558      EMC_AUX_ESTOP_OFF estopOffMsg;
559  
560      return forceCommand(&estopOffMsg); //force the EstopOff message
561  }
562  
563  int Task::emcCoolantMistOn()
564  {
565      EMC_COOLANT_MIST_ON mistOnMsg;
566  
567      sendCommand(&mistOnMsg);
568  
569      return 0;
570  }
571  
572  int Task::emcCoolantMistOff()
573  {
574      EMC_COOLANT_MIST_OFF mistOffMsg;
575  
576      sendCommand(&mistOffMsg);
577  
578      return 0;
579  }
580  
581  int Task::emcCoolantFloodOn()
582  {
583      EMC_COOLANT_FLOOD_ON floodOnMsg;
584  
585      sendCommand(&floodOnMsg);
586  
587      return 0;
588  }
589  
590  int Task::emcCoolantFloodOff()
591  {
592      EMC_COOLANT_FLOOD_OFF floodOffMsg;
593  
594      sendCommand(&floodOffMsg);
595  
596      return 0;
597  }
598  
599  int Task::emcLubeOn()
600  {
601      EMC_LUBE_ON lubeOnMsg;
602  
603      sendCommand(&lubeOnMsg);
604  
605      return 0;
606  }
607  
608  int Task::emcLubeOff()
609  {
610      EMC_LUBE_OFF lubeOffMsg;
611  
612      sendCommand(&lubeOffMsg);
613  
614      return 0;
615  }
616  
617  int Task::emcToolPrepare(int p, int tool)
618  {
619      EMC_TOOL_PREPARE toolPrepareMsg;
620  
621      toolPrepareMsg.pocket = p;
622      toolPrepareMsg.tool = tool;
623      sendCommand(&toolPrepareMsg);
624  
625      return 0;
626  }
627  
628  
629  int Task::emcToolStartChange()
630  {
631      EMC_TOOL_START_CHANGE toolStartChangeMsg;
632  
633      sendCommand(&toolStartChangeMsg);
634  
635      return 0;
636  }
637  
638  
639  int Task::emcToolLoad()
640  {
641      EMC_TOOL_LOAD toolLoadMsg;
642  
643      sendCommand(&toolLoadMsg);
644  
645      return 0;
646  }
647  
648  int Task::emcToolUnload()
649  {
650      EMC_TOOL_UNLOAD toolUnloadMsg;
651  
652      sendCommand(&toolUnloadMsg);
653  
654      return 0;
655  }
656  
657  int Task::emcToolLoadToolTable(const char *file)
658  {
659      EMC_TOOL_LOAD_TOOL_TABLE toolLoadToolTableMsg;
660  
661      strcpy(toolLoadToolTableMsg.file, file);
662  
663      sendCommand(&toolLoadToolTableMsg);
664  
665      return 0;
666  }
667  
668  int Task::emcToolSetOffset(int pocket, int toolno, EmcPose offset, double diameter,
669                       double frontangle, double backangle, int orientation)
670  {
671      EMC_TOOL_SET_OFFSET toolSetOffsetMsg;
672  
673      toolSetOffsetMsg.pocket = pocket;
674      toolSetOffsetMsg.toolno = toolno;
675      toolSetOffsetMsg.offset = offset;
676      toolSetOffsetMsg.diameter = diameter;
677      toolSetOffsetMsg.frontangle = frontangle;
678      toolSetOffsetMsg.backangle = backangle;
679      toolSetOffsetMsg.orientation = orientation;
680  
681      sendCommand(&toolSetOffsetMsg);
682  
683      return 0;
684  }
685  
686  int Task::emcToolSetNumber(int number)
687  {
688      EMC_TOOL_SET_NUMBER toolSetNumberMsg;
689  
690      toolSetNumberMsg.tool = number;
691  
692      sendCommand(&toolSetNumberMsg);
693  
694      return 0;
695  }
696  
697  // Status functions
698  
699  int Task::emcIoUpdate(EMC_IO_STAT * stat)
700  {
701      if (!use_iocontrol) {
702  	// there's no message to copy - Python directly operates on emcStatus and its io member
703  	return 0;
704      }
705      if (0 == emcIoStatusBuffer || !emcIoStatusBuffer->valid()) {
706  	return -1;
707      }
708  
709      switch (emcIoStatusBuffer->peek()) {
710      case -1:
711  	// error on CMS channel
712  	return -1;
713  	break;
714  
715      case 0:			// nothing new
716      case EMC_IO_STAT_TYPE:	// something new
717  	// drop out to copy
718  	break;
719  
720      default:
721  	// something else is in there
722  	return -1;
723  	break;
724      }
725  
726      // copy status
727      *stat = *emcIoStatus;
728  
729      /*
730         We need to check that the RCS_DONE isn't left over from the previous
731         command, by comparing the command number we sent with the command
732         number that emcio echoes. If they're different, then the command
733         hasn't been acknowledged yet and the state should be forced to be
734         RCS_EXEC. */
735      if (stat->echo_serial_number != emcIoCommandSerialNumber) {
736  	stat->status = RCS_EXEC;
737      }
738      //commented out because it keeps resetting the spindle speed to some odd value
739      //the speed gets set by the IO controller, no need to override it here (io takes care of increase/decrease speed too)
740      // stat->spindle.speed = spindleSpeed;
741  
742      return 0;
743  }
744  
745  int Task::emcIoPluginCall(int len, const char *msg)
746  {
747      if (emc_debug & EMC_DEBUG_PYTHON_TASK) {
748  	rcs_print("emcIoPluginCall(%d,%s) - no Python handler set\n",len,msg);
749      }
750      return 0;
751  }
752  
753  
754