Home AMX User Forum NetLinx Studio

Button Array and Stack_Var

I'm working on the Programmer 2 practical exam. (Is this cheating to ask for help?)
I have a switcher with 4 outputs and 12 inputs and you are supposed to use BUTTON_EVENT stacking or channel array. This is what I came up with, but not sure how to specify the input and output.

DEFINE_DEVICE
dvSwitch = 5001:1:0

dvTP = 10001:1:0
dvTP_Switch = 10001:15:0

DEFINE_VARIABLE

Volatile integer nSWITOut
Volatile integer nSWITIn
Volatile integer nSWITBtn [] =
{
11,12,13,14,
21,22,23,24,
31,31,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,102,104,
121,122,123,124
}

DEFINE_EVENTS

BUTTON_EVENT [dvTP_Switch,nSWITBtn]
{
PUSH:
{
STACK_VAR Integer nSWTBtn
nSWTBtn = GET_LAST
nSWITIn = (nSWITBtn Left) //switcher input
nSWITOut = (nSWITBtn Right) //switcher output

SEND_STRING dvSwitch,??nSWITIn*nSWITOutS??
}
}

For instance, button 52 is input 5 and output 2. I was thinking I can do something like nSWITIn = (nSWITBtn / 10). 52 / 10 = 5 Input.

Comments

  • Joe HebertJoe Hebert Posts: 2,159
    Nope, it's not cheating to ask for help. The important thing is that you learn.

    I don't know all the specifics of your assignment so this advice might not pertain but here goes. I assume only 1 input can be selected at a time and I assume 1 or more outputs can be selected. Lastly I assume there is Take button to activate the switch which will route the currently selected input to whatever outputs are selected.

    So...

    1) Create an array of input buttons. Use GET_LAST to track which input is selected and assign that input number to a variable and use that variable for TP button feedback. Only 1 input can be selected at a time so the inputs are mutually exclusive.

    2) Create an array of output buttons. Create an INTEGER array to track which outputs are selected. Use GET_LAST to track which output was pressed and toggle between selected/deselected and store that value in the corresponding slot in the array. Any 1 input can be routed to 1 or more outputs and your output array will tell you which outputs have been selected. Use the output array for TP button feedback.

    3) When the TAKE button is pressed create the string that needs to be sent to the switcher. The input is stored in the input variable and you can loop through the output array to determine which outputs have been selected for routing.

    Hope this helps. Have fun.
  • HARMAN_ChrisHARMAN_Chris Posts: 597
    TUTech wrote: »
    I'm working on the Programmer 2 practical exam. (Is this cheating to ask for help?)
    Not at all - this is a great place to consult with experienced professionals who have solved these challenges before you and often share wisdom that cannot be gained outside of years of experience.
    TUTech wrote: »
    For instance, button 52 is input 5 and output 2. I was thinking I can do something like nSWITIn = (nSWITBtn / 10). 52 / 10 = 5 Input.
    I would discourage the use of math formulas to solve this challenge. Formulas like this, and the use of integer offsets make sense in the moment but are typically confusing years later when you attempt to service the project. Additionally, tactics like this do not always make sense (unless properly commented or named) to other programmers who may be assisting in the future. Like Joe suggested above, integer array's and GET_LAST will prove to be the most scalable way to code this task.

  • TonyAngeloTonyAngelo Posts: 315
    AMX_Chris wrote: »
    Not at all - this is a great place to consult with experienced professionals who have solved these challenges before you and often share wisdom that cannot be gained outside of years of experience.


    I would discourage the use of math formulas to solve this challenge. Formulas like this, and the use of integer offsets make sense in the moment but are typically confusing years later when you attempt to service the project. Additionally, tactics like this do not always make sense (unless properly commented or named) to other programmers who may be assisting in the future. Like Joe suggested above, integer array's and GET_LAST will prove to be the most scalable way to code this task.

    Not to be contrarian (who am I kidding), but if we're concerned with real world situations like how future programmers will deal with some code, then why have him code a matrix switch like this? I've never once, in over ten years, provided a matrix switch GUI. The matrix switch stuff always just happens behind the scenes and the client never sees it.

    That said, if I did have to code a matrix switch, I would almost certainly go the direction the op has, and do some *gasp* Math *gasp*.

    Seriously, if that math (divide by 10!) is too hard to figure out for some random future programmer they probably shouldn't be a programmer.

    Also, basic math operations can make certain programming tasks way easier to accomplish, so why discourage people from doing it?
  • HARMAN_ChrisHARMAN_Chris Posts: 597
    Tony,
    The power of formulas and equations cannot be denied. My answer is specifically related to task being accomplished in this scenario. There are certainly many different approaches that will result in a working outcome for the user. I am advocating the use of arrays and get_last as a best practice given the broader applications for the approach within netlinx coding. Additionally, I specifically mentioned that properly commented code would solve the challenges associated with another programmer hopping into the job at a future date. The matrix switch scenario is relevant, but we are also looking at it from a more abstract point of view than how frequent this particular customer requirement may be. This scenario touches on button events, button feedback, and video switch behavior that requires a take command before executing. As we build exams out, we are seeking to encourage the use of best practices that a coder will find helpful as they progress in their learning. Readability and scalabilty are two very important practices we are focusing on more going forward. Readability is important for any coder, but even more so in multi-coder environments. Again, there are many ways to solve this particular issue. I have a preference based on my experiences, and your experiences and perspective may differ. Ultimately, the objective is to deliver a working system that meets the end user's goals and workflow requirements - regardless of the approach used under the hood.
  • ericmedleyericmedley Posts: 4,177
    TonyAngelo wrote: »

    Not to be contrarian (who am I kidding), but if we're concerned with real world situations like how future programmers will deal with some code, then why have him code a matrix switch like this? I've never once, in over ten years, provided a matrix switch GUI. The matrix switch stuff always just happens behind the scenes and the client never sees it.

    That said, if I did have to code a matrix switch, I would almost certainly go the direction the op has, and do some *gasp* Math *gasp*.

    Seriously, if that math (divide by 10!) is too hard to figure out for some random future programmer they probably shouldn't be a programmer.

    Also, basic math operations can make certain programming tasks way easier to accomplish, so why discourage people from doing it?


    Also, not to be contrarian - :)
    I'm in agreement on the whole "not using math" thing. It's not that it is hard to figure out. It's that it makes for hard code to find/replace if you're changing out stuff. I don't know how many times I've been tasked with a new TP layout with different button schemes and had to search through thousands of lines code for "/10+23" or whatnot to change stuff out. When the buttons are identified in an array, the button numbers can change all they want but the id of that button stays the same.

    I like to write my code in such a way that when if I (or anyone else) needs to change stuff, they do it in either the array definitions or within a function call. (like fn_Projector_power_On(){ // do something } There should be almost no need to run into XXX_Event[] to change anything.

    But, that's just my opinion.

    On the Practical Exam statement: I agree whole-heartedly. I do understand that exams of this nature are hard to create in that you need to test for certain programming skills. and sometimes, the only way to get to the nut of that skill is to couch it in a not-so-real-world example. This is the nature of the beast. But, much care should be taken to let the exam taker know that is is the case. Something along the lines of, "This is not a real world example - but using this other method - for the sake of argument - how would you program this?"
  • TUTechTUTech Posts: 70
    Thanks all. I understand what you are saying. To create an array of input buttons and an array of output buttons and then a take button would actually make sense. And why would you have a button page with a complete matrix of buttons. BUT that is how it is supposed to be done. I'm supposed to create a page with all 48 buttons and there is no "Take" button.
    The thing I don't get is that I get a video drawing with 12 inputs and 4 outputs, but only 1 output is used. I'm supposed to design it for future expansion. I'll have to contact AMX training and ask what they had in mind.
    I agree with making code so that someone else can actually read it and understand it. I put a lot of comments in my code for that reason as well as for my own reference.
  • LOL I swing both ways. Technically, I almost always use data structures to hold any kind of preset data, port assignments, or that kind of thing. The one and only place I always use math is when dealing with volume control constructs where I have an active level and a display level, as well as incremental Up, Down, and Mute, and a percentage display, all tied together. Each logical audio channel uses 4 distinct number ranges for the different functions, all fixed at 10 or 20 apart. Even then, I do this:
    DEFINE_CONSTANT
    buttonset1_vol[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20 }
    buttonset1_offset = 0
    buttonset2_vol[] = {21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40 }
    buttonset2_offset = 20
    
    DEFINE_EVENT
    button_event[ dvtp_audio , buttonset2_vol ] {
         Push: {
              stack_var integer this_channel
              this_channel = button.input.channel - buttonset2_offset
    
              ....
    
    OR (for the sake of this exercise, I don't use this any more)
    
              this_channel = GET_LAST[buttonset2_vol]
    
              ...
    

    The GET_LAST is supposed to give you the index / array position of the last button pressed, so if someone pressed 23, that's the 3rd number in the array so GET_LAST should return 3. Then of course in my volume feedback functions, I have to add the offset back in for whatever I'm doing.

    Caveat: I don't use that any more because in real busy systems there have been reliability problems of GET_LAST in the past. IIRC, if there were another button event fast enough, GET_LAST Would get the last button event, which was the button event *after* the button even that tripped the PUSH event you wanted to act on. Not sure if that is still an issue with NX systems or not.
  • Don't let anyone tell you not to use math! there just faint hearted.... Real programmers use math and lots of it! :cool:
    If you make your code good but hard to read, more reason to keep you employed, since you're the only one who gets it. :p
    This is what I did, but that was about 8 years ago, so things might have changed. I would probably not us this in production but like you said, the whole task is not really practical.
    And it is always fun to make the person at AMX university who has to grade this, shake his head and go:" Oh boy, another one who want to look smart. yuk"...
    DEFINE_DEVICE
    //based on NI-3100 controller
    
    dvTP_SW     = 10001:15:0
    
    DEFINE_VARIABLE
    //matrix switcher
    INTEGER nSWOutput[12] //holds which input is connected to each output
    
    VOLATILE INTEGER nSWitcherButtons[] =
    {
        11,  //input1  > output 1 (all on tp port 15)
        12,  //input1  > output 2
        13,  //input1  > output 3
        14,  //input1  > output 4
        21,  //input2  > output 1
        22,  //input2  > output 2
        23,  //input2  > output 3
        24,  //input2  > output 4
        31,  //input3  > output 1
        32,  //input3  > output 2
        33,  //input3  > output 3
        34,  //input3  > output 4
        41,  //input4  > output 1
        42,  //input4  > output 2
        43,  //input4  > output 3
        44,  //input4  > output 4
        51,  //input5  > output 1
        52,  //input5  > output 2
        53,  //input5  > output 3
        54,  //input5  > output 4
        61,  //input6  > output 1
        62,  //input6  > output 2
        63,  //input6  > output 3
        64,  //input6  > output 4
        71,  //input7  > output 1
        72,  //input7  > output 2
        73,  //input7  > output 3
        74,  //input7  > output 4
        81,  //input8  > output 1
        82,  //input8  > output 2
        83,  //input8  > output 3
        84,  //input8  > output 4
        91,  //input9  > output 1
        92,  //input9  > output 2
        93,  //input9  > output 3
        94,  //input9  > output 4
        101, //input10 > output 1
        102, //input10 > output 2
        103, //input10 > output 3
        104, //input10 > output 4
        111, //input11 > output 1
        112, //input11 > output 2
        113, //input11 > output 3
        114, //input11 > output 4
        121, //input12 > output 1
        122, //input12 > output 2
        123, //input12 > output 3
        124  //input12 > output 4
    }        
    
    DEFINE_FUNCTION fnParseSW(CHAR sTemp[])
    {
        STACK_VAR INTEGER nOutput
        STACK_VAR INTEGER nOutputCount
        STACK_VAR INTEGER nInput
        STACK_VAR INTEGER nInputCount
        IF (FIND_STRING(sTemp,'IN',1)) //likely correct reply
        {
        nOutput = ATOI(REMOVE_STRING(sTemp,'IN',1))
        nInput  = ATOI(REMOVE_STRING(sTemp,'S',1))
        nSWoutput[nOutput] = nInput
        FOR (nInputCount = 1;nInputCount <= 12; nInputCount ++)
        {
            FOR (nOutputCount = 1;nOutputCount <= 4;nOutputCount ++)
            {
            [dvTP_SW,nSwitcherButtons[(nInputCount * 4) - (4 - nOutputCount)]] = (nSWOutput[nOutputCount] = nInputCount)
            }
        }
        }
    }
    
    DEFINE_EVENT
    
    DATA_EVENT [dvSW]
    {
        ONLINE:
        {
        SEND_COMMAND DATA.DEVICE, "'SET BAUD 9600,N,8,1 485 DISABLE'"
        SEND_COMMAND DATA.DEVICE, "'HSOFF'" //hardware handshake off
        SEND_COMMAND DATA.DEVICE, "'XOFF'"  //software handshake off
        }
        STRING:
        {
        //no fixed delimiter (ie. CR)
        //1st check for valid data
        IF (RIGHT_STRING(DATA.TEXT,1) = 'S')
        {
            fnParseSW(DATA.TEXT) //(respons)
        }
        ELSE IF (RIGHT_STRING(DATA.TEXT,1) = 'X')
        {
            //'free' format so no useful info
        }
        }
    }
    
    BUTTON_EVENT [dvTP_SW,nSWitcherButtons]
    {
        PUSH:
        {
        STACK_VAR INTEGER nInput //multiple lines for syntax highlighting
        STACK_VAR INTEGER nOutput
        nInput  = (BUTTON.INPUT.CHANNEL / 10) //discard decimal portion
        nOutput = (BUTTON.INPUT.CHANNEL % 10) //use decimal portion
        SEND_STRING dvSW, "ITOA(nInput),'*',ITOA(nOutput),'S'"
        }
    }
    
    

    the button numbering is what makes the math possible.
  • TonyAngeloTonyAngelo Posts: 315
    I would argue that the math approach is more scalable than using a button array and get_last.

    Using math, if I eventually switch out the matrix for a larger matrix (assuming the protocol is the same) all I have to do is update the touch panel file.

    Using a button array, I would have to edit the code and the touch panel file.
  • JasonSJasonS Posts: 229
    If they didn't want you to use math they wouldn't have numbered the buttons that way!!!
  • TUTechTUTech Posts: 70
    Thanks Richard. That is kind of what I had in mind.
    Doesn't the % 10 actually remove the decimal?

  • TUTech wrote: »
    Thanks Richard. That is kind of what I had in mind.
    Doesn't the % 10 actually remove the decimal?

    Good be. I might have used the wrong words in the comments (English is not my native language). What i meant is that the % 10 keeps the remainder after the division by 10. So 25 /10 = 2 (target variable is an integer) and 25 % 10 = 5

    Richard
  • ericmedleyericmedley Posts: 4,177
    There are times I use math. You almost have to since we don't have timeline arrays. And I also agree it's okay to Se more than one approach.
  • I should probably retract my statement about never using math; that's really just in context of working with buttons and inputs. Netlinx is basically a numeric token-based language, and when numbers are the fundamental basis of the language, you end up using math to manipulate objects. I still lean pretty heavily towards data structures and arrays though.
  • a_riot42a_riot42 Posts: 1,624
    Its not really "doing math" anyway, its encoding, which is obviously a perfectly valid way to do things, as long as you stay away from using magic numbers. I have a button event that is triggered from buttons that have channel numbers in the 1100-1500 range. The middle pair is the zone number, and the last number 1,2,3 or 4 tells me which button they hit. So if 1043 is hit, I know its zone 4 and a mute. 1041 would be zone 4 volume up and 1042 is volume down, 1051 is zone 5 volume up, etc. Its expandable if buttons are added (to a point) and so far hasn't been a problem to have things encoded this way. I typically stay away from coupling channel numbers to buttons, but encoding can be very useful at times when you have a lot of information to package into a channel number.
    Paul
Sign In or Register to comment.