Hello Guest it is April 24, 2024, 01:14:59 PM

Author Topic: Calling ScreenScript or module functions from macro  (Read 1059 times)

0 Members and 1 Guest are viewing this topic.

Calling ScreenScript or module functions from macro
« on: July 21, 2023, 07:07:29 AM »
Hi,

Mach4 and Lua beginner here. I've seen several related questions here but I cannot really translate the answers to my specific issue, so I start a new thread, sorry if this is a duplicate.

I've been trying something very simple for some time now and cannot get it to work, I suspect the reason is I don't understand how Lua scripts are run and how the entire Mach4 environment is set together. I made a module that I am successfully using from ScreenScript, it is made for sending OSC messages to another application via UDP. The module is placed in Modules/OSCDRO.lua and uses some Lua libraries that I added to the Mach4Hobby root folder.

Short Version:
I am trying to call a function that I defined in the ScreenScript from out of ta Module script. Is that even possible? Since I failed, I tried to use module directly in my macro and not take the detour of calling a ScreenScript-function, as I understand that the module loaded in the ScreenScript should also be available in the module (?). But I also failed with that. So, what would already solve my problem would be a way to call a function defined in the ScreenScript out of a macro.

Detailed Version:
I load and initialize it in the screen load script simply with
Code: [Select]
package.loaded.OSCDRO = nil
osc = require "OSCDRO"
oscIntervalMS = 10 --ms

--Open the osc socket
if(osc.Setup()=="") then
    scr.StartTimer(0,oscIntervalMS,0)
end

In the screen timer script I send my packages as follows:
Code: [Select]
if timer == 0 then
if(machEnabled~=0) then
osc.SendXYZ()
end
end

This works fine so far. Now I would like to make use of M-Codes and call, e.g., osc.SendM(100) from my M100 macro. I tried calling a global function from the macro and could not get it to work, apparently it was not found (?), then I tried to access the osc object, but . My Profiles/Mach4Mill/Macros/m100.mcs looks as follows:
Code: [Select]
function m100()

local inst = mc.mcGetInstance()
mc.mcCntlLog(inst, "M100" ,"" ,0 )

        osc=require "OSCDRO"
mc.mcCntlLog(inst, "require okay" ,"" ,0 )

if(osc == nil) then
mc.mcCntlSetLastError(inst, "osc not loaded")
end

if(osc.Setup()=="") then
osc.SendM(100)
else
mc.mcCntlSetLastError(inst, "osc setup failed")
end
end

if (mc.mcInEditor() == 1) then
m100()
end
What's particularly strange here is, that the script execution seems to stop at the line containing the "require", since I don't get any log messages that come after this line.

Note that I would actually prefer to do something like this, because I don't want another instance of osc in every macro, since I would prefer to use one single UDP sender instead of dozens:
Code: [Select]
function m100()
sendM(100) --calling function in ScreenScript, that calls osc.SendM(100)
end

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

What's also strange is that when I run the debugger for my m100.mcs in ZeroBrane everything works well, osc is loaded via require and SendM is called perfectly, but when I run using the "Cycle Start MDI" button, it doesn't: the macro script seems to run, but the require fails.

I don't understand this whole behavior at all, I tried so many things by now by trial-and-error, but I keep failing. I would therefore highly appreciate some advice. Apparently it has to do with something I'm not aware of, e.g., the script loading order, the interaction between ScreenScript and macros, maybe different Lua environments they are run in, execution order, some Lua syntax or scope pitfalls that I'm oblivious about... idk.

(Using Mach4Hobby 4.2.0.5036)
Re: Calling ScreenScript or module functions from macro
« Reply #1 on: July 21, 2023, 07:41:26 AM »
Update (cannot edit the original post): I figured out that macro execution swallows up the errors raised when requiring -- I now did this (posting this here since it may be helpful for others):
Code: [Select]
local ok, result_or_error = pcall(require, 'OSCDRO')

if ok then
-- 'require' was successful, 'result_or_error' is the result of 'require'
local osc = result_or_error
-- Now you can use 'your_module'
else
-- 'require' failed, 'result_or_error' is the error message
mc.mcCntlLog(inst, "Error loading module: " .. result_or_error, "", 0)
end
and found that the module was not found. I added now this
Code: [Select]
package.path = package.path .. ';./Modules/?.lua'
package.path = package.path .. ";./Modules/?.luac"
package.path = package.path .. ";./Modules/?.mcs"
package.path = package.path .. ";./Modules/?.mcc"
package.cpath = package.cpath .. ";./Modules/?.dll"
and now at least the lua and dll files of all dependencies are found.

Now however, I have a different problem, during loading a dependency (Lua OSC-library "losc"), I get another error that I don't get when I use the same code in the ScreenScript.
Quote
2023-07-21 13:28:01.020 [MR   ] - Error loading module: E:\Mach4Hobby\losc\types.lua:168: attempt to yield across a C-call boundary :0
The cause of this is even harder for me to figure out... so, coming back to what I initially wanted to do, is there a good way to call my OSC-code from the ScreenScript and just trigger this from the macro code? I was thinking of using registers as a workaround, e.g., setting some register in the macro and polling in the ScreenScript. But then I am concerned about race-conditions. I.e., if I have a sequence of several M100's in my Gcode, how would I guarantee I don't miss one in the ScreenScript?

Is there a bullet-proof solution or workaround for my requirement?

Offline Bill_O

*
  •  563 563
    • View Profile
Re: Calling ScreenScript or module functions from macro
« Reply #2 on: July 21, 2023, 10:58:42 AM »
You are beyond what I know how to do but this might help.

While one part of M4 is running another can not also run.
Instead of using a function in the screen script make the m100 an actual macro and see if it will run from your module.
Re: Calling ScreenScript or module functions from macro
« Reply #3 on: July 21, 2023, 12:37:35 PM »
Thanks for your reply. What do you mean by "actual macro", though?

The complete file Mach4Hobby/Profiles/Mach4Mill/Macros/m100.mcs now looks like this:
Code: [Select]
package.path = package.path .. ';./Modules/?.lua'
package.path = package.path .. ";./Modules/?.luac"
package.path = package.path .. ";./Modules/?.mcs"
package.path = package.path .. ";./Modules/?.mcc"
package.cpath = package.cpath .. ";./Modules/?.dll"

function m100()

local inst = mc.mcGetInstance()
mc.mcCntlLog(inst, "M100" ,"" ,0 )

        osc=require "OSCDRO"
mc.mcCntlLog(inst, "require okay" ,"" ,0 )

if(osc == nil) then
mc.mcCntlSetLastError(inst, "osc not loaded")
end

if(osc.Setup()=="") then
osc.SendM(100)
else
mc.mcCntlSetLastError(inst, "osc setup failed")
end
end

if (mc.mcInEditor() == 1) then
m100()
end
Isn't this an "actual macro"? The way I got it, in Mach4, the script files in this folder, named according to the M-Code they are supposed to be associated to, are referred to as "macros". No?

To reiterate, maybe I'm getting it all wrong. My overall goal is to trigger an action (sending OSC messages) from within Gcode. I figured I could use M-codes for this, and I thought I've got two options to script this in M4:
  • write a macro associated with the respective M-code and trigger some script that is defined elsewhere (e.g., in the ScreenScript), or
  • write a macro associated with the respective M-code and "require" my OSC-module in the macro itself, accepting that I've got multiple instances of my OSC object, even though only one would suffice.
Unfortunately, I cannot get get to work either of those options: the former because I cannot find a way to call the ScreenScript functions, the latter because for some dubious reason my dependencies would not load without errors in the macro (even though they work fine in the ScreenScript).

Am I completely on the wrong track? Or maybe there is another way to accomplish what I want that is easier to do?
« Last Edit: July 21, 2023, 12:48:39 PM by iko79 »

Offline Bill_O

*
  •  563 563
    • View Profile
Re: Calling ScreenScript or module functions from macro
« Reply #4 on: July 21, 2023, 12:48:06 PM »
In the M4  folder you have a Profiles\Profile you are using\Macros folder.
It has things like m3 (spindle on) and m6 (tool change) macros.
Just make one for your m100.
Then in your module tell it to run g code
mc.mcCntlGcodeExecuteWait(inst, "m100")
or
mc.mcCntlGcodeExecute(inst, "m100")
or
mc.mcCntlMdiExecute(inst, 'm100')

I really do not know if any of this will work but it might.
Re: Calling ScreenScript or module functions from macro
« Reply #5 on: July 21, 2023, 02:02:33 PM »
I'm afraid it won't, but again thanks for the reply. I am pretty sure I have the right folder. The macro does get called, as you realize from my post, so everything seems to be in order. Files for m6, m162, m163 are there, not for m3 though. idk, maybe you use a different version with different folder structure. Also, from the documentation, "Scripting Manual.pdf":
Quote
Using custom M codes requires the scripts to be located in the ‘Macros’ folder located in the folder for
the desired profile. Navigate to the Mach4 root directory, usually located on the C drive, open the
‘Profiles’ folder, then open the folder of the desired profile. The name of the folder will be the name of
the profile, ‘Mach4Mill’ for example. The ‘Macros’ folder will be located in this profile folder.

Regarding the execution of the Gcode, I am not sure we are talking about the same thing: I don't want to execute G-code from my macro. I don't know what sense it would make to execute a "M100" in the macro that's supposed to be called when M100 is encountered in the Gcode. To me this seems upside-down, or actually recursive. What I am in fact doing is I run MDI in Mach4 via the GUI, I thought that what it's meant for: MDI tab, enter my Gcode there in the textfield (including the M-codes that I want to trigger the macros, e.g., M100 that should execute the m100.mcs/mcc macro, which should in turn send an OSC message), then press "Enable", then "Cycle Start MDI". The machine will be run and on ever encounter of a "M100", the m100.mcs/mcc should be executed. For example somehow like this:
Code: [Select]
x0 y0 f100
m100
x1 y0
m100
x1 y1
m100

I figure this is the way it is meant to be done, correct me if I'm wrong.
« Last Edit: July 21, 2023, 02:05:28 PM by iko79 »

Offline Bill_O

*
  •  563 563
    • View Profile
Re: Calling ScreenScript or module functions from macro
« Reply #6 on: July 21, 2023, 02:30:15 PM »
An m code is a g code.
Do you not have a macros folder in your profile?
You are wanting to run a macro but not putting it in the macros folder.
It might be because of the different parts i was talking about.
Anything you have in your screen you need to put in a macro instead and try it that way.
I gave you 3 ways to try running the macro if you have it in your macro folder.
Some work better in some places than others.
I have never used a module so I do not know which works best in it.
If you do make the macro you need to comment out the function you have in your screen.
Re: Calling ScreenScript or module functions from macro
« Reply #7 on: September 08, 2023, 10:13:43 PM »
To include a module in a Mach4 macro, you can do this:

Code: [Select]
function m555()
    local inst = mc.mcGetInstance() 

    -- get your current profile/path names & disk location
    local profile = mc.mcProfileGetName(inst)
    local path = mc.mcCntlGetMachDir(inst)
       
    -- this is a comment

    -- point to the module 'directory'
    package.path = path .. "\\Profiles\\" .. profile .. "\\Modules\\?.lua;" .. path .. "\\Modules\\?.lua;"   
   
    -- debug your path target
    mc.mcCntlSetLastError(inst, tostring(package.path))       
   
    -- import your module
    FiveFiveFiveTimer= require "FiveFiveFiveTimer"                       

    -- use your module
    FiveFiveFiveTimer.startTimer.(param)
end

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

Your error is probably a double include, since you don't have to include all that stuff you are doing globally before the function. You only need to include the Modules folder to use a module in a macro.
« Last Edit: September 08, 2023, 10:25:02 PM by compewter_numerical »
Re: Calling ScreenScript or module functions from macro
« Reply #8 on: September 08, 2023, 10:42:10 PM »
I don't think the scr.'' functions work in macros so you can just create a 'getter' function for the screen elements in your module.