Machsupport Forum

Mach Discussion => Mach4 General Discussion => Topic started by: RecceDG on February 01, 2021, 11:19:14 AM

Title: Scripting Custom Control Panel
Post by: RecceDG on February 01, 2021, 11:19:14 AM
So I have built a custom control panel for my Mach4/EthernetSmoothStepper lathe. It has:

1. Pushbuttons for Cycle Start, Feed Hold, and Manual Spindle (for tailstock drilling etc where the spindle needs to run but no machine motions take place)
2. An E-Stop mushroom
3. A Jog Handle, consisting of an MPG, an X/Z selector Switch, and a 4-position rotary switch for OFF/0.1/0.01/0.001
4. A Program Run LED
5. A Feed Hold LED
6. A Manual Jog Active LED

I'm now at the point where I can wire it into the controller and set up scripting.

Here are the functions if should do:

1. If Cycle Start is pressed, it should react the same as if the on-screen button has been clicked. The on-screen button should light the Program Run LED while a program is running, and turn it off when the program stops.

2. If Feed Hold is pressed when a program is running, it should react the same as if the on-screen button has been clicked. The on-screen button should light the Feed Hold LED and turn off the Program Run while a program is paused, and reverse the LEDs when the program is resumed.

3. If Manual Spindle Start is pressed, the script should check to see if a non-zero value is set in Program RPM. If yes, turn on the spindle. If no, set Program RPM to 1200 and turn on the spindle. If the spindle is already running, turn it off.

4. If EStop is activated, stop the machine.

5. If the rotary Jog Resolution switch is set to 0.1, 0.01, or 0.001, set the MPG resolution to the appropriate value, activate the axis selected by the X/Z selector switch, and turn on the Jog Handle LED. If no resolution is selected, set the MPG resolution to 0, deselect all jog axis, and turn off the Jog LED.

My initial kick at the cat is below. It is missing some functions, identified as "FIXME". Comments and suggestions welcome.

Code: [Select]
--------------------------------------
-- FNR Control Console Code --
--------------------------------------
SigLib = {
    [mc.ISIG_INPUT10] = function (state) -- Cycle Start
        RunFNR()
    end,
    [mc.ISIG_INPUT11] = function (state) -- Feed Hold
        RunFNR()
    end,
    [mc.ISIG_INPUT12] = function (state) -- Manual Spindle Start
        RunFNR()
    end,
    [mc.ISIG_INPUT13] = function (state) -- EStop
        RunFNR()
    end,
    [mc.ISIG_INPUT14] = function (state) -- Jog On, 0.001
        RunFNR()
    end,
    [mc.ISIG_INPUT15] = function (state) -- Jog On, 0.01
        RunFNR()
    end,
    [mc.ISIG_INPUT16] = function (state) -- Jog On, 0.1
        RunFNR()
    end
}
---------------------------------------------------------------
-- FNR Control Console function.
---------------------------------------------------------------
function RunFNR()
    local hSig, rc = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT10) -- Is mapped to Port X Pin X *Cycle Start
    local FNRCycleStart, rc = mc.mcSignalGetState(hSig)
    local hSig, rc = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT11) -- Is mapped to Port X Pin X *Feed Hold
    local FNRFeedHold, rc = mc.mcSignalGetState(hSig)
    local hSig, rc = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT12) -- Is mapped to Port X Pin X *Spindle Start
    local FNRSpindleStart, rc = mc.mcSignalGetState(hSig)
    local hSig, rc = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT13) -- Is mapped to Port X Pin X *EStop
    local FNREStop, rc = mc.mcSignalGetState(hSig)
    local hSig, rc = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT14) -- Is mapped to Port X Pin X *.001 Select
    local FNRStep001, rc = mc.mcSignalGetState(hSig)
    local hSig, rc = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT15) -- Is mapped to Port X Pin X *.010 Select
    local FNRStep010, rc = mc.mcSignalGetState(hSig)
    local hSig, rc = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT16) -- Is mapped to Port X Pin X *.100 Select
    local FNRStep100, rc = mc.mcSignalGetState(hSig)
    local hSig, rc = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT17) -- Is mapped to Port X Pin X *X Axis Jog Select
    local FNRXJog, rc = mc.mcSignalGetState(hSig)
    local hSig, rc = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT18) -- Is mapped to Port X Pin X *Z Axis Jog Select
    local FNRZJog, rc = mc.mcSignalGetState(hSig)
    local FNRJogLED, rc = mc.mcSignalGetHandle(inst, mc.OSIG_OUTPUT10)-- Is mapped to Port X Pin X *Jog Active LED
    local FNRRunLED, rc = mc.mcSignalGetHandle(inst, mc.OSIG_OUTPUT11)-- Is mapped to Port X Pin X *Program Run LED
    local FNRPauseLED, rc = mc.mcSignalGetHandle(inst, mc.OSIG_OUTPUT12)-- Is mapped to Port X Pin X *Pause LED

    -- Set active axis based on X/Z switch
    if FNRXJog == 1 then
        mc.mcMpgSetAxis(inst, 0, 0) --X Axis
        mc.mcCntlSetLastError(inst, "X Jog Selected")
    elseif FNRZJog == 1 then
        mc.mcMpgSetAxis(inst, 0, 1) --Z Axis
        mc.mcCntlSetLastError(inst, "Z Jog Selected")
    else
-- Can't get here, so there's a fault
        mc.mcMpgSetAxis(inst, 0, -1) --No Axis
        mc.mcCntlSetLastError(inst, "No Jog Axis Selected - IMPOSSIBLE - EStop triggered!")
        mc.mcSignalSetState(FNRJogLED, 0)
        mc.mcCntlEStop(inst)
    end

    -- Set step resolution and activate jog based on rotary selector switch
    if FNRStep001 == 1 then
        mc.mcMpgSetInc(inst, 0, .001)
mc.mcCntlSetLastError(inst, "MPG JOG ON - Thousandths")
        mc.mcSignalSetState(FNRJogLED, 1)
    elseif FNRStep010 == 1 then
        mc.mcMpgSetInc(inst, 0, .010)
        mc.mcCntlSetLastError(inst, "MPG JOG ON - Hundredths")
        mc.mcSignalSetState(FNRJogLED, 1)
    elseif FNRStep100 == 1 then
        mc.mcMpgSetInc(inst, 0, .100)
        mc.mcCntlSetLastError(inst, "MPG JOG ON - Tenths")
        mc.mcSignalSetState(FNRJogLED, 1)
    else -- None of the jog enable switches are closed, so ensure jog is off
        mc.mcMpgSetInc(inst, 0, 0) -- Set jog increment to 0 to shut it off. FIXME: is this valid?
        mc.mcMpgSetAxis(inst, 0, -1) -- Set jog axis to -1 to shut off jogging.
        mc.mcCntlSetLastError(inst, "MPG JOG OFF")
        mc.mcSignalSetState(FNRJogLED, 0)
    end

    -- EStop
    if FNREStop == 1 then
        mc.mcCntlEStop(inst)
    end

   -- Cycle Start
   if FNRCycleStart == 1 then
        mc.mcCntlSetLastError(inst, "Console Cycle Start")
        mc.mcSignalSetState(FNRRunLED, 1)
        -- FIXME: Need command to run "Cycle Start"
        -- FIXME: LED control should go in main cycle start command so it works with screen controls too
   end

   -- Feed Hold
   if FNRCycleStart == 1 then
        mc.mcCntlSetLastError(inst, "Console Feed Hold")
        mc.mcSignalSetState(FNRRunLED, 0)
        mc.mcSignalSetState(FNRPauseLED, 1)
        -- FIXME: Need command to run "Feed Hold"
        -- FIXME: LED control should go in main cycle start / feed hold command so it works with screen controls too
   end

   -- Manual Spindle
   if FNRSpindleStart == 1 then
        -- Check current spindle setting, if 0, set to 1200, otherwise leave alone
        -- Check if spindle running, if no, then start. If yes, then stop
        -- FIXME: need code to do this
   end
end
Title: Re: Scripting Custom Control Panel
Post by: Brian Barker on February 01, 2021, 12:53:40 PM
Why don't you send who called the function and at least make it an ifelse set of commands. Option number 2 is to make a function for each operation. One more thing that may be really calling you issues is that you are getting 2 calls for every press. You have one with the state high and one with the state low (On press and one release). You tell me where we should start because I love what your doing! Panels and machines are like peas and carrots!
Title: Re: Scripting Custom Control Panel
Post by: RecceDG on February 01, 2021, 01:43:49 PM
Well, I dog-robbed this code from someone else on this forum who used it to run his pendant, so I figured it was a safe start point.

> One more thing that may be really calling you issues is that you are getting 2 calls for every press. You have one with the state high and one with the state low (On press and one release).

Hm. OK, that's a problem for any of the momentary pushbuttons (Cycle Start, Feed Hold, Manual Spindle).

So... those need a separate function for each button, called by the signal. If the signal is high, do something. If the signal is low, ignore it.

Something like:

Code: [Select]
--------------------------------------
-- FNR Control Console Code --
--------------------------------------
SigLib = {
    [mc.ISIG_INPUT10] = function (state) -- Cycle Start
        RunFNRCycleStart()
    end,
    [mc.ISIG_INPUT11] = function (state) -- Feed Hold
        RunFNRFeedHold()
    end,
    [mc.ISIG_INPUT12] = function (state) -- Manual Spindle Start
        RunFNRManualSpindle()
    end,
    [mc.ISIG_INPUT13] = function (state) -- EStop
        RunFNR()
    end,
    [mc.ISIG_INPUT14] = function (state) -- Jog On, 0.001
        RunFNR()
    end,
    [mc.ISIG_INPUT15] = function (state) -- Jog On, 0.01
        RunFNR()
    end,
    [mc.ISIG_INPUT16] = function (state) -- Jog On, 0.1
        RunFNR()
    end
}

---------------------------------------------------------------
-- FNR Control Console Cycle Start function.
---------------------------------------------------------------
function RunFNRCycleStart()

    local hSig, rc = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT10) -- Is mapped to Port X Pin X *Cycle Start
    local FNRCycleStart, rc = mc.mcSignalGetState(hSig)

    -- Cycle Start
   if FNRCycleStart == 1 then
        mc.mcCntlSetLastError(inst, "Console Cycle Start")
        mc.mcSignalSetState(FNRRunLED, 1)
        -- FIXME: Need command to run "Cycle Start"
        -- FIXME: LED control should go in main cycle start command so it works with screen controls too
   elsif FNRCyleStart == 0 then
        -- We get this case on button release
        -- IGNORE ME!
   end
 end

Etc.

What are the function calls to "click" on the screen cycle start, feed hold, and spindle start clockwise buttons?
Title: Re: Scripting Custom Control Panel
Post by: Brian Barker on February 01, 2021, 02:22:22 PM
your really close!

So it more like this please
Code: [Select]

[mc.ISIG_INPUT1] = function (state)
   if (state == 1) then   -- This is on press (Rising Edge)
        CycleStart()
   else
        mc.mcCntlFeedHold (0) -- This is on release (Falling Edge)
    end

end,

So if you simply look at the state when you call the function you can call it at the correct time. I think  your code will look more like this:

Code: [Select]
    [mc.ISIG_INPUT10] = function (state) -- Cycle Start
        if (state == 1) then   -- This is on press (Rising Edge)
              RunFNRCycleStart()
        end
    end,

Hope that helps !
Brian
Title: Re: Scripting Custom Control Panel
Post by: RecceDG on February 01, 2021, 02:37:06 PM
Well there's no real reason to call a separate function at that point - RunFNRCycleStart() should just be whatever function is executed by clicking on the "Cycle Start" UI element, and that function should have the LED management code added to it.

What is mc.mcCntlFeedHold (0) ?

Is that the built-in feed hold function?

Is there a document that details all the built-in functions?

Title: Re: Scripting Custom Control Panel
Post by: Brian Barker on February 01, 2021, 02:51:43 PM
Hey man you do you ,I am simply showing  you how to get the "state" so you can see on press and on release. The Cycle start and feedhold are out of the stock screen as an example. If you like your way better, you do you! I am only trying to show you how I do it. I don't even know what your "LED management code" is. I don't have anything to prove and if what you have is what you like, I like it.

Feedhold is the stock action at instance zero (see the docs)

Try the Mach4->docs, that is where we keep all the API docs (Mach4CoreAPI.chm)

Hope that helps, if not we will try again
Title: Re: Scripting Custom Control Panel
Post by: RecceDG on February 01, 2021, 03:02:31 PM
Dude, I'm not throwing rocks at you - I'm away from my Mach computer working through a problem by inference looking at other people's code.
Title: Re: Scripting Custom Control Panel
Post by: Brian Barker on February 01, 2021, 03:41:56 PM
I was thinking that you where thinking I was throwing! Sweet that's over, Anyway we will get this done...

I don't get the LED's, Where and why are you making them? We shouldn't need any script to see the state of feedhold and so on. Many ways to solve a problem just some of them will make you work more.

If you want maybe we should make some better code and show everyone how it is done ;) I can tell you I am 4th best at this!
Title: Re: Scripting Custom Control Panel
Post by: Cbyrdtopper on February 01, 2021, 04:26:36 PM
When I make control panels I make them as simple as possible in the Screen Script.  I make it easy on myself, I use the provided code that has been commented out and copy it to each of my inputs that I'm using for button presses. 
The Cycle Start and feedhold I use just like they are in the SigLib, I just change the input# to match what I have it mapped to.
No need to make a new function if the code is already being provided my Artsoft.  From your first post, it seems like the stock functions will do what you want them to do.
You will need to make a function to adjust your MPG handle to switch Axis, but that could be as simple as making a new function and calling the appropriate API calls to set the axis and the increments that you want.  Call those functions on the button press, just like the cylcestart and feedhold are called. 

I also made a keyboard toggle button to toggle the keyboard inputs enable/disable that way I can easily push a button and edit code or mess with CAM without having to use a mouseclick to turn off the keyboard inputs. 

Having an LED... I get the handle of the feedhold state and use that to set an output on or off based on it's state. 
To have an LED on when code is running, use the GCodeRunning output.  That's the easiest way to do that. 
That's how I utilize the stack light I put on machines.  I also run most things through a PLC, but it can all be done in Mach4.
Title: Re: Scripting Custom Control Panel
Post by: RecceDG on February 02, 2021, 08:46:59 AM
I don't get the LED's, Where and why are you making them?

The control panel I have built has 3 LEDs:

1. Program Running (GREEN)
2. Feed Hold Engaged (ORANGE)
3. Manual Jog Handle Active (BLUE)

At some point, I want to be able to blink the orange light in response to errors, and I want to blink the green light to prompt a tool change, but that is future expansion stuff at this point.

The intent is that the LEDs change state in response to either manual button presses on the control panel OR to virtual button presses in the Mach UI. That means that for Program Run and Feed Hold I have to flip the LED signal state accordingly in the main script, not in my own code like I can with the manual jog handle code.

So last night I dumped my current screen script to text and I found the API reference and the scripting manual. I'm going through that now.

I have discovered a few interesting things:

1. Some script functions are... puzzling. For example, clicking the "Cycle Start" UI button calls btnCycleStart_Left_Up_Script(), which only calls CycleStart(), which is a script that checks to see if we are running a loaded program or are in MDI mode (fair ball) before running the appropriate state. So why the wrapper function?

2. There doesn't seem to be much in the way of error checking.... I was raised to always capture and check the result code from any function call as a minimum, and have a hook to throw an error if something doesn't work. And if you want to be doubleplus sure that the call worked, if there is a "state checker" function, test the state after the return code to be sure it matches - or throw an error. There is next to none of this in these scripts. Is that by design?

3. There's a lot of stuff commented out, with no notes as to when, why, or by whom; and

4. There's a bunch of functions that appear to be NOOP - like Mach_Timer_Script() - with no comments as to why they exist.

I feel like there's room for cleanup here.
Title: Re: Scripting Custom Control Panel
Post by: Brian Barker on February 02, 2021, 09:45:29 AM
Making the LED's work will be simple...

Your comments on the screen are following your numbers:
1.) This was done so you can press the same button to trigger a cycle start OR you can trigger an MDI event from the same button. This was done to emulate how it is done on industrial machines (we automatically swap the state in the background in the state machine). If you want to change how it works  you can do that. That's sort of the point of all this, make it your way.
2.)Error checking... Yup agree and they should have checked many of the error returns. The guys that work on the screens are not programmers they are machinist. The reason for this is that my self and Steve simply don't have time to go play screen guys. We did have them audit the screens for error checking but looks like I may have to review it again. Mind you some of them are not needed...
3.)Commented out code.. Machinist ... They are are worried that if they take the code out that they will not be able to find it later for someone. The screens are all under source control so we will never loose anything! They should have put comments to what changed in the Repo but I don't read that stuff for fun.
4.)The NOOP's are because we are building a chunk in the background of all the buttons and calls that are in the screen. This is to give you one programming environment. if  you don't put stuff in the functions they just don't have anything.
No worry they are not called and will not slow the screen

I am in the middle of TRYING to get a  release out and I made them do some house keeping :)

Title: Re: Scripting Custom Control Panel
Post by: Bill_O on February 02, 2021, 10:52:40 AM
I made a pendant not a control panel but same thing for button.
Here is how I make my buttons work.
You can do a lot with these and how Lua/Mach4 function.

[mc.ISIG_INPUT0] = function (state)
    if (state == 1) then
      CycleStart()
   end
end,

[mc.ISIG_INPUT1] = function (state)
    if (state == 1) then
      mc.mcCntlRewindFile(inst)
   end
end,

[mc.ISIG_INPUT2] = function (state)
    if (state == 1) then
      CycleStop()
   end
end,

[mc.ISIG_INPUT3] = function (state)
   if (state == 1) then
      if (Pause == 1) then
         mc.mcCntlCycleStart(inst)
         Pause = 0
      elseif (Pause == 0) then
         mc.mcCntlFeedHold(inst)
         Pause = 1
      end
   end
end,

[mc.ISIG_INPUT4] = function (state)
      if (state == 0) then
         mc.mcCntlEnable(inst, true)
      else
         mc.mcCntlEnable(inst, false)
      end
end,

[mc.ISIG_INPUT20] = function (state)
    local AltRegH = mc.mcRegGetHandle(inst, 'iRegs0/Alt')
   if (state == 1) then
      mc.mcRegSetValue(AltRegH, 1)      
   else
      mc.mcRegSetValue(AltRegH, 0)   
   end
end,

[mc.ISIG_INPUT5] = function (state)
    local AltRegH = mc.mcRegGetHandle(inst, 'iRegs0/Alt')
   local AltVal = mc.mcRegGetValue(AltRegH)
   if (state == 1) then
      if (AltVal == 0) then
      GoToWorkZero()
      elseif (AltVal == 1) then
      SetWorkZero()
      end
   end
end,

[mc.ISIG_INPUT6] = function (state)
    local AltRegH = mc.mcRegGetHandle(inst, 'iRegs0/Alt')
   local AltVal = mc.mcRegGetValue(AltRegH)
   if (state == 1) then
      if (AltVal == 0) then
      ReturnToPosition()
      elseif (AltVal == 1) then
      RememberPosition()
      end
   end
end,

[mc.ISIG_INPUT7] = function (state)
    local AltRegH = mc.mcRegGetHandle(inst, 'iRegs0/Alt')
   local AltVal = mc.mcRegGetValue(AltRegH)
   if (state == 1) then
      if (AltVal == 1) then
         wait = coroutine.create (RefAllHome)
      end
   end
end,




[mc.ISIG_INPUT8] = function (state)
   local AltRegH = mc.mcRegGetHandle(inst, 'iRegs0/Alt')
   local AltVal = mc.mcRegGetValue(AltRegH)
      if (state == 1) then
      if (AltVal == 0) then
      Surface()
      elseif (AltVal == 1) then
      MaxDepth()
      end      
   end
end,

[mc.ISIG_INPUT11] = function (state)
      local AltRegH = mc.mcRegGetHandle(inst, 'iRegs0/Alt')
   local AltVal = mc.mcRegGetValue(AltRegH)
   if (state == 1) then
      if (AltVal == 0) then
      CutSpeedMinus()
      elseif (AltVal == 1) then
      CutSpeedPlus()
      end            
   end
end,

[mc.ISIG_INPUT12] = function (state)
   local AltRegH = mc.mcRegGetHandle(inst, 'iRegs0/Alt')
   local AltVal = mc.mcRegGetValue(AltRegH)   
      if (state == 1) then
      if (AltVal == 1) then
      ExtraDepth()
      end      
   end
end,

[mc.ISIG_INPUT15] = function (state)
      if (state == 1) then
      SpeedsReset()
   end
end,

[mc.ISIG_INPUT16] = function (state)
      local AltRegH = mc.mcRegGetHandle(inst, 'iRegs0/Alt')
   local AltVal = mc.mcRegGetValue(AltRegH)   
      if (state == 1) then
      if (AltVal == 0) then
      MistOnOff()
      elseif (AltVal == 1) then
      SpindleOnOff()
      end                  
   end
end,

[mc.ISIG_INPUT19] = function (state)
      local AltRegH = mc.mcRegGetHandle(inst, 'iRegs0/Alt')
   local AltVal = mc.mcRegGetValue(AltRegH)
   if (state == 1) then
      if (AltVal == 0) then
      JogSpeedMinus()
      elseif (AltVal == 1) then
      JogSpeedPlus()
      end            
   end
end,

[mc.ISIG_INPUT21] = function (state)
    local AltRegH = mc.mcRegGetHandle(inst, 'iRegs0/Alt')
   local AltVal = mc.mcRegGetValue(AltRegH)
   if (state == 1) then
      if (AltVal == 0) then
         local MachHmdreg = mc.mcRegGetHandle(inst, 'iRegs0/MachHmd')
         local MachHmd = mc.mcRegGetValue(MachHmdreg)
         if (MachHmd == 0) then
            wx.wxMessageBox("Must set Machine Home")
         elseif (MachHmd == 1) then
--            mc.mcCntlGcodeExecute(inst, "m6")
            mc.mcCntlMdiExecute(inst, 'm6')
         end
      elseif (AltVal == 1) then
         local hsig = mc.mcSignalGetHandle(inst, mc.OSIG_OUTPUT6)
         local sigOut6 = mc.mcSignalGetState(hsig)
         if (sigOut6 == 0) then
            mc.mcSignalSetState(hsig, 1)
         else
            mc.mcSignalSetState(hsig, 0)
         end
      end
   end
end,

[mc.ISIG_INPUT22] = function (state)
      local AltRegH = mc.mcRegGetHandle(inst, 'iRegs0/Alt')
   local AltVal = mc.mcRegGetValue(AltRegH)
   if (state == 1) then
      if (AltVal == 0) then
      
      elseif (AltVal == 1) then
      
      end            
   end
end,

[mc.ISIG_INPUT25] = function (state)
    if (state == 1) then
      local Tool = mc.mcToolGetSelected(inst)
      --get max tools
      local ATCMaxTlsReg = mc.mcRegGetHandle(inst, 'iRegs0/ATCMaxTools')
      local ATCMaxTlsVal = mc.mcRegGetValue(ATCMaxTlsReg)   
      --if tool is 100(cam), 101(tks) or 102(tko) set tool to 0
      if (Tool == 100) or (Tool == 101) or (Tool == 102) then
         Tool = 0
      end
      --if tool is in range add one to tool #
      if (Tool <= (ATCMaxTlsVal - 1)) then
         Tool = (Tool + 1)
         mc.mcCntlSetPoundVar(inst, mc.SV_CUR_SELECTED_TOOL, Tool)
      end
   end
end,

[mc.ISIG_INPUT26] = function (state)
   local AltRegH = mc.mcRegGetHandle(inst, 'iRegs0/Alt')
   local AltVal = mc.mcRegGetValue(AltRegH)   
      if (state == 1) then
      if (AltVal == 1) then
         local TKSReg = mc.mcRegGetHandle(inst, 'iRegs0/TKStdOnOff')
         local TKSRegVal = mc.mcRegGetValue(TKSReg)
         local TKOReg = mc.mcRegGetHandle(inst, 'iRegs0/TKOscOnOff')
         local TKORegVal = mc.mcRegGetValue(TKOReg)
         local TKSSigH = mc.mcSignalGetHandle(inst, mc.OSIG_OUTPUT7)
         local TKOSigH = mc.mcSignalGetHandle(inst, mc.OSIG_OUTPUT8)
         local TKOscSigH = mc.mcSignalGetHandle(inst, mc.OSIG_OUTPUT9)

         local ATCOnOffReg = mc.mcRegGetHandle(inst, 'iRegs0/ATCOnOff')
         local ATCOnOffVal = mc.mcRegGetValue(ATCOnOffReg)
         local ATCToolHReg = mc.mcRegGetHandle(inst, 'iRegs0/ATCToolHolder')

         local SMReg = mc.mcRegGetHandle(inst, 'iRegs0/SMOnOff')
         local SMRegVal = mc.mcRegGetValue(SMReg)   
         
         mc.mcRegSetValue(TKSReg, 0)
         mc.mcSignalSetState(TKSSigH, 0)
         mc.mcRegSetValue(TKOReg, 0)
         mc.mcSignalSetState(TKOSigH, 0)
         mc.mcSignalSetState(TKOscSigH, 0)
         
         --if atc on change current tool to tool holder tool
         if (ATCOnOffVal == 1) then
            local CurTool = mc.mcToolGetCurrent(inst)
            local ATCToolHVal = mc.mcRegGetValue(ATCToolHReg)
            if (CurTool == 100) or (CurTool == 101) or (CurTool == 102) then
               mc.mcToolSetCurrent(inst, ATCToolHVal)
               mc.mcCntlGcodeExecuteWait(inst, string.format("G43 H" .. tostring(ATCToolHVal)))
               mc.mcCntlSetPoundVar(inst, mc.SV_HEAD_SHIFT_X, 0.000)
               mc.mcCntlSetPoundVar(inst, mc.SV_HEAD_SHIFT_Y, 0.000)
            end
         end
         
         --turn off tang knife mode
         mc.mcCntlSetParameter(502, 0)
         mc.mcCntlSetLastError(inst, 'TK OFF')
         if (SMTempOn == 1) then
            mc.mcRegSetValue(SMReg, 1)
            SMTempOn = 0
         end
         
         --mc.mcRegSetValue(TKSReg, TKSRegVal)
         KeyTKSTemp = 0
         --mc.mcRegSetValue(TKOReg, TKORegVal)
         KeyTKOTemp = 0
      end      
   end
end,

[mc.ISIG_INPUT27] = function (state)
    local AltRegH = mc.mcRegGetHandle(inst, 'iRegs0/Alt')
   local AltVal = mc.mcRegGetValue(AltRegH)
   if (state == 1) then
      if (AltVal == 1) then
         KeyTKSTemp = 1
         TKSOnOff()
      end
   end
end,

[mc.ISIG_INPUT28] = function (state)
    local AltRegH = mc.mcRegGetHandle(inst, 'iRegs0/Alt')
   local AltVal = mc.mcRegGetValue(AltRegH)
   if (state == 1) then
      if (AltVal == 1) then
         KeyTKOTemp = 1
         TKOOnOff()
      end
   end
end,

[mc.ISIG_INPUT29] = function (state)
    if (state == 1) then
      local Tool = mc.mcToolGetSelected(inst)
      --get max tools
      local ATCMaxTlsReg = mc.mcRegGetHandle(inst, 'iRegs0/ATCMaxTools')
      local ATCMaxTlsVal = mc.mcRegGetValue(ATCMaxTlsReg)   
      --if tool is 100(cam), 101(tks) or 102(tko) set tool to MaxTool
      if (Tool == 100) or (Tool == 101) or (Tool == 102) then
         Tool = (ATCMaxTlsVal + 1)
      end
      --if tool is in range subtract one from tool #
      if (Tool >= 2) then
         Tool = (Tool - 1)
         mc.mcCntlSetPoundVar(inst, mc.SV_CUR_SELECTED_TOOL, Tool)
      end
   end
end,

[mc.ISIG_INPUT30] = function (state)
      local AltRegH = mc.mcRegGetHandle(inst, 'iRegs0/Alt')
   local AltVal = mc.mcRegGetValue(AltRegH)
   if (state == 1) then
      if (AltVal == 0) then
      
      elseif (AltVal == 1) then
         local hsig = mc.mcSignalGetHandle(inst, mc.OSIG_OUTPUT5)
         local sigOut5 = mc.mcSignalGetState(hsig)
         if (sigOut5 == 0) then
            mc.mcSignalSetState(hsig, 1)
         else
            mc.mcSignalSetState(hsig, 0)
         end
      end            
   end
end,

[mc.ISIG_INPUT31] = function (state)
      if (state == 1) then
      wait = coroutine.create (RefTKHome)
   end
end,

[mc.ISIG_INPUT32] = function (state)
    local AltRegH = mc.mcRegGetHandle(inst, 'iRegs0/Alt')
   local AltVal = mc.mcRegGetValue(AltRegH)
   local Sig9H = mc.mcSignalGetHandle(inst, mc.OSIG_OUTPUT9)
   local Sig9Val = mc.mcSignalGetState(Sig9H)
   if (state == 1) then
      if (AltVal == 1) then
         if (Sig9Val == 0) then
            mc.mcSignalSetState(Sig9H, 1)
            --wx.wxMilliSleep(1000)
         else
            mc.mcSignalSetState(Sig9H, 0)
            --wx.wxMilliSleep(1000)
         end
      end
   end
end,

Title: Re: Scripting Custom Control Panel
Post by: RecceDG on February 02, 2021, 10:58:00 AM
So I'm tracking why CycleStart() has the MDI check in there - and I agree with the reasoning. Got it.

But why is CycleStart() wrapped in btnCycleStart_Left_Up_Script() - which only calls CycleStart()?

Here is my first kick at a replacement function:

Code: [Select]

-----------------------------------------------------------------------
-- FNR_CycleStart
--
-- To be called by the Cycle Start UI button and/or the button on the FNR Console (INPUT 10)
-- If we are in GCode mode, run the loaded program
-- If we are in MDI mode, run the MDI code
-- If we are in Feed Hold state, release it and continue
-- LED aware for the FNR lathe control console
--
-- Version History
-- V0.5 DG Feb 2 2021 First attempt
--------------------------------------------------------------------------

function FNR_CycleStart()

   -- Are we resuming a Feed Hold?
   local Feedstate, rc = mc.mcCntlFeedHoldState(inst)
   if (rc == mc.MERROR_NOERROR) then
        if (Feedstate == 1) then
             local ec = mc.mcCntlCycleStart(inst)  -- Resume program
             if (ec == mc.MERROR_NOERROR) then
                  -- We can probably trust shutting off a light
                  mc.mcSignalSetState(FNRPauseLED, 0)
                  mc.mcSignalSetState(FNRRunLED, 1)
             else
                  if (ec == mc.MERROR_NOT_NOW) then
                       -- lights stay the same, but record the error
                       mc.mcCntlSetLastError(inst, "Could not resume from feed hold ERROR_NOT_NOW")
                  end
             else
                  -- Should never get here - needs standard error handling trap
                  -- Just complain for now
                  mc.mcCntlSetLastError(inst, "Invalid Instance in CycleStart()")
             end
        else
             -- Not resuming a feed hold
             -- Are we in MDI or Program mode?
             -- Next bit is copypasta from original CycleStart() TODO: Add error handling
             local tab, rc = scr.GetProperty("MainTabs", "Current Tab") -- will lua throw a fit if I redefine a variable like this?
             local tabG_Mdione, rc = scr.GetProperty("nbGCodeMDI1", "Current Tab")
             if ((tonumber(tab) == 0 and tonumber(tabG_Mdione) == 1)) then
                  local state = mc.mcCntlGetState(inst);
                  if (state == mc.MC_STATE_MRUN_MACROH) then
                       -- Program Mode (?)
                       mc.mcCntlCycleStart(inst);
                       -- Turn on the run light
                       mc.mcSignalSetState(FNRRunLED, 1)
                  else
                       if (tonumber(tab) == 0) then
                            -- MDI Mode (?) 
                            scr.ExecMdi('mdi1');
                            -- Turn on the run light
                            mc.mcSignalSetState(FNRRunLED, 1)
                        end
                  end
             else
                  -- For some reason, if we fall through the tab check, we execute a Cycle Start anyway
                  -- Maybe for tool changes?
                  -- If this is a tool change acknowledgement, we don't set the light
                  -- FIXME: Understand how we get here and do the right thing 
                  mc.mcCntlCycleStart(inst);
             end
         end
    else
          -- Should never get here - needs standard error handling trap
          -- Just complain for now
          mc.mcCntlSetLastError(inst, "Couldn't get feed state in CycleStart()")
    end

    -- Belt and suspenders - check if program running, and turn on light if it is
    -- Maybe this invalidates need to turn on light elsewhere?
    -- FIXME: Is this valid for MDI?
    local cycle, rc = mc.mcCntlyIsInCycle(inst)
    if ((rc == mc.MERROR_NOERROR) && (cycle == 1)) then
         mc.mcSignalSetState(FNRRunLED, 1)
    else
         if ((rc == mc.MERROR_NOERROR) && (cycle == 0)) then
              mc.mcSignalSetState(FNRRunLED, 0)
         else
              -- Should never get here - needs standard error handling trap
              -- Just complain for now
              mc.mcCntlSetLastError(inst, "Couldn't determine run state in CycleStart()")
         end
    end
end       
Title: Re: Scripting Custom Control Panel
Post by: Brian Barker on February 02, 2021, 11:03:28 AM
the reason it is wrapped is because in the screen the "btnCycleStart" button uses it's left up event. We mash that all up and make a btnCycleStart_Left_Up_Script() call that we trigger from C++. Lua is a glue that you get use to customize the actions.

Was that any help?
Title: Re: Scripting Custom Control Panel
Post by: RecceDG on February 02, 2021, 11:25:43 AM
So why not have the screen button call CycleStart() from its left button up event?

The way the Feed Hold screen button calls an uwrapped function (which I remember seeing last night, but I can't verify right now because I'm not near that machine).

What value does the extra wrapper provide, other than the name change?
Title: Re: Scripting Custom Control Panel
Post by: Brian Barker on February 02, 2021, 11:52:15 AM
The button is not drawn by Lua. The button is done in C++ and you don't "need" to do any scripting to make a machine work. We have to know the name of the event to call in Lua from C++ for each button that has some Lua code. Not really any other way to do that. The Cycle start code COULD be in the button's On Up script and work but it would not be as easy to call from an input (SignalLib() ). That is why that was done.

The feedhold is doing the API call for feedhold because no matter what your doing feedhold is valid. We don't have an MDI  feedhold. The reason to wrap it the functions is if  you would like to add some code to be done around it.  Your LED's can be controlled in the PMC by looking at the state of the machine.

This was no thrown together in an afternoon :) we have reasons the system is designed the way it is. I am more than happy to talk about it.  When you really dig in and see all that is going on it is impressive. It took 10 years to make this all happen with a target we where focused on!
Title: Re: Scripting Custom Control Panel
Post by: RecceDG on February 02, 2021, 12:12:29 PM
The button is not drawn by Lua. The button is done in C++ and you don't "need" to do any scripting to make a machine work. We have to know the name of the event to call in Lua from C++ for each button that has some Lua code. Not really any other way to do that.

Right - but is that name not editable in the screen editor? If I change the name of the function called on the On Up event in the screen editor to CycleStart(), will it not work?

(Mach isn't here so I can't check)

Quote
The Cycle start code COULD be in the button's On Up script and work but it would not be as easy to call from an input (SignalLib() ). That is why that was done.

Hold that thought

Quote
The feedhold is doing the API call for feedhold because no matter what your doing feedhold is valid. We don't have an MDI  feedhold.

Fair ball. Got it.

Quote
The reason to wrap it the functions is if  you would like to add some code to be done around it.

Right.

So what is stopping me from:

Code: [Select]

function FNR_FeedHold()
  mc.mcCtlFeedHold(inst)
  mc.mcSignalSetState(FNRPauseLED, 1)
end


...and changing the name of the function called by the button onUp event to FNR_FeedHold() in the screen editor?
 
Quote
Your LED's can be controlled in the PMC by looking at the state of the machine.

Why do it that way, rather than via the scripts? The scripting language is pretty straightforward. I mean, it isn't perl, but that's a high standard to meet.

Quote
This was no thrown together in an afternoon :)

Well, you've got production-class machines running on this stuff... perhaps it deserves some love.

Quote
When you really dig in and see all that is going on it is impressive. It took 10 years to make this all happen with a target we where focused on!

Nobody is questioning that.

As it happens, I have a coding background - I wrote the DaimlerChrysler external supplier distributed user management system and coded the LDAP replication process that harmonized two *very* different user database schemas in real time... which I suppose dates me... but anyway, I grok all this stuff. But any time you pick up someone else's code you have to go through a process to understand how their thought processes work when they wrote it, because the code is influenced by a ton of external factors, ranging from "programmer experience level and personality" to "weird undocumented quirks of the programming language, discovered the hard way when something entirely sensible on paper blew up in reality".

And at the end of the day, I want my control console to work.
Title: Re: Scripting Custom Control Panel
Post by: Brian Barker on February 02, 2021, 01:25:25 PM
Quote
Right - but is that name not editable in the screen editor? If I change the name of the function called on the On Up event in the screen editor to CycleStart(), will it not work?

You can't set the name of the function .... that is the button name and the function. It is how we find it in the C++ world...

Code: [Select]
function FNR_FeedHold()
  mc.mcCtlFeedHold(inst)
  mc.mcSignalSetState(FNRPauseLED, 1)
end
Quote
...and changing the name of the function called by the button onUp event to FNR_FeedHold() in the screen editor?
You can do that, no problem.

If you want to do it in script that is cool ... it is your machine you can do it anyway you like :)

Also we have other events (signals) ! Here is al list I got from the header file
Code: [Select]
OSIG_MISC_START  (OSIG_OUTPUT_FINISH + 1) 
#define OSIG_RUNNING_GCODE   (OSIG_MISC_START + 0)
#define OSIG_FEEDHOLD        (OSIG_MISC_START + 1)
#define OSIG_BLOCK_DELETE    (OSIG_MISC_START + 2)
#define OSIG_SINGLE_BLOCK    (OSIG_MISC_START + 3)
#define OSIG_REVERSE_RUN     (OSIG_MISC_START + 4)
#define OSIG_OPT_STOP        (OSIG_MISC_START + 5)
#define OSIG_MACHINE_ENABLED (OSIG_MISC_START + 6)
#define OSIG_TOOL_CHANGE     (OSIG_MISC_START + 7)
#define OSIG_DIST_TOGO       (OSIG_MISC_START + 8)
#define OSIG_MACHINE_CORD    (OSIG_MISC_START + 9)
#define OSIG_SOFTLIMITS_ON   (OSIG_MISC_START + 10)
#define OSIG_JOG_INC         (OSIG_MISC_START + 11)
#define OSIG_JOG_CONT        (OSIG_MISC_START + 12)
#define OSIG_JOG_ENABLED     (OSIG_MISC_START + 13)
#define OSIG_JOG_MPG         (OSIG_MISC_START + 14)
#define OSIG_HOMED_X         (OSIG_MISC_START + 15)
#define OSIG_HOMED_Y         (OSIG_MISC_START + 16)
#define OSIG_HOMED_Z         (OSIG_MISC_START + 17)
#define OSIG_HOMED_A         (OSIG_MISC_START + 18)
#define OSIG_HOMED_B         (OSIG_MISC_START + 19)
#define OSIG_HOMED_C         (OSIG_MISC_START + 20)
#define OSIG_DWELL           (OSIG_MISC_START + 21)
#define OSIG_TP_MOUSE_DN (OSIG_MISC_START + 22)
#define OSIG_LIMITOVER       (OSIG_MISC_START + 23)
#define OSIG_CHARGE          (OSIG_MISC_START + 24)
#define OSIG_CHARGE2         (OSIG_MISC_START + 25)
#define OSIG_CURRENTHILOW    (OSIG_MISC_START + 26)
#define OSIG_SPINDLEON       (OSIG_MISC_START + 27)
#define OSIG_SPINDLEFWD      (OSIG_MISC_START + 28)
#define OSIG_SPINDLEREV      (OSIG_MISC_START + 29)
#define OSIG_COOLANTON       (OSIG_MISC_START + 30)
#define OSIG_MISTON          (OSIG_MISC_START + 31)
#define OSIG_DIGTRIGGER      (OSIG_MISC_START + 32)
#define OSIG_ALARM           (OSIG_MISC_START + 33)
#define OSIG_PRTSF           (OSIG_MISC_START + 34)
#define OSIG_TLCHB           (OSIG_MISC_START + 35) // Tool Change B
#define OSIG_WATO            (OSIG_MISC_START + 36) // Path wating signal
#define OSIG_RETRACT         (OSIG_MISC_START + 37) // Retract Signal
#define OSIG_MISC_FINISH     (OSIG_RETRACT)

The guys are going through the screens for the release and I jammed my head in to see how they looked ... Much better! I have been telling them NOT to comment out the code and keep it in the screens! But you know how that goes... you can only push mud up a hill for so long before you want to go get your own work done. I am currently trying to get the mouse wheel with a low level hook to windows. I can't debug it because it kills my hook as soon as it see's that I stopped the mouse messages ... So I am writing debug tools to do that. Frankly chatting with you has been a nice distraction.

Hope all the output signals helps, you should be able to use that to fire your LED's 
Thanks
Brian