lua Error Handling Macro
This is an example of how to write a lua macro that will perform error checking, abort if something goes wrong and report what goes wrong.
There are a lot of mc.mcCntlLog(...) calls in this example, that you wouldn't want in a real macro (i.e. Function location 1, 2, 3... or the Alpha, Beta, Delta, ...), but they help to show you in the log file what areas were called and in which order. The functionality performed in here is useful at times, but doesn't perform anything terribly useful in total for this macro, other than error processing and debugging.
Here is a YouTube video that will explain what is happening, but you should still be able to get a decent idea of what is happening by reading through the notes after the code and the pictures of the log files.
First we need to understand some functions that will report messages to the user.
All of these functions may be found in your "C:\Mach4Hobby\Docs\Mach4CoreAPI.chm" file.
The first two functions only post messages and don not affect the control state of Mach4:
mc.mcCntlLog()
number: rc = mc.mcCntlLog(number: inst, string: message, string file, number: line)
- rc is the return code, and will be mc.MERROR_NOERROR or 0 if nothing went wrong or a negative number if it did.
- inst is Instance, or the specific function instance that is calling this function: local inst = mc.mcGetInstance('info '). This only needs to be defined once per function or once per lua file.
- message is the message you want sent to the log file.
- fileA string buffer specifying the source file name. I almost always have it set to ""
- line An integer specifying the source line number. Leave it set to -1 unless you are using the file.
This will send information to the Menu -> Diagnostics -> Logging..., that you can open and press start (the play icon) in order to start logging. This is a great place to display lots of detailed information from your scripts.
mc.mcCntlSetLastError()
number: rc = mc.mcCntlSetLastError(number: inst, string: message)
- rc is the return code, and will be mc.MERROR_NOERROR or 0 if nothing went wrong or a negative number if it did
- inst is Instance, or the specific function instance that is calling this function: local inst = mc.mcGetInstance('info '). This only needs to be defined once per function or once per lua file.
- message is a string that will be sent to the History line or Error line visible at the bottom of the screen set
While this is called an error, it is more aptly called a history or informational message, since there are many messages sent there that are not errors.
These next two functions are very helpful inside lua macros (scripts) since they will stop the machine if an error condition occurs (provided you check for and handle that error condition). Either of these functions will stop the execution of the GCode after the macro ends, but will not halt the execution of the macro itself. You will need to put "return" statements in after the calling of these functions, in order to halt the macro itself.
mcCntlMacroAlarm(...)
number: rc = mc.mcCntlMacroAlarm(number: inst, number: error_number, string: error_message)
- rc is the return code, and will be mc.MERROR_NOERROR or 0 if nothing went wrong or a negative number if it did
- inst is Instance, or the specific function instance that is calling this function: local inst = mc.mcGetInstance('info '). This only needs to be defined once per function or once per lua file.
- error_number is the specific number you want displayed before the error message (this may aid in locating the source of the error)
- error_message is a string that you may define yourself or may be filled with the error report's message string
This lua API call:
mc.mcCntlMacroAlarm(inst, 16, 'Error 16 condition')
Does the same thing as this GCode call
#3000 = 16 (Error 16 condition)
This will result in:
- A Cycle Stop, halting GCode execution (not an EStop)
- The status history showing "Error 16 condition"
- Control is set to the ALARM state by raising the OSIG_ALARM signal. This alarm can only be canceled by pressing the screen set's Reset button. OSIG_ALARM can be used to drive a yellow beacon light or similar that is commonly found on production CNC machines.
mcCntlMacroStop(...)
number: rc = mc.mcCntlMacroStop(number: inst, number: error_number, string: error_message)
- rc is the return code, and will be mc.MERROR_NOERROR or 0 if nothing went wrong or a negative number if it did
- inst is Instance, or the specific function instance that is calling this function: local inst = mc.mcGetInstance('info '). This only needs to be defined once per function or once per lua file.
- error_number is the specific number you want displayed before the error message (this may aid in locating the source of the error)
- error_message is a string that you may define yourself or may be filled with the error report's message string
This lua API call:
mc.mcCntlMacroStop(inst, 12, 'Error 12 condition')
Does the same thing as this GCode call
#3006 = 12 (Error 12 condition)
This will result in:
- A Cycle Stop, halting GCode execution (not an EStop)
- The status history showing "Error 12 condition"
- Control is set to the IDLE state. No alarm signal will generated. This does not need to be canceled by pressing the screen set's Reset button. This is basically the same as pressing the Cycle Stop button.
Example Macro
The lua return codes may be found here.
This will demonstrate an example macro m2345.mcs . Please keep in mind that this is just demonstrating so things you can do in a macro, but not a truly useful macro in its entirety.
-------START OF MACRO CODE-------
--This if statement is here to allow the debugging from the lua editor, and will be the first code that the editor calls
if (mc.mcInEditor() == 1) then
local inst = mc.mcGetInstance('Fn m2345 InEditor ') -- Pass in the script number, so we can see the commands called by this script in the log
mc.mcCntlLog(inst, "~~~~Function location Alpha" , "", -1) -- This will send a message to the log window NOT NEEDED IN REAL CODE
m2345()
mc.mcCntlLog(inst, "~~~~Function location Gamma" , "", -1) -- This will send a message to the log window NOT NEEDED IN REAL CODE
end
--This is the function that Mach4 will call from GCode
function m2345()
--This will call your macro and allow for a report of what went wrong if something does go wrong
local inst = mc.mcGetInstance('Fn m2345 ') -- Pass in the script number, so we can see the commands called by this script in the log
mc.mcCntlLog(inst, "~~~~Function location Beta" , "", -1) -- This will send a message to the log windowNOT NEEDED IN REAL CODE
xpcall(m2345Core, m2345ErrorOut)
mc.mcCntlLog(inst, "~~~~Function location Delta" , "", -1) -- This will send a message to the log windowNOT NEEDED IN REAL CODE
end
--This function will handle any errors generated by the core macro code
local function m2345ErrorOut(msg)
--This will report something bad happening
local inst = mc.mcGetInstance('Fn m2345ErrorOut ') -- Pass in the script number, so we can see the commands called by this script in the log
mc.mcCntlLog(inst, "~~~~Function location 6" , "", -1) -- This will send a message to the log window
mc.mcCntlMacroAlarm(inst, 6, msg) -- You may want to replace this with mcCntlMacroStop instead of mcCntlMacroAlarm
mc.mcCntlLog(inst, "~~~~Function location 7" , "", -1) -- This will send a message to the log window
end
local zSafe = 1.2345 --This is local to contain it in this example code, but should exist in your screen load script or be declared globally somewhere else
function m2345Core()
local inst = mc.mcGetInstance('Fn m2345Core ') -- Pass in the script number, so we can see the commands called by this script in the log
mc.mcCntlLog(inst, "~~~~Function location 1" , "", -1) -- This will send a message to the log window
local rc = 0
local msg
rc = mc.mcSpindleSetDirection(inst,0)
if (rc ~= 0) then
--A non zero return code was bad...
msg = mc.mcCntrlGetErrorString(inst,0)
error(msg,1) --This will report the error
return --This will get us out of the macro without running the rest of it.
end
mc.mcCntlLog(inst, "~~~~Function location 2" , "", -1) -- This will send a message to the log window
rc = mc.mcCntlGcodeExecuteWait(inst, string.format("G0 G53 Z%0.4f\n",zSafe)) --Rapid to zSafe, this will fail if debugging
if (rc ~= mc.MERROR_NOERROR) then
--A non zero return code was bad...
--msg = mc.mcCntrlGetErrorString(inst,0)
error("GCode Aborted!",1) --This will report the error
return --This will get us out of the macro without running the rest of it.
end
local hGoodBuildVer
local StrGoodBuildVer
local hBadBuildVer
local StrBadBuildVer
mc.mcCntlLog(inst, "~~~~Function location 3" , "", -1) -- This will send a message to the log window
hGoodBuildVer, rc = mc.mcRegGetHandle(inst, string.format("ESS/Build_Version"))
if (rc ~= mc.MERROR_NOERROR) then
--A non zero return code was bad...
msg = mc.mcCntrlGetErrorString(inst,0)
error(msg,1) --This will report the error
return --This will get us out of the macro without running the rest of it.
end
StrGoodBuildVer, rc = mc.mcRegGetValueString(hGoodBuildVer)
if (rc ~= mc.MERROR_NOERROR) then
--A non zero return code was bad...
msg = mc.mcCntrlGetErrorString(inst,0)
error(msg,1) --This will report the error
return --This will get us out of the macro without running the rest of it.
end
mc.mcCntlLog(inst, "ESS Build Ver " .. StrGoodBuildVer, "", -1) -- This will send a message to the log window
mc.mcCntlLog(inst, "~~~~Function location 4" , "", -1) -- This will send a message to the log window
--START OF This section will fail
hBadBuildVer, rc = mc.mcRegGetHandle(inst, string.format("ESS/Build_VersionBADPath"))
if (rc ~= mc.MERROR_NOERROR) then
--A non zero return code was bad...
msg = mc.mcCntrlGetErrorString(inst,0)
error(msg,1) --This will report the error
return --This will get us out of the macro without running the rest of it.
end
--END OF This section will fail
StrBadBuildVer, rc = mc.mcRegGetValueString(hBadBuildVer)
if (rc ~= mc.MERROR_NOERROR) then
--A non zero return code was bad...
msg = mc.mcCntrlGetErrorString(inst,0)
error(msg,1) --This will report the error
return --This will get us out of the macro without running the rest of it.
end
mc.mcCntlLog(inst, "ESS Build Ver " .. StrBadBuildVer, "", -1) -- This will send a message to the log window
mc.mcCntlLog(inst, "~~~~Function location 5" , "", -1) -- This will send a message to the log window
local hInputSignal9
local InputSignal9
hInputSignal9,rc = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT9)
InputSignal9, rc = mc.mcSignalGetState(hInputSignal9)
--Do more stuff in your macro after here....
end --End of your macro
-------END OF MACRO CODE-------
Testing the Macro
Here is the output I received on the first run:
So I commented out the code associated around that mcGcodeExecuteWait (I wasn't running GCode, so that is why it could not do it now...)
Here is the second run:
So I removed the code around "ESS/Build_VersionBADPath", so we could get by that part.
And finally it ran all the way through the end