/ src / hal / components / plasmac.comp
plasmac.comp
   1  component plasmac;
   2  
   3  description
   4  """
   5  
   6  A plasma cutting table control component for use with the LinuxCNC V2.8 or later.
   7  
   8  LinuxCNC V2.9 or later is required to use the "Paused Motion" feature.
   9  
  10  .I VERSION:
  11  .br
  12  0.100 - 19 Feb 2020
  13  
  14  .I SUMMARY:
  15  .br
  16  Usage of this component is demonstrated in the PlasmaC example configurations included with LinuxCNC.
  17  
  18  .I DISCLAIMER:
  19  .br
  20  THE AUTHOR OF THIS SOFTWARE ACCEPTS ABSOLUTELY NO LIABILITY FOR ANY HARM OR LOSS RESULTING FROM ITS USE.
  21  
  22  IT IS EXTREMELY UNWISE TO RELY ON SOFTWARE ALONE FOR SAFETY.
  23  
  24  Any machinery capable of harming persons must have provisions for completely stopping all motors and moving parts etc. before persons enter any danger area.
  25  
  26  All machinery must be designed to comply with local and national safety codes, and the author of this software can not, and does not, take any responsibility for such compliance.
  27  """;
  28  
  29  /* INPUT PINS */
  30  pin in  float   arc_fail_delay              "arc failure timeout (seconds)";
  31  pin in  float   arc_ok_high                 "maximum voltage level for Arc OK signal [mode 0] (volts)";
  32  pin in  bit     arc_ok_in                   "external arc ok input signal [mode 1 & mode 2]";
  33  pin in  float   arc_ok_low                  "minimum voltage level for Arc OK signal [mode 0] (volts)";
  34  pin in  s32     arc_max_starts              "maximum attempts at starting the arc";
  35  pin in  float   arc_voltage_in              "arc voltage input [mode 0 & mode 1] see Notes above";
  36  pin in  float   arc_voltage_offset          "offset to set arc voltage to 0 at 0 volts";
  37  pin in  float   arc_voltage_scale           "scale to convert arc_voltage input to actual volts";
  38  pin in  float   axis_z_min_limit            "axis z minimum limit, connect to ini.z.min-limit";
  39  pin in  float   axis_z_max_limit            "axis z maximum limit, connect to ini.z.max-limit";
  40  pin in  float   axis_z_position             "current z axis position, connect to joint.N.pos-fb";
  41  pin in  float   axis_x_position             "current x axis position, connect to axis.x.pos-cmd";
  42  pin in  float   axis_y_position             "current y axis position, connect to axis.y.pos-cmd";
  43  pin in  bit     breakaway                   "torch breakaway switch (optional, see float_switch)";
  44  pin in  bit     spotting_start              "start a new spot, connect to spindle.2.on";
  45  pin in  float   spotting_threshold          "threshold voltage to start spotting delay";
  46  pin in  float   spotting_time               "torch off delay after spotting threshold reached";
  47  pin in  bit     consumable_change           "change consumables in torch";
  48  pin in  bit     cornerlock_enable           "enable corner lock";
  49  pin in  float   cornerlock_threshold        "corner lock threshold (% of requested feed rate), speeds below this disable THC";
  50  pin in  float   current_velocity            "current machine velocity , connect to motion.current-vel";
  51  pin in  float   cut_feed_rate               "cut feed rate, set to 0 to use feed rate from gcod file (machine units per minute)";
  52  pin in  float   cut_height                  "cut height (machine units)";
  53  pin in  float   cut_volts                   "cut voltage (volts)";
  54  pin in  bit     cutting_start               "start a new cut, connect to spindle.0.on";
  55  pin in  bit     external_estop              "external estop input";
  56  pin in  float   feed_reduction              "reduce adaptive feed to this percentage (connect to motion.analog-out-03)";
  57  pin in  float   feed_override               "feed override value from gui (connect to halui.feed-override.value)";
  58  pin in  bit     float_switch                "float switch input (can also act as breakaway if it actuates when torch breaks away)";
  59  pin in  float   float_switch_travel         "float switch travel (machine units)";
  60  pin in  float   height_override             "height override adjustment (volts)";
  61  pin in  float   height_per_volt             "torch height change per volt (machine units)";
  62  pin in  bit     homed                       "machine is homed";
  63  pin in  bit     kerfcross_enable            "enable kerf crossing [mode 0 & mode 1]";
  64  pin in  float   kerfcross_override          "kerf crossing threshold override as a percentage";
  65  pin in  float   lowpass_frequency           "lowpass cutoff frequency for arc voltage output";
  66  pin in  s32     mode                        "operating mode";
  67  pin in  bit     move_down                   "external thc down switch [mode 2])";
  68  pin in  bit     move_up                     "external thc up switch [mode 2]";
  69  pin in  bit     multi_tool                  "allows the use of multiple tools";
  70  pin in  float   offset_current              "current z axis offset, connect to axis.z.eoffset";
  71  pin in  bit     ohmic_probe_enable          "ohmic probe enable input";
  72  pin in  float   ohmic_probe_offset          "Z axis offset for ohmic probe (machine units)";
  73  pin in  s32     ohmic_max_attempts          "maximum ohmic probe attempts before fallback to float switch";
  74  pin in  bit     ohmic_probe                 "ohmic probe input";
  75  pin in  bit     ohmic_test                  "test for shorted torch";
  76  pin in  float   paused_motion_speed         "multiplier for speed of motion when paused, from -1 to 1";
  77  pin in  float   pause_at_end                "time to pause at end of cut";
  78  pin in  float   pid_d_gain                  "derivative gain input [mode 0 & mode 1]";
  79  pin in  float   pid_i_gain                  "integral gain input [mode 0 & mode 1]";
  80  pin in  float   pid_p_gain                  "proportional gain input [mode 0 & mode 1]";
  81  pin in  float   pierce_delay                "time required to pierce stock (seconds)";
  82  pin in  float   pierce_height               "pierce height (machine units)";
  83  pin in  float   probe_feed_rate             "probe down velocity (machine units per minute)";
  84  pin in  bit     probe_test                  "probe test only";
  85  pin in  float   probe_start_height          "probe starting height";
  86  pin in  bit     program_is_idle             "program is idle, connect to halui.program.is-idle";
  87  pin in  bit     program_is_paused           "program is paused, connect to halui.program.is-paused";
  88  pin in  bit     program_is_running          "program is running, connect to halui.program.is-running";
  89  pin in  float   puddle_jump_delay           "delay move from pierce height to cut height (seconds), leave disconnected if not required";
  90  pin in  float   puddle_jump_height          "puddle jump height (percentage of pierce height), leave disconnected if not required";
  91  pin in  float   requested_velocity          "requested velocity, set by a known requested velocity or connect to motion.requested-vel";
  92  pin in  float   restart_delay               "time from arc failure till next restart attempt";
  93  pin in  float   safe_height                 "requested safe traverse height (machine units)";
  94  pin in  float   scribe_arm_delay            "delay from scribe arm to scribe on";
  95  pin in  float   scribe_on_delay             "delay from scribe on to motion beginning";
  96  pin in  bit     scribe_start                "start a new scribe, connect to spindle.1.on";
  97  pin in  float   setup_feed_rate             "feed rate for moves to pierce and cut heights (machine units per minute)";
  98  pin in  float   skip_ihs_distance           "skip IHS if less than this distance from last cut";
  99  pin in  bit     thc_enable                  "enable/disable thc and set the IHS skip type";
 100  pin in  bit     thc_disable                 "thc disable";
 101  pin in  float   thc_delay                   "delay from start of cut to THC enable (seconds)";
 102  pin in  float   thc_feed_rate               "maximum feed rate for thc (machine units per minute)";
 103  pin in  float   thc_threshold               "thc threshold (volts), changes below this have no effect";
 104  pin in  bit     torch_enable                "enable torch";
 105  pin in  bit     torch_off                   "turn torch off";
 106  pin in  bit     torch_pulse_start           "torch pulse start";
 107  pin in  float   torch_pulse_time            "torch pulse time (seconds)";
 108  pin in  float   units_per_mm                "for scale calcs, connect to halui.machine.units-per-mm";
 109  pin in  bit     use_auto_volts              "use calculated voltage for thc baseline";
 110  pin in  s32     x_y_velocity                "velocity for consumable change";
 111  pin in  float   x_offset                    "offest to apply to axis x for consumable change";
 112  pin in  float   y_offset                    "offest to apply to axis y for consumable change";
 113  
 114  /* OUTPUT PINS */
 115  pin out float   adaptive_feed               "for reverse-run, connect to motion.adaptive-feed";
 116  pin out bit     arc_ok_out                  "arc ok output";
 117  pin out float   arc_voltage_out             "arc voltage output [mode 0 & mode 1]";
 118  pin out bit     cornerlock_is_locked        "corner locked indicator";
 119  pin out float   cut_length                  "length of current cut job";
 120  pin out float   cut_time                    "time of current cut job";
 121  pin out bit     feed_hold                   "feed hold, connect to motion.feed_hold";
 122  pin out bit     kerfcross_is_locked         "kerf crossing locked indicator [mode 0 & mode 1]";
 123  pin out bit     led_down                    "thc move down indicator";
 124  pin out bit     led_up                      "thc move up indicator";
 125  pin out s32     offset_counts               "number of counts to offset, connect to axis.z.eoffset-counts";
 126  pin out bit     offset_enable               "enable offsets, connect to axis.z.eoffset-enable";
 127  pin out float   offset_scale                "offset scale, connect to axis.z.eoffset-scale";
 128  pin out bit     ohmic_enable                "on only while probing";
 129  pin out s32     pierce_count                "number of pierce attempts";
 130  pin out bit     program_pause               "pause the current program, connect to halui.program.pause";
 131  pin out bit     program_resume              "resume the currently paused program, connect to halui.program.resume";
 132  pin out bit     program_run                 "run the currently loaded program, connect to halui.program.run";
 133  pin out bit     program_stop                "stop current program, connect to halui.program.stop";
 134  pin out bit     safe_height_is_limited      "safe height is limited indicator";
 135  pin out bit     scribe_arm                  "arm the scribe";
 136  pin out bit     scribe_on                   "turn scribe on";
 137  pin out bit     thc_active                  "thc status output";
 138  pin out bit     thc_enabled                 "thc is enabled";
 139  pin out bit     torch_on                    "turn torch on, connect to your torch on input";
 140  pin out s32     x_offset_counts             "x offset for consumable change, connect to axis.x.eoffset-counts";
 141  pin out s32     y_offset_counts             "y offset for consumable change, connect to axis.y.eoffset-counts";
 142  pin out float   z_relative                  "distance of Z from last probed height";
 143  
 144  /* VARIABLES */
 145  variable double angle_x_y;                  /* angle for x/y velocity calcs in radians*/
 146  variable int    arc_starts;                 /* number of attempts to start torch */
 147  variable float  arc_fail_timer;             /* arc failure timer */
 148  variable float  arc_voltage;                /* calculated arc voltage before lowpass filter*/
 149  variable float  axis_x_finish;              /* axis x position at end of cut */
 150  variable float  axis_x_start;               /* axis x position at start of cut */
 151  variable float  axis_y_finish;              /* axis y position at end of cut */
 152  variable float  axis_y_start;               /* axis y position at start of cut */
 153  variable bool   auto_cut;                   /* auto cut mode is active */
 154  variable float  spotting_timer;             /* spotting timer */
 155  variable bool   spotting;                   /* spotting flag */
 156  variable bool   consumable_changing;        /* consumables are being changed */
 157  variable int    count;                      /* for counting */
 158  variable int    cut_height_first;           /* cut height at start of cut */
 159  variable int    cut_height_last;            /* cut height at end of cut */
 160  variable float  cut_offset;                 /* offset from last cut end to this cut start */
 161  variable bool   cut_started;                /* cut has started */
 162  variable int    cut_target;                 /* cut height target offset */
 163  variable bool   error_message;              /* 1 if error message has been sent */
 164  variable bool   first_cut_finished;         /* first cut is complete */
 165  variable bool   float_detected;             /* float switch detected */
 166  variable int    height_ovr_counts;          /* number of counts to change height via override */
 167  variable float  height_ovr_old;             /* old height override value */
 168  variable bool   initialized;                /* initialization flag */
 169  variable float  kerf_ratio;                 /* kerf crossing height to distance ratio */
 170  variable float  kerf_threshold;             /* kerf crossing threshold voltage */
 171  variable float  last_arc_voltage;           /* last sensed arc voltage */
 172  variable bool   manual_cut;                 /* manual cut mode is active */
 173  variable int    offset_datum;               /* datum for safe height calcs */
 174  variable int    offset_min;                 /* minimum allowed offset */
 175  variable int    offset_max;                 /* maximum allowed offset */
 176  variable int    ohmic_attempts;             /* current ohmic probe attempts */
 177  variable bool   ohmic_detected;             /* true if ohmic probe detected */
 178  variable int    setup_velocity;             /* velocity for setup moves */
 179  variable float  pause_at_end_timer;         /* pause at end of cut timer */
 180  variable bool   paused_motion;              /* paused motion flag */
 181  variable float  paused_motion_timer;        /* minimum run timer for paused motion */
 182  variable float  pid_error_now;              /* current error for pid calcs */
 183  variable float  pid_error_old;              /* old error for pid calcs */
 184  variable float  pid_output;                 /* calculated pid output value */
 185  variable float  pierce_timer;               /* pierce delay timer */
 186  variable int    pierce_target;              /* pierce height target offset */
 187  variable int    probe_force;                /* extra movement of probe after first contact */
 188  variable int    probe_force_val;            /* distance for probe_force */
 189  variable bool   probe_inhibit;              /* inhibit probing */
 190  variable int    probe_offset;               /* offset for active probe */
 191  variable bool   probe_required = 1;         /* a probe sequence is required */
 192  variable int    probe_start_target;         /* probe start height target */
 193  variable bool   probe_testing;              /* probe test active */
 194  variable int    probe_velocity;             /* probe down velocity */
 195  variable int    puddle_jump_percent;        /* puddle jump height as percentage of pierce height */
 196  variable int    puddle_jump_target;         /* puddle jump height target offset */
 197  variable float  puddle_jump_timer;          /* puddle jump delay timer */
 198  variable float  requested_feed_rate;        /* requested feed rate */
 199  variable float  restart_timer;              /* time between torch on attempts*/
 200  variable int    safe_min;                   /* minimum safe height allowed */
 201  variable int    safe_preferred;             /* preferred safe height offset */
 202  variable int    safe_target;                /* safe height target offset */
 203  variable bool   scribe_pause;               /* scribe pause flag */
 204  variable float  scribe_arm_timer;           /* scribe timer from arm to on*/
 205  variable float  scribe_on_timer;            /* scribe timer from on to motion*/
 206  variable bool   target_sensed;              /* sensed top of stock */
 207  variable int    target_samples = 6;         /* number of samples for setting target_volts */
 208  variable float  target_total;               /* total voltage of samples for setting target_volts */
 209  variable float  target_volts;               /* target voltage for thc, set by arc voltage at cut height */
 210  variable float  thc_delay_timer;            /* thc delay timer */
 211  variable int    thc_velocity;               /* velocity for thc moves */
 212  variable float  torch_off_timer;            /* arc off delay timer */
 213  variable bool   torch_pulse;                /* torch pulse flag */
 214  variable float  torch_pulse_timer;          /* torch pulse timer */
 215  variable float  velocity_scale;             /* the velocity multipler */
 216  variable int    x_velocity;                 /* velocity for x motion for consumable change */
 217  variable int    y_velocity;                 /* velocity for y motion for consumable change */
 218  variable int    zero_target;                /* zero height target offset */
 219  
 220  /* DEBUGGING PINS */
 221  pin out s32     state_out                   "current state";
 222  pin out s32     stop_type_out               "current stop type";
 223  
 224  function _;
 225  
 226  author "Phillip A Carter";
 227  
 228  license "GPLv2 or greater";
 229  
 230  option singleton yes;
 231  
 232  ;;
 233  
 234  #include "rtapi_math.h"
 235  
 236  typedef enum{IDLE,
 237               PROBE_HEIGHT,
 238               PROBE_DOWN,
 239               PROBE_UP,
 240               ZERO_HEIGHT,
 241               PIERCE_HEIGHT,
 242               TORCH_ON,
 243               ARC_OK,
 244               PIERCE_DELAY,
 245               PUDDLE_JUMP,
 246               CUT_HEIGHT,
 247               CUT,
 248               PAUSE_AT_END,
 249               SAFE_HEIGHT,
 250               MAX_HEIGHT,
 251               FINISH,
 252               TORCHPULSE,
 253               PAUSED_MOTION,
 254               OHMIC_TEST,
 255               PROBE_TEST,
 256               SCRIBING,
 257               CONSUMABLE_CHANGE_ON,
 258               CONSUMABLE_CHANGE_OFF,
 259               DEBUG} state_t;
 260  
 261  typedef enum{NONE,
 262               STOP,
 263               WAIT,
 264               PAUSE} stop_type_t;
 265  
 266  typedef enum{ZERO,
 267               UP,
 268               DOWN} move_direction_t;
 269  
 270  typedef enum{FLOAT,
 271               OHMIC} probe_type_t;
 272  
 273  typedef enum{EMPTY,
 274               CUTTING,
 275               SCRIBE,
 276               SPOTTING} tool_t;
 277  
 278  state_t state = IDLE;
 279  stop_type_t stop_type = NONE;
 280  move_direction_t move_direction = ZERO;
 281  probe_type_t probe_type = FLOAT;
 282  tool_t tool = EMPTY;
 283  
 284  FUNCTION(_) {
 285  
 286      /* do these first run only */
 287      if(!initialized && units_per_mm){
 288          offset_scale = units_per_mm * fperiod;
 289          velocity_scale = 1 / units_per_mm / 60;
 290          offset_enable = TRUE;
 291          adaptive_feed = 1;
 292          initialized = TRUE;
 293          probe_force_val = 0.125 * units_per_mm / offset_scale;
 294      }
 295  
 296      /* set the active tool */
 297      if(cutting_start){ //  this allows M3 for cutting as well as M3 $0
 298          tool = CUTTING;
 299      }else if(multi_tool && scribe_start && !cutting_start && !spotting_start){
 300          tool = SCRIBE;
 301      }else if(multi_tool && spotting_start && !cutting_start && !scribe_start){
 302          tool = SPOTTING;
 303      }else{
 304          tool = EMPTY;
 305      }
 306  
 307      /* output the relative Z height */
 308      if(zero_target){
 309          z_relative = offset_current - zero_target * offset_scale;
 310      }else{
 311          z_relative = axis_z_position - axis_z_min_limit;
 312      }
 313  
 314      /* convert feed rates to velocity */
 315      setup_velocity = setup_feed_rate * velocity_scale;
 316      if(probe_feed_rate < setup_feed_rate){
 317          probe_velocity = probe_feed_rate * velocity_scale;
 318      }else{
 319          probe_velocity = setup_velocity;
 320      }
 321      if(mode == 2){
 322          thc_velocity = thc_feed_rate * velocity_scale * pid_p_gain / 100;
 323      }else{
 324          thc_velocity = thc_feed_rate * velocity_scale;
 325      }
 326      if(thc_velocity < 1){
 327          thc_velocity = 1;
 328      }
 329  
 330      /* turn torch off if torch off timer completed */
 331      if(torch_off_timer > 0){
 332          torch_off_timer -= fperiod;
 333          if(torch_off_timer <= 0){
 334              torch_on = FALSE;
 335              torch_off_timer = 0;
 336          }
 337      }
 338  
 339      /* turn torch off from external input */
 340      if(torch_off){
 341          torch_on = FALSE;
 342      }
 343  
 344      /* set THC state */
 345      thc_enabled = (thc_enable && !thc_disable ? 1:0);
 346  
 347      /* set THC status */
 348      thc_active = (state == CUT) && target_volts && thc_enabled && !cornerlock_is_locked && !kerfcross_is_locked ? 1:0;
 349  
 350      /* set ohmic probe state */
 351      ohmic_detected = ohmic_probe && ohmic_probe_enable;
 352  
 353      /* set adaptive feed reduction if no paused motion */
 354      if(state != PAUSED_MOTION){
 355          if(feed_reduction < 10 && state == CUT){
 356              adaptive_feed = 1;
 357          }else if(feed_reduction < 100 && state == CUT){
 358              adaptive_feed = feed_reduction * 0.01;
 359          }else{
 360              adaptive_feed = 1;
 361          }
 362      }
 363  
 364      /* check for a manual cut */
 365      if(tool == CUTTING && !auto_cut){
 366          manual_cut = TRUE;
 367      }
 368  
 369      /* check for an abort */
 370      /* or for a pause or wait while active */
 371      if(auto_cut){
 372          if(!probe_test && (external_estop || program_is_idle) && cut_started && !program_run){
 373              auto_cut = FALSE;
 374              if(!pause_at_end){
 375                  torch_on = FALSE;
 376              }
 377              stop_type = STOP;
 378              program_stop = TRUE;
 379              cut_started = FALSE;
 380              probe_required = TRUE;
 381              axis_x_finish = 0;
 382              axis_y_finish = 0;
 383              if(!external_estop){
 384                  pause_at_end_timer = pause_at_end;
 385                  state = PAUSE_AT_END;
 386              }else{
 387                  state = MAX_HEIGHT;
 388              }
 389          }else if(!probe_test && state > IDLE && state <= CUT && stop_type == NONE && cut_started){
 390              if(program_is_paused){
 391                  torch_on = FALSE;
 392                  stop_type = PAUSE;
 393                  probe_required = TRUE;
 394                  pause_at_end_timer = pause_at_end;
 395                  state = MAX_HEIGHT;
 396              }else if(tool == EMPTY){
 397                  if(!pause_at_end){
 398                      torch_on = FALSE;
 399                  }
 400                  stop_type = WAIT;
 401                  if(thc_enabled && ((!use_auto_volts || (use_auto_volts && target_volts)) || !torch_enable)){
 402                      axis_x_finish = axis_x_position;
 403                      axis_y_finish = axis_y_position;
 404                  }else{
 405                      axis_x_finish = 0;
 406                      axis_y_finish = 0;
 407                  }
 408                  pause_at_end_timer = pause_at_end;
 409                  state = PAUSE_AT_END;
 410              }else if(breakaway){
 411                  torch_on = FALSE;
 412                  program_pause = TRUE;
 413                  stop_type = PAUSE;
 414                  probe_required = TRUE;
 415                  rtapi_print_msg(RTAPI_MSG_ERR,"breakaway switch activated\n"
 416                                                "program is paused.\n");
 417                  probe_inhibit = TRUE;
 418                  state = MAX_HEIGHT;
 419              }else if(state > PIERCE_HEIGHT && float_switch){
 420                  torch_on = FALSE;
 421                  program_pause = TRUE;
 422                  stop_type = PAUSE;
 423                  probe_required = TRUE;
 424                  rtapi_print_msg(RTAPI_MSG_ERR,"float switch activated\n"
 425                                                "program is paused.\n");
 426                  probe_inhibit = TRUE;
 427                  state = MAX_HEIGHT;
 428              }else if(state > PIERCE_HEIGHT && ohmic_detected){
 429                  torch_on = FALSE;
 430                  program_pause = TRUE;
 431                  stop_type = PAUSE;
 432                  probe_required = TRUE;
 433                  rtapi_print_msg(RTAPI_MSG_ERR,"ohmic probe activated\n"
 434                                                "program is paused.\n");
 435                  probe_inhibit = TRUE;
 436                  state = MAX_HEIGHT;
 437              }else if(state > ARC_OK && !arc_ok_out && torch_enable && !torch_off){ ;
 438                  torch_on = FALSE;
 439                  program_pause = TRUE;
 440                  stop_type = PAUSE;
 441                  probe_required = TRUE;
 442                  rtapi_print_msg(RTAPI_MSG_ERR,"valid arc lost\n"
 443                                                "program is paused.\n");
 444                  state = MAX_HEIGHT;
 445              }
 446          }
 447      }else if(manual_cut){
 448          if(tool == EMPTY){
 449              manual_cut = FALSE;
 450              torch_on = FALSE;
 451              stop_type = STOP;
 452              program_stop = TRUE;
 453              cut_started = FALSE;
 454              probe_required = TRUE;
 455              axis_x_finish = 0;
 456              axis_y_finish = 0;
 457              state = MAX_HEIGHT;
 458          }
 459      }
 460  
 461      /* calculate arc voltage */
 462      arc_voltage = (arc_voltage_in - arc_voltage_offset) * arc_voltage_scale;
 463      if(lowpass_frequency){
 464          arc_voltage_out += (arc_voltage - arc_voltage_out) * (1 - pow(2.71828, -(2 * M_PI * lowpass_frequency) * fperiod));
 465      }else{
 466          arc_voltage_out = arc_voltage;
 467      }
 468  
 469      /* set arc ok from either arc ok input of from actual arc voltage
 470       * if using arc ok input, set arc_ok_low_in and/or arc_ok_high_in to 0 */
 471      if(mode > 0){
 472          arc_ok_out = arc_ok_in;
 473      }else if(arc_voltage_out >= arc_ok_low && arc_voltage_out <= arc_ok_high){
 474          arc_ok_out = TRUE;
 475      }else{
 476          arc_ok_out = FALSE;
 477      }
 478  
 479      /* reset program states */
 480      if(program_is_idle){
 481          program_stop = FALSE;
 482          program_resume = FALSE;
 483      }else if(program_is_paused){
 484          program_pause = FALSE;
 485      }else if(program_is_running){
 486          program_run = FALSE;
 487          program_resume = FALSE;
 488          auto_cut = TRUE;
 489      }
 490  
 491      /* if puddlejump height is 0 then set it to 100 */
 492      if(puddle_jump_height == 0){
 493          puddle_jump_percent = 100;
 494      }else{
 495          puddle_jump_percent = puddle_jump_height;
 496      }
 497  
 498  /* state machine */
 499      switch(state){
 500          case IDLE:
 501              if(probe_inhibit && !float_switch && !breakaway && !ohmic_detected){
 502                  probe_inhibit = FALSE;
 503              }else if(!probe_inhibit){
 504                  /* if we get a paused motion request and we are paused */
 505                  if(paused_motion_speed && stop_type == PAUSE){
 506                      state = PAUSED_MOTION;
 507                  /* if we get a consumable change start request and we are paused */
 508                  }else if(consumable_change && stop_type == PAUSE && !consumable_changing){
 509                      state = CONSUMABLE_CHANGE_ON;
 510  
 511                  /* if we get a consumable change stop request and we are changing consumables */
 512                  }else if(!consumable_change && stop_type == PAUSE && consumable_changing){
 513                          state = CONSUMABLE_CHANGE_OFF;
 514  
 515                  /* if we get a torch start request and we are stopped or waiting for a restart */
 516              }else if((tool == CUTTING || tool == SPOTTING || probe_test) && (stop_type == NONE || stop_type == WAIT) && homed){
 517                      feed_hold = TRUE;
 518                      stop_type = NONE;
 519                      //touchdown = FALSE;
 520                      if(!probe_test){
 521                          cut_started = TRUE;
 522                      }else{
 523                          probe_testing = TRUE;
 524                      }
 525                      if(!thc_enabled && first_cut_finished){
 526                          cut_offset = sqrt(pow(axis_x_start - axis_x_position, 2) + pow(axis_y_start - axis_y_position, 2));
 527                      }else if((axis_x_finish || axis_y_finish) && first_cut_finished){
 528                          cut_offset = sqrt(pow(axis_x_finish - axis_x_position, 2) + pow(axis_y_finish - axis_y_position, 2));
 529                          axis_x_finish = 0;
 530                          axis_y_finish = 0;
 531                      }
 532                      if(cut_offset && cut_offset < skip_ihs_distance && !probe_required){
 533                          if(thc_enabled){
 534                              cut_target = cut_target + cut_height_last - cut_height_first;
 535                              pierce_target = pierce_target + cut_height_last - cut_height_first;
 536                              puddle_jump_target = puddle_jump_target + cut_height_last - cut_height_first;
 537                              cut_height_first = cut_height_last;
 538                          }
 539                          cut_offset = 0;
 540                          state = PIERCE_HEIGHT;
 541                      }else{
 542                          ohmic_enable = TRUE;
 543                          if(ohmic_detected && !probe_inhibit){
 544                              ohmic_enable = FALSE;
 545                              probe_inhibit = TRUE;
 546                              if(probe_testing){
 547                                  rtapi_print_msg(RTAPI_MSG_ERR,"ohmic probe detected before moving to probe height.");
 548                              }else{
 549                                  stop_type = PAUSE;
 550                                  program_pause = TRUE;
 551                                  rtapi_print_msg(RTAPI_MSG_ERR,"ohmic probe detected before moving to probe height.\n"
 552                                                                "program is paused.\n");
 553                              }
 554                          }else if(float_switch && !probe_inhibit){
 555                              probe_inhibit = TRUE;
 556                              if(probe_testing){
 557                                  rtapi_print_msg(RTAPI_MSG_ERR,"float switch detected before moving to probe height.");
 558                              }else{
 559                                  stop_type = PAUSE;
 560                                  program_pause = TRUE;
 561                                  rtapi_print_msg(RTAPI_MSG_ERR,"float switch detected before moving to probe height.\n"
 562                                                                "program is paused.\n");
 563                              }
 564                          }else if(breakaway && !probe_inhibit){
 565                              probe_inhibit = TRUE;
 566                              if(probe_testing){
 567                                  rtapi_print_msg(RTAPI_MSG_ERR,"breakaway switch detected before moving to probe height.");
 568                              }else{
 569                                  stop_type = PAUSE;
 570                                  program_pause = TRUE;
 571                                  rtapi_print_msg(RTAPI_MSG_ERR,"breakaway switch detected before moving to probe height.\n"
 572                                                                "program is paused.\n");
 573                              }
 574                          }else if (!program_is_paused){
 575                              if(!first_cut_finished){
 576                                  if(probe_start_height){
 577                                      probe_start_target = offset_counts - ((axis_z_position - axis_z_min_limit - probe_start_height) / offset_scale);
 578                                  }else{
 579                                      probe_start_target = offset_counts;
 580                                  }
 581                              }
 582                              cut_offset = 0;
 583                              state = PROBE_HEIGHT;
 584                          }
 585                      }
 586                  /* if we get a resume request and we are paused */
 587                  }else if(!program_is_paused && stop_type == PAUSE){
 588                      if(consumable_changing){
 589                          state = CONSUMABLE_CHANGE_OFF;
 590                      }
 591                      feed_hold = TRUE;
 592                      stop_type = NONE;
 593                  /* if torch pulse requested */
 594                  }else if(torch_pulse_start){
 595                      feed_hold = TRUE;
 596                      state = TORCHPULSE;
 597                  /* if ohmic probe shorted test requested */
 598                  }else if(ohmic_test){
 599                      feed_hold = TRUE;
 600                      ohmic_enable = TRUE;
 601                      state = OHMIC_TEST;
 602                  /* if we get a air-scribe start request */
 603              }else if(tool == SCRIBE && !probe_test && homed){
 604                      state = SCRIBING;
 605                      scribe_arm_timer = scribe_arm_delay;
 606                      scribe_pause = FALSE;
 607                  }else if(!cut_started){
 608                      feed_hold = FALSE;
 609                  }
 610              }
 611              break;
 612          case PROBE_HEIGHT:
 613              /* move to probe height at setup speed */
 614              if(probe_testing && !probe_test){
 615                  state = PROBE_TEST;
 616                  break;
 617              }
 618              if(float_switch && !float_detected){
 619                  if(probe_testing){
 620                      rtapi_print_msg(RTAPI_MSG_ERR,"float switch detected while moving to probe height.");
 621                      state = PROBE_TEST;
 622                  }else{
 623                      stop_type = PAUSE;
 624                      program_pause = TRUE;
 625                      state = MAX_HEIGHT;
 626                      rtapi_print_msg(RTAPI_MSG_ERR,"float switch detected while moving to probe height.\n"
 627                                                    "program is paused.\n");
 628                  }
 629              }else if(ohmic_detected){
 630                  if(probe_testing){
 631                      rtapi_print_msg(RTAPI_MSG_ERR,"ohmic probe detected while moving to probe height.");
 632                      state = PROBE_TEST;
 633                  }else{
 634                      stop_type = PAUSE;
 635                      program_pause = TRUE;
 636                      state = MAX_HEIGHT;
 637                      rtapi_print_msg(RTAPI_MSG_ERR,"ohmic probe detected while moving to probe height.\n"
 638                                                    "program is paused.\n");
 639                  }
 640              }else if(offset_counts - setup_velocity > probe_start_target){
 641                  offset_counts -= setup_velocity;
 642              }else if(offset_counts + setup_velocity < probe_start_target){
 643                  offset_counts += setup_velocity;
 644              }else if(offset_counts > probe_start_target || offset_counts < probe_start_target){
 645                  offset_counts = probe_start_target;
 646              }else{
 647                  if(float_detected){
 648                      if(!float_switch){
 649                          float_detected = FALSE;
 650                      }
 651                  }else{
 652                      state = PROBE_DOWN;
 653                  }
 654              }
 655              break;
 656          case PROBE_DOWN:
 657              /* probe down to top of stock at probe speed, then push a little further for good contact */
 658              feed_hold = TRUE;
 659              if(probe_testing && !probe_test){
 660                  state = PROBE_TEST;
 661                  break;
 662              }
 663              if(target_sensed){
 664                  if(probe_force > 0){
 665                      probe_force -= 21;
 666                      offset_counts -= probe_velocity;
 667                  }else{
 668                      target_sensed = FALSE;
 669                      if(float_switch || ohmic_detected){
 670                          state = PROBE_UP;
 671                      }else{
 672                          rtapi_print_msg(RTAPI_MSG_ERR,"probe trip error while probing.");
 673                          state = IDLE;
 674                      }
 675                  }
 676              }else if(!float_switch && !ohmic_detected){
 677                  if(axis_z_position - (probe_velocity * offset_scale) <= axis_z_min_limit){
 678                      if(!probe_test){
 679                          rtapi_print_msg(RTAPI_MSG_ERR,"bottom limit reached while probing down.\n"
 680                                                        "program is paused.\n");
 681                          stop_type = PAUSE;
 682                          program_pause = TRUE;
 683                      }else{
 684                          rtapi_print_msg(RTAPI_MSG_ERR,"bottom limit reached while probe testing.\n");
 685                      }
 686                      state = MAX_HEIGHT;
 687                  }else{
 688                      offset_counts -= probe_velocity;
 689                  }
 690              }else if(float_switch && !float_detected){
 691                  if(ohmic_attempts == ohmic_max_attempts){
 692                      probe_type = FLOAT;
 693                      target_sensed = TRUE;
 694                      probe_force = probe_force_val;
 695                      ohmic_attempts = 0;
 696                      float_detected = FALSE;
 697                  }else{
 698                      state = PROBE_HEIGHT;
 699                      ohmic_attempts += 1;
 700                      float_detected = TRUE;
 701                  }
 702              }else if(ohmic_detected){
 703                  probe_type = OHMIC;
 704                  target_sensed = TRUE;
 705                  probe_force = probe_force_val;
 706                  ohmic_attempts = 0;
 707              }
 708              break;
 709          case PROBE_UP:
 710              /* probe up at minimum speed to find top of stock */
 711              if(probe_testing && !probe_test){
 712                  state = PROBE_TEST;
 713                  break;
 714              }
 715              if(float_switch || ohmic_detected){
 716                  offset_counts += 1;
 717              }else{
 718                  if(probe_type == OHMIC){
 719                      probe_offset = ohmic_probe_offset / offset_scale;
 720                  }else{
 721                      probe_offset = float_switch_travel / offset_scale;
 722                  }
 723                  zero_target = offset_counts + probe_offset;
 724                  cut_target = cut_height_first = offset_datum = offset_counts + probe_offset + (cut_height / offset_scale);
 725                  pierce_target = offset_counts + probe_offset + (pierce_height / offset_scale);
 726                  puddle_jump_target = offset_counts + probe_offset + (pierce_height * (puddle_jump_percent / 100) / offset_scale);
 727                  safe_min = (pierce_height + (1 * units_per_mm)) / offset_scale;
 728                  safe_preferred = safe_height / offset_scale;
 729                  offset_min = offset_counts - ((axis_z_position - axis_z_min_limit) / offset_scale);
 730                  offset_max = offset_counts + (axis_z_max_limit - axis_z_position - (1 * units_per_mm)) / offset_scale;
 731                  if(safe_height == 0){
 732                      safe_target = offset_max;
 733                      ohmic_enable = FALSE;
 734  //                    state = ZERO_HEIGHT;
 735                      state = PIERCE_HEIGHT;
 736                  }else if(offset_counts + safe_min >= offset_max){
 737                      safe_target = offset_max;
 738                      rtapi_print_msg(RTAPI_MSG_ERR, "material too high for safe traverse.\n"
 739                                                     "program is paused.\n");
 740                      stop_type = PAUSE;
 741                      program_pause = TRUE;
 742                      state = MAX_HEIGHT;
 743                  }else if(offset_counts + safe_preferred >= offset_max && !safe_height_is_limited){
 744                      rtapi_print_msg(RTAPI_MSG_ERR, "safe traverse height has been reduced.\n");
 745                      safe_target = offset_max;
 746                      safe_height_is_limited = TRUE;
 747                      ohmic_enable = FALSE;
 748  //                    state = ZERO_HEIGHT;
 749                      state = PIERCE_HEIGHT;
 750                  }else{
 751                      safe_target = offset_counts + safe_preferred;
 752                      ohmic_enable = FALSE;
 753  //                    state = ZERO_HEIGHT;
 754                      state = PIERCE_HEIGHT;
 755                  }
 756              }
 757              break;
 758          case ZERO_HEIGHT:
 759              probe_required = FALSE;
 760              if(probe_testing && !probe_test){
 761                  state = PROBE_TEST;
 762                  break;
 763              }
 764              if(offset_counts - setup_velocity > zero_target){
 765                  offset_counts -= setup_velocity;
 766              }else if(offset_counts + setup_velocity < zero_target){
 767                  offset_counts += setup_velocity;
 768              }else{
 769                  offset_counts = zero_target;
 770                  axis_x_start = axis_x_position;
 771                  axis_y_start = axis_y_position;
 772                  state = PIERCE_HEIGHT;
 773              }
 774              break;
 775          case PIERCE_HEIGHT:
 776              /* move up to pierce height */
 777              if(probe_testing && !probe_test){
 778                  state = PROBE_TEST;
 779                  break;
 780              }
 781              if(pierce_height && cut_height && (use_auto_volts || (!use_auto_volts && cut_volts))){
 782                  feed_hold = TRUE;
 783                  if(offset_counts - setup_velocity > pierce_target){
 784                      offset_counts -= setup_velocity;
 785                  }else if(offset_counts + setup_velocity < pierce_target){
 786                      offset_counts += setup_velocity;
 787                  }else{
 788                      offset_counts = pierce_target;
 789                      arc_starts = 0;
 790                      if(probe_testing){
 791                          state = PROBE_TEST;
 792                      }else if(!torch_enable){
 793                          pierce_timer = pierce_delay;
 794                          state = PIERCE_DELAY;
 795                      }else if(tool == CUTTING || tool == SPOTTING){
 796                          state = TORCH_ON;
 797                      }
 798                  }
 799              }else if(!probe_testing){
 800                  stop_type = PAUSE;
 801                  program_pause = TRUE;
 802                  state = MAX_HEIGHT;
 803                  rtapi_print_msg(RTAPI_MSG_ERR,"invalid pierce height.\n"
 804                                                "or invalid cut height.\n"
 805                                                "or invalid cut volts.");
 806              }
 807              break;
 808          case TORCH_ON:
 809              /* turn torch on and start arc fail timer
 810               * if too many attempts then turn torch off, pause program and return to idle state */
 811              feed_hold = TRUE;
 812              if(arc_starts > arc_max_starts - 1){
 813                  program_pause = TRUE;
 814                  restart_timer = 0;
 815                  if (!error_message){
 816                      rtapi_print_msg(RTAPI_MSG_ERR,"no arc detected after %d start attempts\nprogram is paused.\n", arc_max_starts);
 817                      error_message = 1;
 818                  }
 819              }else{
 820                  error_message = 0;
 821                  restart_timer -= fperiod;
 822                  if(restart_timer <= 0){
 823                      restart_timer = 0;
 824                      arc_fail_timer = arc_fail_delay;
 825                      if(torch_enable){
 826                          torch_on = TRUE;
 827                          pierce_count += 1;
 828                      }
 829                      spotting = FALSE;
 830                      state = ARC_OK;
 831                  }
 832              }
 833              break;
 834          case ARC_OK:
 835              /* wait for arc ok
 836               * if timeout occurs turn torch off then return to TORCH_ON for another attempt */
 837              feed_hold = TRUE;
 838              if(tool == SPOTTING){
 839                  if(!spotting){
 840                      if(arc_voltage >= spotting_threshold){
 841                          spotting = TRUE;
 842                          spotting_timer = spotting_time * 0.001;
 843                      }
 844                  }else{
 845                      spotting_timer -= fperiod;
 846                      if(spotting_timer <= 0){
 847                          spotting = FALSE;
 848                          spotting_timer = 0;
 849                          torch_on = FALSE;
 850                          stop_type = WAIT;
 851                          state = SAFE_HEIGHT;
 852                      }
 853                  }
 854              }
 855              arc_fail_timer -= fperiod;
 856              if(arc_fail_timer <= 0){
 857                  torch_on = FALSE;
 858                  restart_timer = restart_delay;
 859                  arc_starts += 1;
 860                  state = TORCH_ON;
 861              }else if(arc_ok_out && tool != SPOTTING){
 862                      pierce_timer = pierce_delay;
 863                      state = PIERCE_DELAY;
 864              }
 865              break;
 866          case PIERCE_DELAY:
 867              /* wait for arc to pierce stock */
 868              feed_hold = TRUE;
 869              if(pierce_timer > 0){
 870                  pierce_timer -= fperiod;
 871              }else{
 872                  puddle_jump_timer = puddle_jump_delay;
 873                  state = PUDDLE_JUMP;
 874              }
 875              break;
 876          case PUDDLE_JUMP:
 877              /* move to puddle_jump height */
 878              feed_hold = TRUE;
 879              if(offset_counts - setup_velocity > puddle_jump_target){
 880                  offset_counts -= setup_velocity;
 881              }else if(offset_counts + setup_velocity < puddle_jump_target){
 882                  offset_counts += setup_velocity;
 883              }else{
 884                  offset_counts = puddle_jump_target;
 885                  count = 0;
 886                  if(puddle_jump_timer > 0){
 887                      feed_hold = FALSE;
 888                      puddle_jump_timer -= fperiod;
 889                  }else{
 890                      puddle_jump_timer = 0;
 891                      feed_hold = TRUE;
 892                      state = CUT_HEIGHT;
 893                  }
 894              }
 895              break;
 896          case CUT_HEIGHT:
 897              /* move to cut height */
 898              feed_hold = TRUE;
 899              if(offset_counts - setup_velocity > cut_target){
 900                  offset_counts -= setup_velocity;
 901              }else if(offset_counts + setup_velocity < cut_target){
 902                  offset_counts += setup_velocity;
 903              }else{
 904                  offset_counts = cut_target;
 905                  if((int)floor(offset_current / offset_scale) <= cut_target + 1 && 
 906                     (int)floor(offset_current / offset_scale) >= cut_target - 1){
 907                      count = 0;
 908                      /* set feed rate for this cut */
 909                      if(cut_feed_rate > 0){
 910                          requested_feed_rate = cut_feed_rate;
 911                      }else{
 912                          requested_feed_rate = requested_velocity;
 913                      }
 914                      feed_hold = FALSE;
 915                      thc_delay_timer = thc_delay;
 916                      state = CUT;
 917                  }
 918              }
 919              break;
 920          case CUT:
 921              /* while cutting and it is not a dry run:
 922               * if thc is enabled then vary the torch height to keep the arc voltage constant
 923               * if corner lock enabled, only allow THC if current velocity is greater than the threshold percentage of requested velocity
 924               * if kerf crossing is enabled, only allow THC if the voltage change is less than the threshold voltage (modes 0 & 1 only)
 925               * adjust torch height and target voltage to suit if height override requested (modes 0 & 1 only)*/
 926              if(torch_on){
 927  //            if(torch_enable){
 928                  /* thc control by arc voltage */
 929                  if(mode < 2){
 930                      thc_delay_timer -= fperiod;
 931                      if(thc_delay_timer <= 0){
 932                          /* set target voltage */
 933                          if(target_volts == 0){
 934                              if(use_auto_volts){
 935                                  /* wait until velocity is at least 99.9% of requested velocity before sampling arc voltage */
 936                                  if(current_velocity * 60 > requested_feed_rate * feed_override * 0.999){
 937                                      count += 1;
 938                                      target_total += arc_voltage_out;
 939                                      if(count == target_samples){
 940                                          target_volts = target_total / target_samples;
 941                                          last_arc_voltage = target_volts;
 942                                          count = 0;
 943                                          target_total = 0;
 944                                      }
 945                                  }
 946                                  if(target_volts == 0){
 947                                      cornerlock_is_locked = TRUE;
 948                                  }else{
 949                                      cornerlock_is_locked = FALSE;
 950                                  }
 951                              }else{
 952                                  target_volts = cut_volts;
 953                              }
 954                          /* height override setup*/
 955                          }else if(fabs(height_override - height_ovr_old) > 0.05){
 956                              height_ovr_counts -= (height_override - height_ovr_old) / 10 * units_per_mm / offset_scale;
 957                              height_ovr_old = height_override;
 958                          /* height override z motion */
 959                          }else if(height_ovr_counts != 0){
 960                              if((setup_velocity) < height_ovr_counts){
 961                                  offset_counts -= setup_velocity;
 962                                  height_ovr_counts -= setup_velocity;
 963                              }else{
 964                                  offset_counts -= height_ovr_counts;
 965                                  height_ovr_counts = 0;
 966                              }
 967                          /* torch height control */
 968                          }else if(thc_enabled){
 969                              /* lock thc if velocity < requested velocity * cornerlock threshold percentage */
 970                              if(cornerlock_enable){
 971                                  if(current_velocity * 60 < requested_feed_rate * feed_override * cornerlock_threshold * 0.01){
 972                                      cornerlock_is_locked = TRUE;
 973                                  }else if(cornerlock_is_locked && current_velocity * 60 > requested_feed_rate * feed_override * 0.99){
 974                                      cornerlock_is_locked = FALSE;
 975                                  }
 976                              }else{
 977                                  cornerlock_is_locked = FALSE;
 978                              }
 979                              /* unlock kerfcross lock if voltage reduced to safe level  */
 980                              if(kerfcross_is_locked){
 981  //                                if(arc_voltage_out < last_arc_voltage + (kerf_threshold * 0.05)){
 982                                  if(arc_voltage_out < target_volts + (kerf_threshold * 0.5)){
 983                                      kerfcross_is_locked = FALSE;
 984                                      last_arc_voltage = arc_voltage_out;
 985                                  }
 986                              /* set kerfcross lock if voltage change > kerfcross threshold volts */
 987                              }else if(kerfcross_enable){
 988                                  if(thc_feed_rate < current_velocity * 60){
 989                                      kerf_ratio = thc_feed_rate / (current_velocity * 60);
 990                                  }else{
 991                                      kerf_ratio = 1; /* 45 degree ramp */
 992                                  }
 993                                  kerf_threshold = ((kerf_ratio * current_velocity * fperiod) / height_per_volt) * (kerfcross_override * 0.01);
 994  //                                if(arc_voltage_out > last_arc_voltage + kerf_threshold){
 995                                  if(arc_voltage_out > target_volts + kerf_threshold){
 996                                      kerfcross_is_locked = TRUE;
 997                                  }else{
 998                                      last_arc_voltage = arc_voltage_out;
 999                                  }
1000                              }
1001                              /* do thc if ok to go */
1002                              if(!cornerlock_is_locked && !kerfcross_is_locked){
1003                                  pid_error_now = (target_volts + height_override - arc_voltage_out) * 0.1;
1004                                  if(fabs(pid_error_now) < fabs(thc_threshold * 0.1)){
1005                                      pid_error_now = 0;
1006                                  }
1007                                  pid_output = pid_error_now * pid_p_gain;
1008                                  pid_output += pid_error_now * pid_i_gain * fperiod;
1009                                  pid_output += (pid_error_now - pid_error_old) * pid_d_gain / fperiod;
1010                                  pid_error_old = pid_error_now;
1011                                  if(pid_output > thc_velocity){
1012                                      pid_output = thc_velocity;
1013                                  }else if(pid_output < -thc_velocity){
1014                                      pid_output = -thc_velocity;
1015                                  }
1016                                  /* if we hit a soft limit during thc*/
1017                                  if(offset_counts + pid_output <= offset_min || offset_counts + pid_output >= offset_max){
1018                                      torch_on = FALSE;
1019                                      stop_type = PAUSE;
1020                                      program_pause = TRUE;
1021                                      if(offset_counts + pid_output <= offset_min){
1022                                          rtapi_print_msg(RTAPI_MSG_ERR,"bottom limit reached while THC moving down.\n"
1023                                                                        "program is paused.\n");
1024                                      }else{
1025                                          rtapi_print_msg(RTAPI_MSG_ERR,"top limit reached while THC moving up.\n"
1026                                                                        "program is paused.\n");
1027                                      }
1028                                      pid_output = 0;
1029                                      state = MAX_HEIGHT;
1030  
1031                                  }
1032                                  offset_counts += pid_output;
1033                              }
1034                          }
1035                          if(pid_output > 0){
1036                              led_up = TRUE;
1037                          }else if((pid_output) < 0){
1038                              led_down = TRUE;
1039                          }else{
1040                              led_down = FALSE;
1041                              led_up = FALSE;
1042                          }
1043                          pid_output = 0;
1044                      }
1045                  }else{ /* thc control by move-up and move-down inputs (no kerf crossing in this mode) */
1046                      if(thc_enabled){
1047                          /* lock thc if velocity < requested velocity * cornerlock threshold percentage */
1048                          if(cornerlock_enable){
1049                              if(current_velocity * 60 < requested_feed_rate * feed_override * cornerlock_threshold * 0.01){
1050                                  cornerlock_is_locked = TRUE;
1051                              }else if(cornerlock_is_locked && current_velocity * 60 > requested_feed_rate * feed_override * 0.99){
1052                                  cornerlock_is_locked = FALSE;
1053                              }
1054                          }else{
1055                              cornerlock_is_locked = FALSE;
1056                          }
1057                          if(move_down && !cornerlock_is_locked){
1058                              if(offset_counts - thc_velocity <= offset_min){
1059                                  torch_on = FALSE;
1060                                  stop_type = PAUSE;
1061                                  program_pause = TRUE;
1062                                  rtapi_print_msg(RTAPI_MSG_ERR,"bottom limit reached while THC moving down.\n"
1063                                                                "program is paused.\n");
1064                                  state = MAX_HEIGHT;
1065                              }else{ /* move down at requested velocity */
1066                                  offset_counts -= thc_velocity;
1067                                  led_down = TRUE;
1068                              }
1069                          }else if(move_up && !cornerlock_is_locked){
1070                              if(offset_counts + thc_velocity + safe_min >= offset_max){
1071                                  torch_on = FALSE;
1072                                  stop_type = PAUSE;
1073                                  program_pause = TRUE;
1074                                  rtapi_print_msg(RTAPI_MSG_ERR,"top limit reached while THC moving up.\n"
1075                                                                "program is paused.\n");
1076                                  state = MAX_HEIGHT;
1077                              }else{ /* move up at requested velocity */
1078                                  offset_counts += thc_velocity;
1079                                  led_up = TRUE;
1080                              }
1081                          }else{
1082                              led_down = FALSE;
1083                              led_up = FALSE;
1084                          }
1085                      }
1086                  }
1087                  /* check if safe height is below maximium offset */
1088                  if(offset_counts > offset_datum){
1089                      safe_target += offset_counts - offset_datum;
1090                      offset_datum = offset_counts;
1091                      if(safe_target > offset_max){
1092                          safe_target = offset_max;
1093                          if(!safe_height_is_limited){
1094                              safe_height_is_limited = TRUE;
1095                              rtapi_print_msg(RTAPI_MSG_ERR, "safe traverse height has been reduced.");
1096                          }
1097                      }
1098                  }
1099              }
1100              cut_height_last = offset_counts;
1101              cut_length = cut_length + current_velocity * fperiod;
1102              cut_time = cut_time + fperiod;
1103              break;
1104          case PAUSE_AT_END:
1105              feed_hold = TRUE;
1106              pause_at_end_timer -= fperiod;
1107              if(pause_at_end_timer <= 0){
1108                  pause_at_end_timer = 0;
1109                  torch_on = FALSE;
1110                  if(program_is_idle){
1111                      state = MAX_HEIGHT;
1112                  }else{
1113                      state = SAFE_HEIGHT;
1114                  }
1115              }
1116              break;
1117          case SAFE_HEIGHT:
1118              /* move to safe height */
1119              feed_hold = TRUE;
1120              if(!torch_off_timer || !torch_on){
1121                  cornerlock_is_locked = FALSE;
1122                  if(!probe_test){
1123                      if(offset_counts - setup_velocity > safe_target){
1124                          offset_counts -= setup_velocity;
1125                      }else if(offset_counts + setup_velocity < safe_target){
1126                          offset_counts += setup_velocity;
1127                      }else{
1128                          offset_counts = safe_target;
1129                          if(stop_type == WAIT){
1130                              feed_hold = FALSE;
1131                          }
1132                          first_cut_finished = TRUE;
1133                          /* do height override here for for remainder of job */
1134                          height_ovr_old = 0;
1135                          state = FINISH;
1136                      }
1137                  }
1138              }
1139              break;
1140          case MAX_HEIGHT:
1141              /* move to maximum height */
1142              feed_hold = TRUE;
1143              cornerlock_is_locked = FALSE;
1144              if(!probe_test){
1145                  if(offset_counts - setup_velocity > offset_max){
1146                      offset_counts -= setup_velocity;
1147                  }else if(offset_counts + setup_velocity < offset_max){
1148                      offset_counts += setup_velocity;
1149                  }else{
1150                      offset_counts = offset_max;
1151                      if(stop_type == WAIT){
1152                          feed_hold = FALSE;
1153                      }
1154                      /* do height override here for remainder of job */
1155                      height_ovr_old = 0;
1156                      state = FINISH;
1157                  }
1158              }
1159              break;
1160          case FINISH:
1161              /* clean up and return to idle state */
1162              target_volts = 0;
1163              cornerlock_is_locked = FALSE;
1164              kerfcross_is_locked = FALSE;
1165              led_down = FALSE;
1166              led_up = FALSE;
1167              ohmic_enable = FALSE;
1168              cut_length = 0;
1169              cut_time = 0;
1170              if(stop_type == STOP){
1171                  auto_cut = FALSE;
1172                  manual_cut = FALSE;
1173                  program_run = FALSE;
1174                  paused_motion = FALSE;
1175                  adaptive_feed = 1;
1176                  if(program_is_idle){
1177                      first_cut_finished = FALSE;
1178                      if(offset_counts - setup_velocity > 0){
1179                          offset_counts -= setup_velocity;
1180                      }else if(offset_counts + setup_velocity < 0){
1181                          offset_counts += setup_velocity;
1182                      }else{
1183                          offset_counts = 0;
1184                          stop_type = NONE;
1185                          safe_height_is_limited = FALSE;
1186                          cut_started = FALSE;
1187                          /* do height override here for one cut only */
1188                          /* height_ovr_old = 0; */
1189                          if(consumable_changing){
1190                              state = CONSUMABLE_CHANGE_OFF;
1191                          }
1192                      }
1193                  }
1194              }else{
1195                  state = IDLE;
1196              }
1197              break;
1198          case TORCHPULSE:
1199              /* single pulse the torch on and off */
1200              if(!torch_pulse){
1201                  torch_pulse_timer = torch_pulse_time;
1202                  if(torch_enable){
1203                      torch_on = TRUE;
1204                  }
1205                  torch_pulse = TRUE;
1206              }else{
1207                  if(torch_pulse_timer > 0){
1208                      torch_pulse_timer -= fperiod;
1209                  }else{
1210                      torch_on = FALSE;
1211                      if(!torch_pulse_start){
1212                          torch_pulse = FALSE;
1213                          state = IDLE;
1214                      }
1215                  }
1216              }
1217              break;
1218          case PAUSED_MOTION:
1219              /* a bit kludgy but we need a timer here for a minimum run
1220               * time to give the GUI time to poll the status channel */
1221              if(paused_motion_speed){
1222                  if(!paused_motion){
1223                      paused_motion_timer = 0.2; /* 0.2 seconds */
1224                      paused_motion = TRUE;
1225                      adaptive_feed = paused_motion_speed;
1226                      feed_hold = FALSE;
1227                      program_resume = TRUE;
1228                  }else{
1229                      paused_motion_timer -= fperiod;
1230                  }
1231              }else{
1232                  paused_motion_timer -= fperiod;
1233                  feed_hold = TRUE;
1234                  program_pause = TRUE;
1235                  if(program_is_paused && paused_motion_timer <= 0){
1236                      paused_motion = FALSE;
1237                      adaptive_feed = 1;
1238                      state = IDLE;
1239                  }
1240              }
1241              break;
1242          case OHMIC_TEST:
1243              /* wait here until ohmic_test input released */
1244              if (!ohmic_test){
1245                  ohmic_enable = FALSE;
1246                  state = IDLE;
1247              }
1248              break;
1249          case PROBE_TEST:
1250              /* wait here until probe_test input released */
1251              if(!probe_test){
1252                  probe_testing = FALSE;
1253                  if(offset_counts - setup_velocity > 0){
1254                      offset_counts -= setup_velocity;
1255                  }else if(offset_counts + setup_velocity < 0){
1256                      offset_counts += setup_velocity;
1257                  }else{
1258                      offset_counts = 0;
1259                      state = FINISH;
1260                  }
1261              }
1262              break;
1263          case SCRIBING:
1264              if(tool == SCRIBE){
1265                  if(!program_is_paused){
1266                      scribe_arm = TRUE;
1267  
1268                      if(scribe_arm_timer){
1269                          feed_hold = TRUE;
1270                          scribe_arm_timer -= fperiod;
1271                          if(scribe_arm_timer < 0){
1272                              scribe_arm_timer = 0;
1273                          }
1274                      }else if(scribe_on_timer){
1275                          feed_hold = TRUE;
1276                          scribe_on_timer -= fperiod;
1277                          if(scribe_on_timer < 0){
1278                              scribe_on_timer = 0;
1279                          }
1280                      }else if(scribe_arm && !scribe_arm_timer && !scribe_on){
1281                          feed_hold = TRUE;
1282                          scribe_on_timer = scribe_on_delay;
1283                          scribe_on = TRUE;
1284                      }else if(scribe_on && !scribe_on_timer){
1285                          feed_hold = FALSE;
1286                      }
1287                  }else{
1288                      scribe_arm = FALSE;
1289                      scribe_on = FALSE;
1290                      scribe_arm_timer = scribe_arm_delay;
1291                      scribe_on_timer = 0;
1292                  }
1293              }else{
1294                  scribe_arm = FALSE;
1295                  scribe_on = FALSE;
1296                  scribe_arm_timer = 0;
1297                  scribe_on_timer = 0;
1298                  state = IDLE;
1299              }
1300              break;
1301          case CONSUMABLE_CHANGE_ON:
1302              if(!consumable_changing){
1303                  if(x_offset == 0 && y_offset == 0){
1304                      x_velocity = 0;
1305                      y_velocity = 0;
1306                  }else if(x_offset == 0){
1307                      x_velocity = 0;
1308                      if(y_offset > 0){
1309                          y_velocity = x_y_velocity;
1310                      }else{
1311                          y_velocity = x_y_velocity * -1;
1312                      }
1313                  }else if(y_offset == 0){
1314                      if(x_offset > 0){
1315                          x_velocity = x_y_velocity;
1316                      }else{
1317                          x_velocity = x_y_velocity * -1;
1318                      }
1319                  }else{
1320                      angle_x_y = atan2(y_offset, x_offset);
1321                      x_velocity = x_y_velocity * cos(angle_x_y);
1322                      y_velocity = x_y_velocity * sin(angle_x_y);
1323                  }
1324                  consumable_changing = TRUE;
1325              }else{
1326                  if(x_velocity && x_offset_counts != x_offset){
1327                      if(fabs(x_offset_counts) + fabs(x_velocity) < fabs(x_offset)){
1328                          x_offset_counts += x_velocity;
1329                      }else{
1330                          x_offset_counts = x_offset;
1331                      }
1332                  }
1333                  if(y_velocity && y_offset_counts != y_offset){
1334                      if(fabs(y_offset_counts) + fabs(y_velocity) < fabs(y_offset)){
1335                          y_offset_counts += y_velocity;
1336                      }else{
1337                          y_offset_counts = y_offset;
1338                      }
1339                  }
1340              }
1341              if(x_offset_counts == x_offset && y_offset_counts == y_offset){
1342                  state = IDLE;
1343              }
1344              break;
1345          case CONSUMABLE_CHANGE_OFF:
1346              if(x_velocity){
1347                  if(fabs(x_offset_counts) - fabs(x_velocity) > 0){
1348                      x_offset_counts -= x_velocity;
1349                  }else{
1350                      x_offset_counts = 0;
1351                  }
1352              }
1353              if(y_velocity){
1354                  if(fabs(y_offset_counts) - fabs(y_velocity) > 0){
1355                      y_offset_counts -= y_velocity;
1356                  }else{
1357                      y_offset_counts = 0;
1358                  }
1359              }
1360              if(!x_offset_counts && !y_offset_counts){
1361                  consumable_changing = FALSE;
1362                  state = IDLE;
1363              }
1364              break;
1365          case DEBUG:
1366              /* holding state for debugging */
1367              rtapi_print_msg(RTAPI_MSG_ERR, "I have no idea how we got here...\n");
1368              break;
1369      }
1370  
1371      /* set debugging pins */
1372      state_out = state;
1373      stop_type_out = stop_type;
1374  
1375  }