Home AMX User Forum AMX Technical Discussion

Which array triggered button? (How do you do your grids/matrixes?)

say you have a couple arrays of button numbers and you stack them on a button event, how can you figure out which array the button is in?

[code]
DEFINE_CONSTANT
btns1[] = [1,16,99]
btns2[] = [13,2,5]

BUTTON_EVENT[tp,btns1]
BUTTON_EVENT[tp,btns2]
{
PUSH:{
//was this btns1 or btns2?
}
}
[code]

if the buttons are in some kind of order, you can do math to figure it out, but is there any trick for GET_LAST or something like that?
This must come up all the time for grids of buttons, like a matrix switcher.
I did it with a bunch of math, but it was pain. At least now I have an include of a bunch of Grid functions.

I'm coming back to it a few months later, thinking there must be an easy way. But when I started, it turned out to be just as much work.

Comments

  • Joe HebertJoe Hebert Posts: 2,159
    travis wrote:
    say you have a couple arrays of button numbers and you stack them on a button event, how can you figure out which array the button is in?
    I don’t know if you can figure it out. I surprised something like that compiles. I wouldn’t approach it that way because it seems illogical, at least to me. What advantage do you have with stacking those arrays? How about just 1 button event with 1 array?

    Here is one generic way to tell what row and column was pushed inside an array of grid buttons and it’s easily configurable.
    DEFINE_DEVICE
    
    dvTp = 10001:1:0
    
    DEFINE_CONSTANT
    
    INTEGER nGridButtons[] = {
    
       11,12,13,14,15,	// row 1 columns 1-5
       21,22,23,24,25, 	// row 2 columns 1-5
       31,32,33,34,35 	// row 3 columns 1-5
    
    }
    
    INTEGER nNumberOfRows		= 3
    INTEGER nNumberOfColumns 	= 5
    
    DEFINE_EVENT
    
    BUTTON_EVENT[dvTP,nGridButtons] {
    
       PUSH: {
       
          INTEGER index
          INTEGER row
          INTEGER column
          
          index = GET_LAST(nGridButtons)
          
          row = index / nNumberOfColumns
          IF (index % nNumberOfColumns) {row++}
          
          IF (index % nNumberOfColumns = 0) {column = nNumberOfColumns}
          ELSE {column = (index % nNumberOfColumns)}
          
          SEND_STRING 0,"'BUTTON.INPUT.CHANNEL = ',ITOA(BUTTON.INPUT.CHANNEL)"
          SEND_STRING 0,"'Index = ',ITOA(index)"
          SEND_STRING 0,"'Row = ',ITOA(row)"
          SEND_STRING 0,"'Column = ',ITOA(column)"
          
          
       }
    
    }
    

    I’ve seen a better way of doing this calculation but I can’t remember where or how:
          row = index / nNumberOfColumns
          IF (index % nNumberOfColumns) {row++}
    

    If anyone knows please post it. 
  • viningvining Posts: 4,368
    You could try
    BUTTON_EVENT[tp,0]
    {
    PUSH:{
        SELECT
          {
          ACTIVE(get_last(btns1)):
              {
               //
              }
          ACTIVE(get_last(btns2)):
              {
               //
              }
         }
    
  • Joe HebertJoe Hebert Posts: 2,159
    vining wrote: »
    You could try
    BUTTON_EVENT[tp,0]
    {
    PUSH:{
        SELECT
          {
          ACTIVE(get_last(btns1)):
              {
               //
              }
          ACTIVE(get_last(btns2)):
              {
               //
              }
         }
    

    I don't think that will work.
  • travistravis Posts: 180
    Joe Hebert wrote: »
    I don’t know if you can figure it out. I surprised something like that compiles. I wouldn’t approach it that way because it seems illogical, at least to me. What advantage do you have with stacking those arrays? How about just 1 button event with 1 array?

    Here is one generic way to tell what row and column was pushed inside an array of grid buttons and it’s easily configurable.
    DEFINE_DEVICE
    
    dvTp = 10001:1:0
    
    DEFINE_CONSTANT
    
    INTEGER nGridButtons[] = {
    
       11,12,13,14,15,	// row 1 columns 1-5
       21,22,23,24,25, 	// row 2 columns 1-5
       31,32,33,34,35 	// row 3 columns 1-5
    
    }
    
    INTEGER nNumberOfRows		= 3
    INTEGER nNumberOfColumns 	= 5
    
    DEFINE_EVENT
    
    BUTTON_EVENT[dvTP,nGridButtons] {
    
       PUSH: {
       
          INTEGER index
          INTEGER row
          INTEGER column
          
          index = GET_LAST(nGridButtons)
          
          row = index / nNumberOfColumns
          IF (index % nNumberOfColumns) {row++}
          
          IF (index % nNumberOfColumns = 0) {column = nNumberOfColumns}
          ELSE {column = (index % nNumberOfColumns)}
          
          SEND_STRING 0,"'BUTTON.INPUT.CHANNEL = ',ITOA(BUTTON.INPUT.CHANNEL)"
          SEND_STRING 0,"'Index = ',ITOA(index)"
          SEND_STRING 0,"'Row = ',ITOA(row)"
          SEND_STRING 0,"'Column = ',ITOA(column)"
          
          
       }
    
    }
    

    I’ve seen a better way of doing this calculation but I can’t remember where or how:
          row = index / nNumberOfColumns
          IF (index % nNumberOfColumns) {row++}
    

    If anyone knows please post it. 
    That's the approach I took.


    Took me way too long, but here are my functions with no IFs
    DEFINE_FUNCTION INTEGER F_BTN_IS_IN_ROW(INTEGER _btn, INTEGER _numRows){
        RETURN (_btn - 1) % (_numRows) + 1
    }
    DEFINE_FUNCTION INTEGER F_BTN_IS_IN_COL(INTEGER _btn, INTEGER _numRows){
        RETURN (_btn - 1 ) / (_numRows) + 1
    }
    


    These are for the other way to align the grid (sometimes you aren't the one who numbers things):

    {
    1,4,7,
    2,5,8,
    3,6,9
    }
  • Joe HebertJoe Hebert Posts: 2,159
    travis wrote: »

    These are for the other way to align the grid (sometimes you aren't the one who numbers things):

    {
    1,4,7,
    2,5,8,
    3,6,9
    }

    If you are referring to the code I posted, it doesn't matter what the button numbers are. Button numbers themselves don't mean a thing in this case, they're just used as placeholders. It will work with whatever button numbers, whatever amount of rows, and whatever amount of columns. Aside from the constants, the code doesn't need to change.
  • Joe HebertJoe Hebert Posts: 2,159
    travis wrote: »
    Took me way too long, but here are my functions with no IFs
    DEFINE_FUNCTION INTEGER F_BTN_IS_IN_ROW(INTEGER _btn, INTEGER _numRows){
        RETURN (_btn - 1) % (_numRows) + 1
    }
    DEFINE_FUNCTION INTEGER F_BTN_IS_IN_COL(INTEGER _btn, INTEGER _numRows){
        RETURN (_btn - 1 ) / (_numRows) + 1
    }
    

    I don't think those functions will work. Try pushing button 15 (button index 5) and it should be row 1 column 5. The functions above will return row 2 column 2.
  • a_riot42a_riot42 Posts: 1,624
    I don't think I would bother with grids. You don't necessarily have to model the code to the device's characteristics. In your example, I would just have two button events rather than stacking them. Then you always know which channel number was from which array. I will store data in a 2 dimensional array (grid) but since they don't work in button events I'll just use one array for button presses. I very rarely stack button events, since that design has inherent problems as you have discovered.
    Paul
  • viningvining Posts: 4,368
    Joe Hebert wrote: »
    I don't think that will work.
    Yeah, I tested this morning and it does wierd crap that I can't even explain.

    Even trying it like this so that the arrays are built into the event table works in the same bizarre manner.
    DEFINE_EVENT 	//BUTTON EVENT TP_ANI_ARRAY
    
    BUTTON_EVENT[vTest,nBtns1]
    BUTTON_EVENT[vTest,nBtns2]
         
         {
         PUSH:
    	  {
    	  SELECT
    	       {
    	       ACTIVE(get_last(nBtns1))://{1,16,99}
    		    {
    		    SEND_STRING 0,"'TEST btns1 BTN-[ ',itoa(get_last(nBtns1)),' ] :DEBUG<',itoa(__LINE__),'>'";
    		    }
    	       ACTIVE(get_last(nBtns2))://{13,2,5};
    		    {
    		    SEND_STRING 0,"'TEST btns2 BTN-[ ',itoa(get_last(nBtns2)),' ] :DEBUG<',itoa(__LINE__),'>'";
    		    }
    	       ACTIVE(1):
    		    {
    		    SEND_STRING 0,"'TEST btns(1)  BTN-[ ',itoa(BUTTON.INPUT.CHANNEL),' ] :DEBUG<',itoa(__LINE__),'>'";
    		    }
    	       }
    	  }
         }
    
  • ericmedleyericmedley Posts: 4,177
    Not that I would ever approach this issue the way the OP did in the first place... But I think I would just do two button events with the different arrays and call it "done"
  • JasonSJasonS Posts: 229
    Straight out of my 2010 Programmer re-Certification Practical exam:
    INTEGER cSw_Btns[]		= {11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 43, 44, 51, 52, 53, 54, 
    				    61, 62, 63, 64, 71, 72, 73, 74, 81, 82, 83, 84, 91, 92, 93, 94, 101, 102, 103, 104,
    				    111, 112, 113, 114, 121, 122, 123, 124}
    
    BUTTON_EVENT [tpSwitch, cSw_Btns]
    {
        PUSH:
        {
            STACK_VAR INTEGER Input
    	STACK_VAR INTEGER Output
    	    
    	Input = Button.Input.Channel / 10
    	Output = Button.Input.Channel % 10
    	SEND_STRING dvSwitch, "ITOA(Input), '*', ITOA(Output), 'S'"
        }
    }
    
    This is not limited to numbers below 10, just change your divisor. It is much easier to understand in increments of 10 however. Does require consecutive button numbers, but that is no big deal IMO. Why write two button events and use a variable to store the value of the first one triggered when you can do it all in one event? On the other hand I don't really find occasion to use a grid of buttons very often.
  • travistravis Posts: 180
    ah, those functions i posted are for a little different scenario...

    DEFINE_CONSTANT
    
    INTEGER OFFSET = 13
    INTEGER NUM_ROWS = 10
    INTEGER nGrid3[] =
    {
        14,24,34,44,54,64,74,84,94,104,114,124
        ,15,25,35,45,55,65,75,85,95,105,115,125
        ,16,26,36,46,56,66,76,86,96,106,116,126
        ,17,27,37,47,57,67,77,87,97,107,117,127
        ,18,28,38,48,58,68,78,88,98,108,118,128
        ,19,29,39,49,59,69,79,89,99,109,119,129
        ,20,30,40,50,60,70,80,90,100,110,120,130
        ,21,31,41,51,61,71,81,91,101,111,121,131
        ,22,32,42,52,62,72,82,92,102,112,122,132
        ,23,33,43,53,63,73,83,93,103,113,123,133
    }
    
    
    DEFINE_EVENT
    
    BUTTON_EVENT[dv_TPs, nGrid3]
    {
        PUSH:{
    	LOCAL_VAR INTEGER chan
    	LOCAL_VAR INTEGER ROW3
    	LOCAL_VAR INTEGER COLUMN3
    	
    	//GL3 = GET_LAST(nGrid3)
    	chan = BUTTON.INPUT.CHANNEL
    	
    	ROW3 = F_BTN_IS_IN_ROW(chan-offset, NUM_ROWS)
    	COLUMN3 = F_BTN_IS_IN_COL(chan-offset, NUM_ROWS)
    	
    	SEND_STRING 0, "'BTN-', itoa(BUTTON.INPUT.CHANNEL), ' ROW-', ITOA(row3), ' COL-', ITOA(column3)"
        }
    }
    
    
    Line      1 (11:52:02.850)::  BTN-23 ROW-10 COL-1
    Line      2 (11:52:10.031)::  BTN-133 ROW-10 COL-12
    Line      3 (11:52:33.601)::  BTN-124 ROW-1 COL-12
    Line      4 (11:52:40.312)::  BTN-14 ROW-1 COL-1
    Line      5 (11:52:59.195)::  BTN-69 ROW-6 COL-6
    

    I'm going to write another set to work with GET_LAST...
  • travistravis Posts: 180
    PROGRAM_NAME='test'
    include'config'
    //include'incGrid'
    
    DEFINE_CONSTANT
    
    INTEGER OFFSET = 13
    INTEGER NUM_ROWS = 10
    INTEGER NUM_COLS = 12
    INTEGER nGrid3[] =
    {
        14,24,34,44,54,64,74,84,94,104,114,124
        ,15,25,35,45,55,65,75,85,95,105,115,125
        ,16,26,36,46,56,66,76,86,96,106,116,126
        ,17,27,37,47,57,67,77,87,97,107,117,127
        ,18,28,38,48,58,68,78,88,98,108,118,128
        ,19,29,39,49,59,69,79,89,99,109,119,129
        ,20,30,40,50,60,70,80,90,100,110,120,130
        ,21,31,41,51,61,71,81,91,101,111,121,131
        ,22,32,42,52,62,72,82,92,102,112,122,132
        ,23,33,43,53,63,73,83,93,103,113,123,133
    }
    
    DEFINE_FUNCTION INTEGER F_BTN_IS_IN_ROW(INTEGER _btn, INTEGER _numRows){
        RETURN (_btn - 1) % (_numRows) + 1
    }
    DEFINE_FUNCTION INTEGER F_BTN_IS_IN_COL(INTEGER _btn, INTEGER _numRows){
        RETURN (_btn - 1 ) / (_numRows) + 1
    }
    
    DEFINE_FUNCTION INTEGER F_IDX_IS_IN_ROW(INTEGER _btn, INTEGER _numCols){
        RETURN (_btn - 1) / (_numCols) + 1
    }
    DEFINE_FUNCTION INTEGER F_IDX_IS_IN_COL(INTEGER _btn, INTEGER _numCols){
        RETURN (_btn - 1 ) % (_numCols) + 1
    }
    
    DEFINE_EVENT
    
    BUTTON_EVENT[dv_TPs, nGrid3]
    {
        PUSH:{
    	LOCAL_VAR INTEGER chan
    	LOCAL_VAR INTEGER ROW3
    	LOCAL_VAR INTEGER COL3
    	LOCAL_VAR INTEGER COLUMN3
    	LOCAL_VAR INTEGER GL3
    	
    	GL3 = GET_LAST(nGrid3)
    	chan = BUTTON.INPUT.CHANNEL
    	
    	ROW3 = F_BTN_IS_IN_ROW(chan-offset, NUM_ROWS)
    	COLUMN3 = F_BTN_IS_IN_COL(chan-offset, NUM_ROWS)
    	
    	SEND_STRING 0, "'BTN-', itoa(BUTTON.INPUT.CHANNEL), ' ROW-', ITOA(row3), ' COL-', ITOA(column3)"
    	
    	ROW3 = F_IDX_IS_IN_ROW(GL3,NUM_COLS)
    	COL3 = F_IDX_IS_IN_COL(GL3,NUM_COLS)
    	
    	SEND_STRING 0, "'BTN-', itoa(BUTTON.INPUT.CHANNEL), ' ROW-', ITOA(row3), ' COL-', ITOA(column3)"
        }
    }
    
    
    
    Line      1 (12:17:33.416)::  BTN-14 ROW-1 COL-1
    Line      2 (12:17:33.417)::  BTN-14 ROW-1 COL-1
    Line      3 (12:17:37.341)::  BTN-124 ROW-1 COL-12
    Line      4 (12:17:37.341)::  BTN-124 ROW-1 COL-12
    Line      5 (12:17:41.318)::  BTN-133 ROW-10 COL-12
    Line      6 (12:17:41.319)::  BTN-133 ROW-10 COL-12
    Line      7 (12:17:44.641)::  BTN-23 ROW-10 COL-1
    Line      8 (12:17:44.643)::  BTN-23 ROW-10 COL-1
    
    
  • JasonS wrote: »
    Straight out of my 2010 Programmer re-Certification Practical exam:
    INTEGER cSw_Btns[]		= {11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 43, 44, 51, 52, 53, 54, 
    				    61, 62, 63, 64, 71, 72, 73, 74, 81, 82, 83, 84, 91, 92, 93, 94, 101, 102, 103, 104,
    				    111, 112, 113, 114, 121, 122, 123, 124}
    
    BUTTON_EVENT [tpSwitch, cSw_Btns]
    {
        PUSH:
        {
            STACK_VAR INTEGER Input
    	STACK_VAR INTEGER Output
    	    
    	Input = Button.Input.Channel / 10
    	Output = Button.Input.Channel % 10
    	SEND_STRING dvSwitch, "ITOA(Input), '*', ITOA(Output), 'S'"
        }
    }
    
    This is not limited to numbers below 10, just change your divisor. It is much easier to understand in increments of 10 however. Does require consecutive button numbers, but that is no big deal IMO. Why write two button events and use a variable to store the value of the first one triggered when you can do it all in one event? On the other hand I don't really find occasion to use a grid of buttons very often.

    How did you work the feedback on this problem?
  • JasonSJasonS Posts: 229
    Call the Switch_Feedback() function from wherever you like to trigger feedback from.
    DEFINE CONSTANT
    
        INTEGER NumSwOuts = 4
    
    DEFINE_VARIABLE
    
        VOLATILE INTEGER Sw_State[NumSwOuts]
    
    DEFINE_FUNCTION INTEGER FIND_Sw_Response(CHAR Buffer[], CHAR Response[])
    {
        STACK_VAR LONG Len
        STACK_VAR LONG Count
        
        Len = LENGTH_ARRAY(Buffer)
        FOR (Count = 1; Count <= Len; Count++)
        {
    	IF (Buffer[Count] == 'S' OR Buffer[Count] == 'X')
    	{
    	    Response = LEFT_STRING(Buffer, Count)
    	    REMOVE_STRING(Buffer, Response, 1)
    	    RETURN TRUE
    	}
        }
        RETURN FALSE
    }
    
    DEFINE_FUNCTION Switch_Feedback()
    {
        STACK_VAR INTEGER Len
        STACK_VAR INTEGER Count
        STACK_VAR INTEGER Input
        STACK_VAR INTEGER Output
        
        Len = MAX_LENGTH_ARRAY(cSw_Btns)
        FOR (Count = 1; Count <= Len; Count++)
        {
    	Input = cSw_Btns[Count] / 10
    	Output = cSw_Btns[Count] % 10
    	[tpSwitch, cSw_Btns[Count]] = Sw_State[Output] == Input
        }
    }
    
    DEFINE_EVENT
    
        DATA_EVENT [dvSwitch]
        {
    	ONLINE:
    	{
    	    SEND_COMMAND dvSwitch, 'SET BAUD 9600,N,8,1 485 DISABLE'
    	    SEND_COMMAND dvSwitch, 'HSOFF'
    	}
    	STRING:
    	{
    	    STACK_VAR CHAR Response[32]
    	    STACK_VAR INTEGER Output
    	    
    	    WHILE (FIND_Sw_Response(Sw_RxBuffer, Response))
    	    {
    		SELECT
    		{
    		    ACTIVE (RIGHT_STRING(Response, 1) == 'S'):
    		    {
    			Output = ATOI(REMOVE_STRING(Response, 'IN', 1))
    			IF (Output > 0 AND Output <= NumSwOuts)
    			{
    			    Sw_State[Output] = ATOI(Response)
    			}
    		    }
    		}
    	    }
    	}
        }
    
  • cmasoncmason Posts: 123
    travis wrote: »
    say you have a couple arrays of button numbers and you stack them on a button event, how can you figure out which array the button is in?
    DEFINE_CONSTANT
    btns1[] = [1,16,99]
    btns2[] = [13,2,5]
    
    BUTTON_EVENT[tp,btns1]
    BUTTON_EVENT[tp,btns2]
    {
    PUSH:{
    //was this btns1 or btns2?
    }
    }
    [code]
    
    if the buttons are in some kind of order, you can do math to figure it out, but is there any trick for GET_LAST or something like that?
    This must come up all the time for grids of buttons, like a matrix switcher. 
    I did it with a bunch of math, but it was pain. At least now I have an include of a  bunch of Grid functions. 
    
    I'm coming back to it a few months later, thinking there must be an easy way. But when I started, it turned out to be just as much work.[/QUOTE]
     
    This is easy, and I stack EVENTS when I can. It makes sense when you have similar functions, although there are other (cleaner) ways to do it.
    
    Here is my solution:
    
    [Code]
    DEFINE_CONSTANT
    btns1[] = [1,16,99]
    btns2[] = [13,2,5]
    
    BUTTON_EVENT[tp,btns1]
    BUTTON_EVENT[tp,btns2]
    {
        PUSH:
        {
    	STACK_VAR INTEGER intButton
    	STACK_VAR INTEGER intLoop
    	STACK_VAR INTEGER intButtonArray
    	
    	intButton = BUTTON.INPUT.CHANNEL
    	FOR(intLoop = 1; intLoop <= LENGTH_ARRAY(btns1); intLoop++)
    	{
    	    IF(intButton == btns1[intLoop])
    	    {
    		intButtonArray = 1
    		BREAK
    	    }
    	    ELSE IF(intButton == btns2[intLoop])
    	    {
    		intButtonArray = 2
    		BREAK
    	    }
    	}
    	SEND_STRING 0, "'Button number ',ITOA(intButton), 'from "btns',ITOA(intButtonArray), '" array was pushed!'"
        }
    }
    
  • viningvining Posts: 4,368
    I would scrub the loop and just use two get_last.

    nBtn1 = get_last (btns1);
    nBtn2 = get_last (btns2);

    if theres a match one var will be 0 and the other will hold the index value. Then if/else if
  • cmasoncmason Posts: 123
    vining wrote: »
    I would scrub the loop and just use two get_last.

    nBtn1 = get_last (btns1);
    nBtn2 = get_last (btns2);

    if theres a match one var will be 0 and the other will hold the index value. Then if/else if

    I stopped using the GET_LAST function a year or so ago, when I was getting inconsistent results from it with the code I was writing at that time. Of course I don't recall the exact issue I was a having, but I stopped trusting some innate Netlinx functions because they sometimes didn't work as expected, and became very frustrating to track down.

    There was another one that was giving me fits but I can't remember it at the moment.

    I suppose maybe the issues I encountered with those functions have since been resolved.

    Given a consistent result, I agree your recommendation would be simpler.
Sign In or Register to comment.