Hello Guest it is May 14, 2021, 09:11:33 PM

Author Topic: Coroutines, Button Scripts, Modules, Probing  (Read 1288 times)

0 Members and 1 Guest are viewing this topic.

Coroutines, Button Scripts, Modules, Probing
« on: January 31, 2018, 01:47:20 PM »
I am posting this as a means of saying thank you to all of you that have helped me to begin to understand how Mach4 operates and hope that this serves as a jump start for all of the new users of Mach4

This example shows:
How to use a coroutine in a button script so the GUI does not lock while probing.
The use of a module so that the routine can be used by any button.
Using Lua's ability to return multiple values from a function
Use of registers used by Mach4 for probe
Use of global vars set with the screen load script load modules section
Use of the scr variable to retrieve and set screen elements
Use of relative movements in Lathe since fanuc lathes do not recognize G91

A big thank you to DazTheGas for his video on coroutines: https://www.youtube.com/watch?v=t2xQYvAXT8o&feature=youtu.be
and to Brannab for her video on using the screen editor: https://www.youtube.com/watch?v=P1xZkFgS5cQ

Be sure to watch both of these if your new to Mach4.

The file 'load_modules.mcs' residing in the profile's '\Macros' directory and loads any of your self defined modules for the profile being used.

Code: [Select]
---------------------------------------------------------------
-- C:\Mach4Hobby\Profiles\MyTurn4\Macros\load_modules.mcs
-- Load modules that you want to be able to use from Mcodes
---------------------------------------------------------------
inst = mc.mcGetInstance()
local profile = mc.mcProfileGetName(inst)
local path = mc.mcCntlGetMachDir(inst)

package.path = path.."\\Profiles\\"..profile.."\\Modules\\?.lua;"..path.."\\Modules\\?.lua;"

--ErrorCheck module
package.loaded.mcErrorCheck = nil
ec = require "mcErrorCheck"

mm = require "mcMasterModule" -- resides: C:\Mach4Hobby\Modules
prb = require "mcProbing"     -- resides: C:\Mach4Hobby\Modules
rt = require "rtMyModule"     -- resides: C:\Mach4Hobby\Profiles\MyTurn4\Modules
gs = require "GcodeScripter"  -- resides: C:\Mach4Hobby\Profiles\MyTurn4\Modules


The following is one of the functions (probe) I use in a module 'rtMyModule.lua' that resides in the profile's '\Modules' subdirectory.  The function could be defined and used in a button script but would then not be available to any other button calls.  I use this function for five buttons on a tab I created.

Code: [Select]
local rtModule = {}

-- probes using relative positioning and returns the work and machine coordinates of a successful strike
-- rc==nil for failure and rc==0 for success
function rtModule.probe(prbNmbr,axis,dis,spd)
  if prb==nil then
    --build:3481 prb and mm are initialized in the load script. mcMasterModule.lu and mcProbing.lua
    wx.wxMessageBox('prb is nil')
    return nil,nil,nil
  end
  if not prb.CheckProbe(1,tonumber(prbNmbr)) then--CheckProbe throws an error message if probe is obstructed
    return nil,nil,nil
  end
  axis=string.upper(axis)
  --set vars for the axis to probe and their vars, see Mill GCode Programming.pdf page 19 g31
  --Lathe does not recognize G91 so axes are represented by alternate letters for relative positioning
  if axis=='Z'then
    prb.NilVars(5063,5063)
    prb.NilVars(5073,5073)
    relAxis='w'   
    mcAxis=mc.Z_AXIS
  elseif axis=='X'then
    prb.NilVars(5061,5061)
    prb.NilVars(5071,5071)
    relAxis='u'   
    mcAxis=mc.X_AXIS
  elseif axis=='Y'then
    prb.NilVars(5062,5062)
    prb.NilVars(5072,5072)
    relAxis='v'
    mcAxis=mc.Y_AXIS
--[[ need axis alternate letters for A,B,C
  elseif axis=='C'then
    prb.NilVars(5066,5066)
    prb.NilVars(5076,5076)
    relAxis='h' 
    mcAxis=mc.C_AXIS
  --]]
  else
    wx.wxMessageBox('Invalid Axis: '..axis)
  end;

  --execute the gcode and call the coroutine to let the GUI be updated
  --these two lines act like what you would expect mcCntlGcodeExecuteWait to do
  mc.mcCntlGcodeExecute(mc.mcGetInstance(),'g'..tostring(prbNmbr)..relAxis..tostring(dis)..' f'..tostring(spd))
  coroutine.yield()

  --check for valid probe strike
  local rc=mc.mcCntlProbeGetStrikeStatus(mc.mcGetInstance())
  if rc==0 then
    wx.wxMessageBox('Fault: No Probe Strike')
    return nil,nil,nil
  end;

  --find where the probe made contact
  local posWrk,rc=mc.mcAxisGetProbePos(mc.mcGetInstance(),mcAxis,0)--probe Strike Position Work Coords
  local posMach,rc=mc.mcAxisGetProbePos(mc.mcGetInstance(),mcAxis,1)--probe Strike Position Machine Coords

  --at this point you could execute more probes by:
  -- local s='g1'..tostring(prbNmbr)..relAxis..tostring(-dis)..'\n' --back off
  -- s=s..'g'..tostring(prbNmbr)..relAxis..tostring(dis)..'\n'      --probe
  -- mc.mcCntlGcodeExecute(mc.mcGetInstance(),s)
  -- coroutine.yield()

  return posWrk,posMach,0 --return the work and machine coordinates for the strike and indicate success with 0
end

return rtModule

--Button code to probe from current position for x minus direction

Code: [Select]
wait=coroutine.create(  --wait defined in PLC script found in screen edit mode top entry in the Screen Tree Manager
  function ()
  -- set results initially to nil for visual confirmation of good or bad result
  scr.SetProperty('txtProbeResult','Value','nil')
  scr.SetProperty('txtProbeResultMach','Value','nil')
  scr.SetProperty('txtProbeOver','Value','nil')
  -- get parameters
  local prbCde=scr.GetProperty('txtProbeCode','Value') --which probe input is being used: 31.0 31.1,31.2,31.3
  local dis=scr.GetProperty('txtProbeDistance','Value') -- max distance to probe
  local spd=scr.GetProperty('txtProbeSpeed','Value') -- speed to probe at
  local prbDia=tonumber(tostring(scr.GetProperty('txtProbeDiameter','Value'))) --used for tool setting calculation

  --call the probe function defined in Module rtMyModule.lua, rt set in Screen Load Script Load Module section
  --probe returns the position of the probe strike in work and machine coordinates
  -- probe returns nils for unsuccessful probe attempt
  local prbWrk,prbMach,rc=rt.probe(prbCde,'x','-'..dis,spd)
  if rc~=0 then
    return
  end

  local prbOver=mc.mcAxisGetMachinePos(mc.mcGetInstance(),mc.X_AXIS) -- overshoot of probe
  local prbErr=math.Wrk(prbMach-prbOver)  -- difference between Strike point and stop/overshoot point

  -- 0==success, update results
  if rc==0 then
    scr.SetProperty('txtProbeResult','Value',string.format('%.5f',prbWrk))
    scr.SetProperty('txtProbeResultMach','Value',string.format('%.5f',prbMach))
    scr.SetProperty('txtProbeOver','Value',string.format('%.5f',prbErr))
    mc.mcCntlSetLastError(mc.mcGetInstance(),'X Probe Complete')
  end
end
)

HTH

RT
Re: Coroutines, Button Scripts, Modules, Probing
« Reply #1 on: February 04, 2018, 03:58:20 PM »
Coroutines have been a bit of a mystery to me: I am much happier with the concept of independent threads and semaphores. So thank you for posting and whetting my appetite to give them a try.

I too have extensively re-written the mcProbing module. Amonst other things, all Gcode is funnelled through a single function, the idea being that it would be easy to incorporate the necessary coroutine.yield() call if I decided to go down that path. As I have found that the GUI does not lock up even though I exclusively  use mcCntlGcodeExecuteWait  I had not taken that option.

Seeing your post left me unable to resist giving coroutines a try in a tool length measuring function that I was working on. This is initiated by a call from a button script. I found that with or without the coroutine approach, the function worked well, moving the tool over a sensor, probing, and returning the tool to its starting position without any obvious lock up or drag on the GUI.

I then ran a small program with an M6 macro that waited on a signal for manual tool insertion. During this hold, Mach4 remained in a File Run  state. I tried running the measurement function, again via a screen button, during this M6 hold. I modified the coroutine.resume function to test for the File Run  state in addition to Idle.  I found that the original routine ran perfectly during the M6 hold, with no GUI lock ups. To my complete surprise, the coroutine version of the routine locked up the DROs  which only caught up when the routine had completed.

This has left me wondering whether coroutines are the panacea that I had once imagined. I have removed them from my code, at least for now, as it seems to work better without. I welcome any ideas as to what might have caused the poor performance with coroutines when in the macro hold, as I am currently mystified by it.

Allan