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