Machsupport Forum

Mach Discussion => Mach4 General Discussion => Topic started by: thespindoctor on September 14, 2021, 09:45:15 AM

Title: activate script on button from script
Post by: thespindoctor on September 14, 2021, 09:45:15 AM
I would like to combine the scripts on several buttons on a screen into a separate single button without copying the scripts from those buttons.  How does one push a button to activate a script from another button on the screen from a script?  thanks
Title: Re: activate script on button from script
Post by: Bill_O on September 14, 2021, 11:24:49 AM
which buttons are you wanting to do?
Title: Re: activate script on button from script
Post by: DazTheGas on September 14, 2021, 11:50:26 AM
You could use

Code: [Select]
scr.ButtonClick(ctrlName)
scr.ButtonDown(ctrlName)
scr.ButtonUp(ctrlName)

DazTheGas
Title: Re: activate script on button from script
Post by: thespindoctor on September 14, 2021, 12:40:42 PM
Thanks Daz!!
Title: Re: activate script on button from script
Post by: smurph on September 16, 2021, 12:47:02 PM
Code: [Select]
scr.ButtonClick(ctrlName)
scr.ButtonDown(ctrlName)
scr.ButtonUp(ctrlName)

Yeah, I put those functions in there because Mach3 had something similar.  But I do so regret it!  Because it is not the best way to handle this type of thing in Mach4.  This is going to be a long one, so sit back with some coffee or something.  :)

The best way is to put the functions that do your actions into the screen load script.  There would be one function for each of your current buttons.  Then on each button's up script event, simply call the appropriate function.  Then in your multi-function button, call all of the functions in the order you wish.

In the screen load script:
Code: [Select]
function MyButtonFunction1()
    --some code to perform the action
end

function MyButtonFunction2()
    --some code to perform the action
end
In the button1 up script event:
Code: [Select]
MyButtonFunction1()
In the button2 up script event:
Code: [Select]
MyButtonFunction2()
In the multiple function button up script event:
Code: [Select]
MyButtonFunction1()
MyButtonFunction2()

Basically, you build a tool box of functions in the screen load script.  The you just call those functions from any screen element.  It just takes a bit more planning but ultimately it is WAY worth it. 

But wait, there's more!  :)  What if you might want to call these tool box functions from M code macro scripts as well?  This is where LUA modules come into the picture.  If you write a module and put your tool box functions in there, then "require" that module in you screen load script and your macro scripts, you can use all of the functions in that module from both screen elements AND macro scripts.  The caveat is that global variables cannot be used.  However, one can get around that by using Mach registers as your global data storage if you need it. 

in a file called MyToolboxModule.lua that is in the Mach Modules directory:
Code: [Select]
local MyToolbox = {}

function MyToolbox.MyButtonFunction1()
--some code to perform the action
end

function MyToolbox.MyButtonFunction2()
--some code to perform the action
end
return MyToolbox
In your screen load script, put this at the top of the script:
Code: [Select]
mt = require("MyToolboxModule") -- mt is the namespace by which you access the functions in the module.  Short for "My Toolbox". :)
In a file called MyToolbox.mcs in your profile's macros directory:
Code: [Select]
mt = require("MyToolboxModule") -- mt is the namespace by which you access the functions in the module.  Short for "My Toolbox". :)
Now, back to the button scripts themselves. In the button1 up script event:
Code: [Select]
mt.MyButtonFunction1() -- notice the mt. prefix! 
In the button2 up script event:
Code: [Select]
mt.MyButtonFunction2()
In the multiple function button up script event:
Code: [Select]
mt.MyButtonFunction1()
mt.MyButtonFunction2()
And finally, a macro script that uses the tool box functions. In a file called m114.msc in the Macros directory:
Code: [Select]
function m114()
    mt.MyButtonFunction1()
    mt.MyButtonFunction2()

end

if (mc.mcInEditor() == 1) then
    m114()
end

It is a lot to take in, but it does demonstrate the power of the Mach LUA integration.  It makes code reuse easy too.  Say you have another machine that needs the same functions.  Or another screen/profile.  All you have to do is move the module code over and "require" it.  :) 

Steve
Title: Re: activate script on button from script
Post by: thespindoctor on September 17, 2021, 05:04:50 AM
Thanks so much Steve! I am devouring your post to understand and it will definitely up my game..
Title: Re: activate script on button from script
Post by: thespindoctor on September 18, 2021, 10:39:04 AM
So far not having luck with the basic problem.  I see how to put functions into the script load which works fine and is definitely a better way to organize my programming.  However the issue now is that I need to move the motor which tilts a plate that a person is standing on.  Then the motor pauses and waits in that position while recording values from analog sensors in the PLC program.  When the recording is done, the array is added to an open file.  Now repeat a motor move and record again.

So I need to move to 0 degrees, let the PLC record for 5 seconds, save the data array to the open file, move 2 degrees and repeat.  What happens is in the script, all the G code moves execute so the plate goes on to 0,2,4,6 degrees without stopping to let the PLC record and at the end of all the moves, the recording starts.  No matter what I have tried from G4, while loop, to timer it will not wait.  I figured the while loop would be perfect to have the G move the while the global variable startrecording = "on" do nothing then save the values when startrecording = "off" but it just goes through all the programmed G moves. 

What am I missing?  Seems like a simple issue I am not understanding.

So PLC has the routine to sample the analog sensors 10 times a second for 5 seconds.  startrecording is a global variable controlled from a screen button with startrecording="on" and PLC turns it off after 5 seconds controlled by monitoring sweeps.  It all works great when I use separate buttons to control each move and record but combining everything into one script in one button is not working.

Thanks
Title: Re: activate script on button from script
Post by: smurph on September 19, 2021, 12:52:25 PM
Separate buttons == separate events.  That is why it worked.  Now you have one button with one event.  And here is the insidious part, the PLC cycle is an event!  So by running that one event, it prevents the PLC script from executing. 

So the rule of thumb is to not have long running processes in button or other screen elements.  This is accomplished by the use of a "state machine".  All the button script code would do is start the state machine. 

Button code:
Code: [Select]
StartRecording()

Screen Load script:
Code: [Select]
RecordingProcessState = 0 -- a global LUA variable for the state machine state.

function StartRecording()
    RecordingProcessState = 1 -- start the recording state machine.
end
function Execute0()
    --Gcode to execute the 0 degree stuff
    mc.mcGcodeExecute() (Don't use mc.mcGcodeExecuteWait() as you want to hand the task off to the interpreter and go!
    wxMilliSleep(50) -- give the G code a chance to start.end
function Execute2()
   --Gcode to execute the 2 degree stuff
    mc.mcGcodeExecute()
    wxMilliSleep(50)
end
function Execute4()
   --Gcode to execute the 4 degree stuff
    mc.mcGcodeExecute()
    wxMilliSleep(50)
end
function Execute6()
   --Gcode to execute the 6 degree stuff
    mc.mcGcodeExecute()
    wxMilliSleep(50)
end
function WaitForIdle()
    local state = mc.mcCntlGetState()
    if (state == mc.MC_STATE_IDLE) then
         return true
    end
    return false
end

function RecordingStateMachine()
    if (RecordingProcessState > 0) then
        if (RecordingProcessState == 1) then
            RecordingProcessState = 2
            Execute0()
        elseif (RecordingProcessState == 2) then
            -- wait for Execute0() to finish.
            if (WaitForIdle() == true) then
                RecordingProcessState = 3
            end
        elseif (RecordingProcessState == 3) then
            RecordingProcessState = 4
            Execute2()
        elseif (RecordingProcessState == 4) then
            -- wait for Execute2() to finish.
            if (WaitForIdle() == true) then
                RecordingProcessState = 5
            end
        elseif (RecordingProcessState == 5) then
            RecordingProcessState = 6
            Execute4()
        elseif (RecordingProcessState == 6) then
            -- wait for Execute4() to finish.
            if (WaitForIdle() == true) then
                RecordingProcessState = 7
            end
        elseif (RecordingProcessState == 7) then
            RecordingProcessState = 8
            Execute6()
        elseif (RecordingProcessState == 8) then
            -- wait for Execute6() to finish.
            if (WaitForIdle() == true) then
                RecordingProcessState = 9
            end
        elseif (RecordingProcessState == 9) then
            RecordingProcessState = 0
            -- The state machine has finished!!!  Maybe raise a signal or the like?
        end
    end
end

Then is your PLC script, call the state machine function

Code: [Select]
RecordingStateMachine()

The whole concept of a state machine lets you run long running processes without actually tying up the event loop with a while loop.  Generally speaking, while loops should be avoided at all costs in any of the screen scripts.  They are ok in M code scripts when you want to wait on the whole script to complete anyway. 

You will also want to put some sort of abort mechanism in case the operator wants to abort during the process (set RecordingProcessState = 0).  Notice that if you use Stop Cycle (which I hate), you will have to modify your screen to make the stop cycle button stop the cycle AND set RecordingProcessState = 0, otherwise, the state machine will see the machine go to the idle state and continue to the next phase! 

Steve
Title: Re: activate script on button from script
Post by: thespindoctor on September 19, 2021, 01:57:47 PM
Thanks so much Steve!  Trying to wrap my head around it. Still hampered by lacking some basic skills.
There are 3 events to deal with
  1.) Move the plate with Gcode in degrees then after completed
  2.) turn on recording - then turn it off after 5 seconds
  3.) Save the data collected to the open array and save the file
repeat

Each step has to be completed before the next.  I will start as simple as possible to see if I can get the machine states concept to work.

Still not getting Mcode to work either despite following your explicit example.  Not sure what I am doing wrong.  Just trying to get a wx.messagebox to activate through an M code.  Set up my own ToolBoxmodule but not successfully.  Lots of trial and error and gradually getting familiar.
Title: Re: activate script on button from script
Post by: smurph on September 19, 2021, 02:47:26 PM
Use mc.mcCntlSetLastError() to debug instead of wx.MessageBox().  That way you can see the history. 

Steve
Title: Re: activate script on button from script
Post by: thespindoctor on September 20, 2021, 06:55:59 PM
Steve and Daz, Back in 2016 I was helped with the same issue of how to set up an Mcode in Mach4. Steve has listed the steps again but it does not work for me.  I am not sure where I am failing but back in 2016, Daz listed putting the require line into a module load file in the profile/macros directory which Steve did not mentioned this time.  Is this an omission or a change in the version of Mach4? I think I have done everything else correctly but the Mcode programmed M100 never does anything.  I have stepped away from Mach4 and Lua for several years and am back trying to get proficient again, sorry.

I updated to the newest version today since I was running on a version from 2016 but that has not helped.  The post dealing with the topic was topic=33849.20  This time, we are taking about a toolbox which I would like to implement.  Should I start a new topic or continue here. I also am working on Steve's last help for machine state, but not successful yet.

Thanks
Title: Re: activate script on button from script
Post by: smurph on September 21, 2021, 01:36:37 AM
I did mention the module load file. 

In a file called MyToolbox.mcs in your profile's macros directory:
Code: [Select]
mt = require("MyToolboxModule") -- mt is the namespace by which you access the functions in the module.  Short for "My Toolbox". :)

There are several examples out here on the forum that use one file to load/require all modules that you want in the macro script environment.  But I like to put each require into their own separate file, named appropriately of course.  To me, it is a little more clear when you go looking at the stuff months from now.  :) 

Steve
Title: Re: activate script on button from script
Post by: thespindoctor on September 23, 2021, 12:46:04 PM
How about using register flags like switches on the old machines before CNC

have reg1, reg2, reg3.  0=off 1=on

reg1 on and reg2 and reg3 off means automatic testing started go ahead and do G code move

reg1on and reg2 on and reg3 off means G code move finished so ok to record from analog inputs then when finished recording turn reg3 on

reg1,2,3 on means done and now save data and switch reg2 and 3 off indicating back to G code move

Title: Re: activate script on button from script
Post by: thespindoctor on September 28, 2021, 08:32:14 AM
function WaitForIdle()
    local state = mc.mcCntlGetState()
    if (state == mc.MC_STATE_IDLE) then
         return true
    end
    return false
end

cant get this to work  tried lots of stuff but it will not compile

thanks
Title: Re: activate script on button from script
Post by: SwiftyJ on September 28, 2021, 11:15:45 AM
You are missing the 'inst' from mcCntlGetState.. see below

Code: [Select]
function WaitForIdle()
    local inst = mc.mcGetInstance()
    local state = mc.mcCntlGetState(inst)
    if (state == mc.MC_STATE_IDLE) then
         return true
    end
    return false
end
Title: Re: activate script on button from script
Post by: thespindoctor on September 28, 2021, 12:58:36 PM
function WaitForIdle()
    local inst = mc.mcGetInstance()
    local state = mc.mcCntlGetState(inst)
    if (state == mc.MC_STATE_IDLE) then
         return true
    end
    return false
end

I think I tried the inst without success, but will try again   thanks
Title: Re: activate script on button from script
Post by: thespindoctor on September 28, 2021, 01:03:57 PM
function WaitForIdle()
    local inst = mc.mcGetInstance()
    local state = mc.mcCntlGetState(inst)
    if (state == mc.MC_STATE_IDLE) then
         return true
    else
    return false
   end --added end here
end

is this better but I think it still fails I will try both
Title: Re: activate script on button from script
Post by: thespindoctor on September 30, 2021, 09:11:11 AM
This is the proper script!

function WaitForIdle()
   local inst = mc.mcGetInstance()
   local state = mc.mcCntlGetPoundVar(inst, mc.SV_MSTATE)--Returns the Machine State
   --Idle will return 0
      if (state == 0) then
      return true
   else
      return false
   end
end
Title: Re: activate script on button from script
Post by: smurph on September 30, 2021, 01:37:16 PM
I would use mc.mcCntlGetState(inst) over the #var.  The G code variables can be stale depending on what the interpreter is doing. 

This compiles fine:
Code: [Select]
function WaitForIdle()
    local inst = mc.mcGetInstance()
    local state = mc.mcCntlGetState(inst)
    if (state == mc.MC_STATE_IDLE) then
return true
    end
    return false
end

Steve
Title: Re: activate script on button from script
Post by: thespindoctor on September 30, 2021, 01:58:04 PM
Thanks Steve