Home AMX User Forum NetLinx Studio

Dynamic variables

Hi there,

First post and my first 'big' project.

Let me explain:
I must program a exhibition place where 7 different MIO CLASSIC TP's will control 7 different stands.
Each stand has one or two LCD screens where some video will be displayed.

The question:
I'm want to make some functions to reduce the code.
For example:
BUTTON_EVENT[MIO_1_POWER] // VOLUME UP
{
    PUSH:
    {
	fnPOWER(1,dvPHILIPS_BDL4231_1);
    }
}

In 'fnPOWER" I want to do the following:
DEFINE_FUNCTION fnPOWER(INTEGER TP, DEV DEVICE) // CONTROL THE POWER OFF THE LCD SCREEN
{
    LOCAL_VAR POWER
    POWER = GET_LAST("'MIO_',TP,'_POWER'")
    SWITCH(POWER)
    {
	CASE 0:
	{
	TEST = 'Hi I'm on'
	}
	
	CASE 1:
	{
	TEST = 'Hmm, I'm turned off'
	}
    }
}

What I want is a dynamic variable, so that in GET_LAST I can call every MIO_?_POWER in the program suplyed by the function call.

Can anybody suply me with some tips/hints/tricks?

Thanks.
«1

Comments

  • Petar wrote: »
    What I want is a dynamic variable, so that in GET_LAST

    GET_LAST() only works in the button event. You've got use GET_LAST in the button event, and pass that result into the function as a parameter. But I'm not sure whether you're trying to trap the POWER button event from multiple panels, or trying to control multiple devices with the power button.

    The GET_LAST is really an indexing function. If you have a group of buttons defined:
    integer MIO_1_POWER = 27 //aribitrary button channel number defined in panel
    integer MIO_2_POWER = 33 
    integer MIO_3_POWER = 41 
    integer MIO_4_POWER = 52 
    integer MIO_5_POWER = 54 
    integer MIO_6_POWER = 67 
    integer MIO_7_POWER = 92 
    
    integer button_set_power[] = { 
       MIO_1_POWER, //INDEX 1
       MIO_2_POWER, //INDEX 2
       MIO_3_POWER, //INDEX 3
       MIO_4_POWER, //INDEX 4
       MIO_5_POWER, //INDEX 5
       MIO_6_POWER, //INDEX 6
       MIO_7_POWER //INDEX 7
    }
    
    BUTTON_EVENT[some_tp,button_set_power] { 
       PUSH: { 
          fnPOWERCONTROL(GET_LAST(button_set_power))
       }
    }
    
    DEFINE_FUNCTION fnPOWER(INTEGER button_index) { // CONTROL POWER 
       //Still not sure what you're trying to do here... 
       //Does each button represent power off or on specifically, or just power toggle?
       //Note that one you start with indexing, you need to essentially index everything
       //Like creating a DEV array of all the touchpanels
       //And another DEV array of all the devices it actually needs to control
       //And of course arrays of the buttons
       //And they all have to match one another
    }
    

    Anyway, that should at least get you started with using GET_LAST() to dynamically get an index number that you can then use to control a device ( PULSE all_devices_array[button_index],some_data) assuming you have a matching device array.
  • a_riot42a_riot42 Posts: 1,624
    GET_LAST() only works in the button event. You've got use GET_LAST in the button event, and pass that result into the function as a parameter.

    Small correction. get_last does work anywhere, but it is recommended to be used in the button event only.
    Paul
  • jjamesjjames Posts: 2,908
    a_riot42 wrote: »
    Small correction. get_last does work anywhere, but it is recommended to be used in the button event only.
    Paul
    I'll agree with Derrick, because the word "works" is so general, that it could mean "works properly" - which is how I read it . . . :D
  • HedbergHedberg Posts: 671
    jjames wrote: »
    I'll agree with Derrick, because the word "works" is so general, that it could mean "works properly" - which is how I read it . . . :D


    How is it that get_last can be made to work improperly? Can it return an array index which does not correspond to the most recent? For example, if I have a function and in that function I want to identify the touch panel (included in a device array) which most recently had a button pressed, wouldn't get_last be appropriate to use in the function for that purpose? I believe that I've successfully used get_last in that manner.
  • Spire_JeffSpire_Jeff Posts: 1,917
    Just to argue, because I seem to enjoy it :), get_last does work properly anywhere in code. The reason to use it in the button event is because at that point, you are relatively sure that the last item used in the array is the same one that triggered the code block. If you use get_last in a function, there is a possibility that a different button event (probably on a different panel) will have fired and this will cause the last item to change. Get_last is not doing anything differently, so it is working properly. It is doing exactly what you asked it to do, but not necessarily what you hoped it would do :)

    Jeff
  • jjamesjjames Posts: 2,908
    Just kidding
    Spire_Jeff wrote: »
    Just to argue, because I seem to enjoy it :), get_last does work properly anywhere in code. The reason to use it in the button event is because at that point, you are relatively sure that the last item used in the array is the same one that triggered the code block. If you use get_last in a function, there is a possibility that a different button event (probably on a different panel) will have fired and this will cause the last item to change. Get_last is not doing anything differently, so it is working properly. It is doing exactly what you asked it to do, but not necessarily what you hoped it would do :)

    Jeff

    Okay . . . to argue back . . . .

    all I read was blah-blah-blah-blah-blah-blah.

    :D

    "If you can't answer a man's argument, all is not lost; you can still call him vile names." -Elbert Hubbard.
  • Spire_JeffSpire_Jeff Posts: 1,917
    jjames wrote: »
    Okay . . . to argue back . . . .

    all I read was blah-blah-blah-blah-blah-blah.

    :D

    "If you can't answer a man's argument, all is not lost; you can still call him vile names." -Elbert Hubbard.

    See, that's your problem. You shouldn't be reading the words, you should taste the words and see the sounds, and hear your food. I guess I need to explain everything.

    Jeff

    P.S.
    "If you ignore all of the rules of logic, the only way to loose an argument is to stop arguing." - Me
  • jjamesjjames Posts: 2,908
    Yeah . . . was just my way of saying "you're right . . . i just don't wanna admit it."

    :D
  • a_riot42a_riot42 Posts: 1,624
    Spire_Jeff wrote: »
    Just to argue, because I seem to enjoy it :), get_last does work properly anywhere in code. The reason to use it in the button event is because at that point, you are relatively sure that the last item used in the array is the same one that triggered the code block. If you use get_last in a function, there is a possibility that a different button event (probably on a different panel) will have fired and this will cause the last item to change. Get_last is not doing anything differently, so it is working properly. It is doing exactly what you asked it to do, but not necessarily what you hoped it would do :)

    Jeff

    I have wondered about this but haven't had time to test it. Since Netlinx is single threaded wouldn't the processor have to have finished the thread in which get_last was called before processing the next button event? If so, get_last will work anywhere in code and always return the correct value.
  • viningvining Posts: 4,368
    a_riot42 wrote:
    Small correction. get_last does work anywhere, but it is recommended to be used in the button event only.
    I think the recommendation is to use GET_LAST in events only, not just BUTTON_EVENTs. I use them frequently in DATA_EVENTs as well as BUTTON_EVENTs but never tried CHANNEL_EVENTs or LEVEL_EVENTs. I recall there was an issue using Get_Last in LEVEL_EVENTs but a quick search led me to this recent post by Joe Hebert in this thread:

    http://amxforums.com/showthread.php?t=1670&highlight=get_last+level+events

    Joe Hebert wrote:
    FYI

    The new features for the NI Series firmware released on Dec 3,2007 (v3.30.371) states:

    *Fixed GET_LAST to work properly with levels for DEV and DEVLEV arrays

    If that got fixed maybe we?ll be able to use TIMELINE arrays in the near future?

    I can't say I've tried it so I don't know how valid this is, Joe, what's the verdict?
  • Joe HebertJoe Hebert Posts: 2,159
    vining wrote:
    I can't say I've tried it so I don't know how valid this is, Joe, what's the verdict?
    No verdict from me either, I was just reported what I read. I use GET_LAST with BUTTON_EVENTs, DATA_EVENTs, and CHANNEL_EVENTs all the time and haven?t run into any issues but I haven?t tried it with LEVEL arrays yet.

    For what it?s worth, I?m on the side of the fence that only uses GET_LAST inside an event trigger because to me that?s where it makes the most sense (and that?s the way I was trained.)

    Here?s a real life example. Most jobs I work on are route any source to any zone (and I?m sure there are many out there that do the same thing) so I always have a general function called:
    DEFINE_FUNCTION fnRouteSource (integer source, integer zone) {
    
    }
    

    That function can be called from several different parts in code; from a touch panel button push, keypad button push, IR input from a handheld remote, a timer of some sort, a garage door opening, blah blah blah, so using GET_LAST inside of the function wouldn?t do me any good. What I normally do is something like this:
    BUTTON_EVENT[dvTPs,nSourceButtons] {
    
       PUSH: {
       
          INTEGER tp   //the TP that triggered the event
          INTEGER zone //the current zone the TP is controlling
          INTEGER src  //the source that needs to get routed
          
          tp = GET_LAST(dvTPs)
          zone = sTPInfo[tp].CurrentZone
          src = nSourceMap[GET_LAST(nSourceButtons)]
          
          fnRouteSource(src,zone)
       }
       
    }
    

    I then have the same type of events for an array of remotes and buttons, or an array of keypads and buttons, and so on. If I need the value of GET_LAST I grab it when the event fires, do what I want with it, and then pass the data as a parameter(s) to a function.

    I?d like to hear from those sitting on the other side of the GET_LAST fence and ask why they prefer using GET_LAST outside an event. Maybe I?m missing something I never thought of.
  • a_riot42a_riot42 Posts: 1,624
    Joe Hebert wrote: »
    I?d like to hear from those sitting on the other side of the GET_LAST fence and ask why they prefer using GET_LAST outside an event. Maybe I?m missing something I never thought of.

    I have only used it inside an event, but it would be nice to be able to use it elsewhere if need be. I have a feeling it was rather naively written though, meaning that its just a pointer to the top of the button queue. It likely isn't reentrant, so you can probably get situations like this:

    TP1 button push gets queued
    TP2 button push gets queued
    Button event handler for TP1 gets executed and calls get_last, but now the pointer has moved to point to TP2 so the wrong index is returned.

    Even if you only use it in a button event, I doubt this is any guarantee of correctness. Will get_last return the correct index for this button event?
    define_variable
    integer ui
    
    define_event
    button_event[dvTPs, 1]
    {
      push:
      {
        wait random_number(100)
          ui = get_last(dvTPs)
      }
    }
    

    Paul
  • Joe HebertJoe Hebert Posts: 2,159
    a_riot42 wrote: »
    Will get_last return the correct index for this button event?
    define_variable
    integer ui
    
    define_event
    button_event[dvTPs, 1]
    {
      push:
      {
        wait random_number(100)
          ui = get_last(dvTPs)
      }
    }
    
    I personally have never had GET_LAST fail me and I use it all the time in a whole bunch of events. I would never call GET_LAST inside of a WAIT like that since GET_LAST won?t be evaluated until after the WAIT expires, just as I would never call all any embedded object (e.g. DATA.DEVICE, BUTTON.INPUT.CHANNEL, etc.) inside of a WAIT. I forget the instance but I recall using DATA.DEVICE in a WAIT and slapped myself in the head when I realized why my code wasn?t working as expected.

    So to answer your question, yes GET_LAST will return correctly. But not necessarily the way you would want it to. Computers are stupid like that, they follow directions. :)
    a_riot42 wrote: »
    I have only used it inside an event, but it would be nice to be able to use it elsewhere if need be.
    Can you give an example of where? Just curious.
  • jjamesjjames Posts: 2,908
    a_riot42 wrote: »
    define_variable
    integer ui
    
    define_event
    button_event[dvTPs, 1]
    {
      push:
      {
        wait random_number(100)
          ui = get_last(dvTPs)
      }
    }
    
    I doubt, with very much certainty, that the piece of code posted above would not work. Take for example, this piece of code:
    DEFINE_FUNCTION fnTEST(INTEGER nVAR)
    {
      SEND_STRING 0, "ITOA(nVAR),' was send to the controller'";
    }
    
    ...
    BUTTON_EVENT[dvTP,1]
    {
      PUSH:
      {
        FOR(nLOOP=1;nLOOP<=5;nLOOP++)
          WAIT 5
             fnTEST(nLOOP);
      }
    }
    
    I can pretty much guarantee without testing that 5 will be sent to the controller. I didn't write this code in NS, so the formatting may be off, and there may be a syntax error if you try to compile - but I say give this a shot. WAITs are tricky at first - because you have to remember their limitations, just like the HOLD event. You cannot expect GET_LAST to return the results you'd expect in a PUSH or RELEASE event in a HOLD event, considering a HOLD is just a PUSH with a WAIT_UNTIL (<insert time here>) and a while loop if REPEAT is specified.

    But . . . I question why even use a GET_LAST outside of an event? I really can't comprehend why anyone would do such a thing. Why not just pass a parameter if you desire its use in a function / call?

    If I'm not making sense, it's because of of the late night Taco Bell run I just had. Their food is poisonous.
  • Joe HebertJoe Hebert Posts: 2,159
    Button down the hatches!
    jjames wrote:
    If I'm not making sense, it's because of of the late night Taco Bell run I just had.
    I?m not going anywhere tonight, there?s a big red parallelogram heading our way. I hope it?s not the same storm that just wiped out Cedar Rapids, Iowa today. What?s with this frickin? weather? Man.
  • a_riot42a_riot42 Posts: 1,624
    jjames wrote: »
    I doubt, with very much certainty, that the piece of code posted above would not work.

    You doubt with much certainty? Is that even possible?
    jjames wrote: »
    Take for example, this piece of code:
    DEFINE_FUNCTION fnTEST(INTEGER nVAR)
    {
      SEND_STRING 0, "ITOA(nVAR),' was send to the controller'";
    }
    
    ...
    BUTTON_EVENT[dvTP,1]
    {
      PUSH:
      {
        FOR(nLOOP=1;nLOOP<=5;nLOOP++)
          WAIT 5
             fnTEST(nLOOP);
      }
    }
    
    I can pretty much guarantee without testing that 5 will be sent to the controller.

    All the waits would get cancelled except the last one so 5 gets sent, but I don't get the point you are trying to make here.
    jjames wrote: »
    But . . . I question why even use a GET_LAST outside of an event? I really can't comprehend why anyone would do such a thing. Why not just pass a parameter if you desire its use in a function / call?

    What about if you want to play different sounds on the push and release of different groups of buttons? To do this the way you suggest all the button events would have to look like this:

    button_event[dvTPs, 1]
    {
      push:
      {
        stack_var integer tp, btn
        tp = get_last(dvTPs)
        btn = get_last(iBtns)
        pressSound(tp)
        
        // more button processing here
        
      }
      release:
      {
        stack_var integer tp, btn
        tp = get_last(dvTPs)
        btn = get_last(iBtns)
        releaseSound(tp)
        
        // more button processing here
      }
    }
    
    button_event[dvTPs, 2]
    {
      push:
      {
        stack_var integer tp, btn
        tp = get_last(dvTPs)
        btn = get_last(iBtns)
        pressSound(tp)
        
        // more button processing here
        
      }
      release:
      {
        stack_var integer tp, btn
        tp = get_last(dvTPs)
        btn = get_last(iBtns)
        releaseSound(tp)
        
        // more button processing here
      }
    }
    
    define_function pressSound(integer tp)
    {
      send_command dvTPs[tp], "'@SOU-press.mp3'"
    }
    define_function releaseSound(integer tp)
    {
      send_command dvTPs[tp], "'@SOU-release.mp3'"
    }
    


    Personally I would rather write it like this:
    button_event[dvTPs, 1]
    {
      push:
      {
        pressSound()
        // more button processing here
      }
      release:
      {
        releaseSound()
        // more button processing here
      }
    }
    
    button_event[dvTPs, 2]
    {
      push:
      {
        pressSound()
        // more button processing here
      }
      release:
      {
        releaseSound()
        // more button processing here
      }
    }
    
    define_function pressSound()
    {
      stack_var integer tp 
      tp = get_last(dvTPs)
      send_command dvTPs[tp], "'@SOU-press.mp3'"
    }
    define_function releaseSound()
    {
      stack_var integer tp 
      tp = get_last(dvTPs)
      send_command dvTPs[tp], "'@SOU-release.mp3'"
    }
    
  • PetarPetar Posts: 14
    Who... that are many reply's :)

    Well, to answer a question in the begining:
    But I'm not sure whether you're trying to trap the POWER button event from multiple panels, or trying to control multiple devices with the power button.

    There is one controller:
    5 MIO Classic panels
    5 LCD screens.

    Every panel must control 1 LCD screen. I must control soundlevel and the power of the screen.
    The point is that the code is pretty the same each time and the only difference is a digit that change in a line.

    To get to the point: how can I setup my code so that I can reduce my programming code.

    That's why I was thinking about a dynamicly variable in a function :)
  • viningvining Posts: 4,368
    Joe Hebert wrote:
    I would never call GET_LAST inside of a WAIT like that since GET_LAST won’t be evaluated until after the WAIT expires, just as I would never call all any embedded object (e.g. DATA.DEVICE, BUTTON.INPUT.CHANNEL, etc.) inside of a WAIT. I forget the instance but I recall using DATA.DEVICE in a WAIT and slapped myself in the head when I realized why my code wasn’t working as expected.
    Same here! The GET_LAST would likely return the correct value if placed inside the wait providing nothing else gets pushed before the wait executes. So it does what it's supposed to and if something else gets pushed while the wait is pending then when the wait executes guess what's the last button pushed. Not the button your code was expecting.

    As far as embedded data object like DATA.DEVICE or BUTTON.INPUT.CHANNEL they act like stack_vars and only hold a value during the duration of the event code is executing. Put it in a wait and the value is zero when the wait executes.

    jjames wrote:
    But . . . I question why even use a GET_LAST outside of an event? I really can't comprehend why anyone would do such a thing. Why not just pass a parameter if you desire its use in a function / call?
    Even if you want to pass it to a function if a wait is involved anywhere you need to place the GET_LAST value in a local var to hold the value or pass the value. Of course this too can change if some one pushes a button that triggers the same code block while the wait is pending.

    Petar wrote:
    To get to the point: how can I setup my code so that I can reduce my programming code.
    scroll down to Joe's code and use the GET_LAST to determine the index of you TP in your TP array and then the BUTTON index of your button array. That way one block of code can handle all TPs and obviously all buttons. You can then use a SWITCH CASE or SELECT ACTIVE to add things that may be TP (zone) specific if you choose.
  • PetarPetar Posts: 14
    vining wrote: »
    Joe Hebert wrote:
    scroll down to Joe's code and use the GET_LAST to determine the index of you TP in your TP array and then the BUTTON index of your button array. That way one block of code can handle all TPs and obviously all buttons. You can then use a SWITCH CASE or SELECT ACTIVE to add things that may be TP (zone) specific if you choose.

    Thanks! Because of the amount of reply's I didn't correctly read that part :)

    Only one thing: what kind of variable is 'nSourceButtons' in this case?
  • jjamesjjames Posts: 2,908
    a_riot42 wrote: »
    You doubt with much certainty? Is that even possible?
    Sure - can't one say that he certainly doubts something, rather than hesitantly doubting something?
    vining wrote: »
    Even if you want to pass it to a function if a wait is involved anywhere you need to place the GET_LAST value in a local var to hold the value or pass the value. Of course this too can change if some one pushes a button that triggers the same code block while the wait is pending.
    This is what I meant with the WAITs; you'd simply do this:
    BUTTON_EVENT[dv_TP,nSRC_BTNS]
    {
    	PUSH:
    	{
    		STACK_VAR INTEGER nPNL;
    		nPNL = GET_LAST(dv_TP)
    		
    		SWITCH (nPNL):
    		{
    			CASE 1:WAIT 20 fnDO_SOMETHING(1);
    			CASE 2:WAIT 20 fnDO_SOMETHING(2);
    			CASE 3:WAIT 20 fnDO_SOMETHING(3);
    			CASE 4:WAIT 20 fnDO_SOMETHING(4);
    			CASE 5:WAIT 20 fnDO_SOMETHING(5);
    		}                                
    	}
    }
    

    This way, if touch panels 1, 3, and 4 press something all within two seconds, they execute properly. And look, I'm using a STACK_VAR. *Gasp!* I believe this is the recommended way of calling something with the use a WAIT - embellish it all you want (i.e. cancel_waits, wait_untils, etc.), but you get the jist. The idea is to evaluate the GET_LAST immediately, then do your wait. Then GET_LAST will work like a charm with your WAITs.
    Joe Hebert wrote:
    What’s with this frickin’ weather? Man.
    I'm one of those pessimists that think it's the end of the world, don't get me started. Haha! :D
  • Joe HebertJoe Hebert Posts: 2,159
    jjames wrote:
    Take for example, this piece of code:
    DEFINE_FUNCTION fnTEST(INTEGER nVAR)
    {
      SEND_STRING 0, "ITOA(nVAR),' was send to the controller'";
    }
    
    ...
    BUTTON_EVENT[dvTP,1]
    {
      PUSH:
      {
        FOR(nLOOP=1;nLOOP<=5;nLOOP++)
          WAIT 5
             fnTEST(nLOOP);
      }
    }
    
    I can pretty much guarantee without testing that 5 will be sent to the controller
    a_riot42 wrote:
    All the waits would get cancelled except the last one so 5 gets sent, but I don't get the point you are trying to make here
    That?s not true, only the first WAIT gets put into the queue. The WAIT is ignored the last 4 times through the loop. It?s a common WAIT misconception which is what I think Jay was trying to express. The value of 5 will still get sent because that?s the value of nLoop when the WAIT expires.

    If as you say that all the WAITs are cancelled except for the last one then if you have the following code:
    DEFINE_VARIABLE
    
    INTEGER nFlash
    
    DEFINE_PROGRAM
    
    WAIT 10 nFlash = !nFlash
    

    nFlash wouldn?t ever toggle its value since every pass though mainline would cancel the WAIT. However, that?s not the case. The WAIT is put into the queue on the first pass and all subsequent passes are ignored until the WAIT expires. If you watch nFlash you?ll see it toggle once a second.
  • Joe HebertJoe Hebert Posts: 2,159
    Petar wrote:
    Only one thing: what kind of variable is 'nSourceButtons' in this case?
    DEFINE_CONSTANT
    
    INTEGER nSourceButtons[] = {1,2,3,4,5}
    
  • ericmedleyericmedley Posts: 4,177
    Joe Hebert wrote: »
    DEFINE_CONSTANT
    
    INTEGER nSourceButtons[] = {1,2,3,4,5}
    


    Just a technicality... but you could also make it a variable as well.

    VOLATILE INTEGER nSourceButtons[] = {1,2,3,4,5}
  • Petar wrote: »
    Every panel must control 1 LCD screen. I must control soundlevel and the power of the screen.

    What you need is to create a DEV array of all your panels, and a matching DEV array of your actual devices.
    DEFINE_DEVICE
    dvLCD1 = 5001:1:0
    dvLCD2 = 5001:2:0
    dvLCD3 = 5001:3:0
    dvLCD4 = 5001:4:0
    dvLCD5 = 5001:5:0
    
    dvTP1 = 10001:1:0
    dvTP2 = 10002:1:0
    dvTP3 = 10003:1:0
    dvTP4 = 10004:1:0
    dvTP5 = 10005:1:0
    
    DEFINE_CONSTANT
    button_power_on = 12 //totally arbitrary button assignments
    button_power_off = 13
    button_vol_up = 14
    button_vol_down = 15
    
    DEFINE_VARIABLE
    dev lcd_all = { 
       dvLCD1, //Array index of this device is 1
       dvLCD2, //Index 2
       dvLCD3, //and so on, I hope you get the indexing concept!
       dvLCD4,
       dvLCD5
    }
    
    dev tp_all = { 
       dvTP1, //Array index of this TP is 1
       dvTP2, //and so on with the indexing...
       dvTP3, //so that you have matching indexes
       dvTP4, //for the panels and the devices!
       dvTP5
    }
    
    //FUNCTIONS AND CALLS
    DEFINE_FUNCTION fnPOWER_ON(INTEGER this_index) { 
       SEND_STRING lcd_all[this_index],"'k a 1 1',$0D" //this would power up an LG display (or not) :-)
    }
    
    //add 3 more functions for power off and volume up and down
    
    
    DEFINE_EVENTS
    BUTTON_EVENT[tp_all,button_power_off] { //all panels use the same channel number for power off button
       push: { 
          fnPOWER_ON(GET_LAST(tp_all)) //This sends the panel INDEX number to the function
       }
    }
    
    //add 3 more button events for power off and volume up and down
    

    So, in this example, you're matching the panel index to the device index. You get the index of the panel with the GET_LAST() in your button_event, and pass that off to the function, which will identify which device in your LCD device array you want to send the string to.

    You only need one button_event trigger for each distinct action for all devices and all panels, and one function to handle the communications for all devices. But it comes at a cost of lots of array definitions! For really small projects, it might not be worth it. I'll leave it to you to decide whether I've "saved programming code" in this example or not. But even for just 4 distinct events for each of 5 devices I think indexing does reduce the overall size of the program.

    Once you've got the hang of this example, the next thing to try is indexing your button set so you can have just a single BUTTON_EVENT that handles all of the actions you need. And whittling down the functions to just one function (hint: match an indexed set of commands to your index of actions, pass the button index, and viola! one function will send the correct command for each action).

    As an aside note, I'm really sorry to have started a huge discussion about the particulars of GET_LAST when my intent was only to help Petar wrap his head around indexing as a way to reduce code. Yes, it does return some value no matter where you put it in the code, so it does "work" anywhere. Of course I was implying "work reliably" - in the sense that if you limit the use of GET_LAST to actual events (button event, etc.), it will always return the value you expect.

    If you use GET_LAST() in functions and waits, you will likely get what you expect when you're testing the system, but when you put it in production and really have several people pounding away at several panels at the same time, the system is likely to do some unexpected things.
  • viningvining Posts: 4,368
    You could also take this short version:
    BUTTON_EVENT[dvTPs,nSourceButtons] {
    
       PUSH: {
       
          INTEGER tp   //the TP that triggered the event
          INTEGER zone //the current zone the TP is controlling
          INTEGER src  //the source that needs to get routed
          
          tp = GET_LAST(dvTPs)
          zone = sTPInfo[tp].CurrentZone
          src = nSourceMap[GET_LAST(nSourceButtons)]
          
          fnRouteSource(src,zone)
       }
    

    and make it shorter.
    BUTTON_EVENT[dvTPs,nSourceButtons] {
    
       PUSH: {
       
         fnRouteSource(nSourceMap[GET_LAST(nSourceButtons)],sTPInfo[GET_LAST(dvTPs)].CurrentZone)
       }
    
    

    Of course I would go with the 1st version (Joe's) since it's more readable. Less code is of course less code but not necassarily better code or code that is easier to follow six months down the road.


    fogled@mizzou wrote:
    I'm really sorry to have started a huge discussion about the particulars of GET_LAST

    That's what makes the forum fun and the more times you beat a subject to death the more we can all learn or re-affirm what we think we know and since most days I can't tell what I actually know from what I think I know or what I think I don't know from what I know I actually don't know this continued beating of the dead horse stuff actually still helps me.
  • DarksideDarkside Posts: 345
    Spire_Jeff wrote: »
    Just to argue, because I seem to enjoy it :), get_last does work properly anywhere in code.

    The reason to use it in the button event is because at that point, you are relatively sure that the last item used in the array is the same one that triggered the code block.

    If you use get_last in a function, there is a possibility that a different button event (probably on a different panel) will have fired and this will cause the last item to change. Get_last is not doing anything differently, so it is working properly.

    It is doing exactly what you asked it to do, but not necessarily what you hoped it would do :)

    Jeff
    Good concise and correct explanation of GET_LAST behavior.

    If you need to work on the GET_LAST value, copy it with a LOCAL_VAR because as others rightly point out, the value of GET_LAST can change very quickly - particularly with multiple users. At least if you copy it you can be sure it's the right val.

    jjames's code below is how I would do it too.

    ...With or without waits? - At least you have the option to do something like that if you need to using this method....

    BUTTON_EVENT[dv_TP,nSRC_BTNS]
    {
    PUSH:
    {
    STACK_VAR INTEGER nPNL;
    nPNL = GET_LAST(dv_TP)

    SWITCH (nPNL):
    {
    CASE 1:WAIT 20 fnDO_SOMETHING(1);
    CASE 2:WAIT 20 fnDO_SOMETHING(2);
    CASE 3:WAIT 20 fnDO_SOMETHING(3);
    CASE 4:WAIT 20 fnDO_SOMETHING(4);
    CASE 5:WAIT 20 fnDO_SOMETHING(5);
    }
    }
    }
  • Yes but
    a_riot42 wrote: »
    I have wondered about this but haven't had time to test it. Since Netlinx is single threaded wouldn't the processor have to have finished the thread in which get_last was called before processing the next button event? If so, get_last will work anywhere in code and always return the correct value.

    You are right NetLinx is single threaded and one event has to be played out before another can begin. The problem is how are you using that code. Is the code being called directly from a button event and only a button event with no waits or timeline delays? then yes the GET_LAST function will always return the correct value. Once you add in a wait or timeline delay then all bets are off (I think even long FOR loops or WHILE loops will add enough of a delay in some situations). The other problem/bug is when using GET_LAST in a hold statement. When you press or release a button GET_LAST gets updated. When you hold a button GET_LAST does not get updated. We had some code where the user would press and then hold a button. In the time period between the press and the hold another button somewhere else was pressed. GET_LAST now changes focus to the other panel. When the event handler tried to run the code in the first buttons hold statement the GET_LAST function was now wrong (or not what we were expecting) and we did not get the desired results. We were actually controlling functions in the second room. This was very difficult to track down because every thing would work 99.99% of the time.

    I hope that helps to understand a little bit more.

    Dan
  • jjamesjjames Posts: 2,908
    The other problem/bug is when using GET_LAST in a hold statement. When you press or release a button GET_LAST gets updated. When you hold a button GET_LAST does not get updated. We had some code where the user would press and then hold a button. In the time period between the press and the hold another button somewhere else was pressed. GET_LAST now changes focus to the other panel. When the event handler tried to run the code in the first buttons hold statement the GET_LAST function was now wrong (or not what we were expecting) and we did not get the desired results.

    Dan, I'm sure you've already figured it out with whatever way your doing it, but just to open it up to everyone, here's how I'd do such a thing (setting a preset without a HOLD event, and using GET_LAST() properly.)
    BUTTON_EVENT[dv_TP,nTUNER_PRESET_BTNS]		// Tuner preset select
    {
    	PUSH:
    	{
    		LOCAL_VAR INTEGER nPANEL
    		LOCAL_VAR INTEGER nIND
    		nIND = GET_LAST(nTUNER_PRESET_BTNS)
    		nPANEL = GET_LAST(dv_TP);
    		
    		// lock out other users from setting a preset at the same time
    		IF(!nTUNER_LOCK2)
    		{
    			ON[nTUNER_LOCK2]
    			
    			// Wait 3 seconds
    			WAIT 30 'RADIO PRESET'
    			{
    				// Turn on lock to indicate hold
    				ON[nLOCK2]
    				// Set preset
    					cTUNER_PRESET[nIND] = cSomeVariableWithText
    				// Set preset & do any other fancy stuff such as displaying feedback of the current
    				// preset on the button you're pressing, or double beeping, or displaying
    				// a scary picture to scare the crap out of the client.
    				
    				// you can also add a delay so that nothing can happen to the preset RIGHT AWAY
    				WAIT 20
    					OFF[nLOCK2]
    			}
    		}
    	}
    	RELEASE:
    	{
    		STACK_VAR INTEGER nIND
    		STACK_VAR INTEGER nPNL
    		nPNL = GET_LAST(dv_TP);
    		nIND = GET_LAST(nTUNER_PRESET_BTNS)
    		
    		// Cancel wait so preset saving doesn't occur
    		CANCEl_WAIT 'RADIO PRESET'
    		OFF[nTUNER_LOCK2]
    		
    		// If the wait hasn't been executed, go to selected preset
    		IF(!nLOCK2)
    		{
    			// send it to the tuner, or whatever
    			SEND_STRING dvRADIO, "cTUNER_PRE[nIND]"
    		}
    	}
    }
    
  • a_riot42a_riot42 Posts: 1,624
    You are using the semaphore method which has its pitfalls as well. I had a fear get_last might not work in a hold, so when I saw the weird behavior I tested it first and found out what you have found out.

    I got around it a different way. I keep track of what ui was returned in the get_last in the push and use it in the hold. This seems to work fine without too much extra code or blocking other users out. If two users try and do a hold on the same device, Netlinx nicely takes care of that automatically.
    Paul
  • jjamesjjames Posts: 2,908
    a_riot42 wrote: »
    You are using the semaphore method which has its pitfalls as well. I had a fear get_last might not work in a hold, so when I saw the weird behavior I tested it first and found out what you have found out.

    I got around it a different way. I keep track of what ui was returned in the get_last in the push and use it in the hold. This seems to work fine without too much extra code or blocking other users out. If two users try and do a hold on the same device, Netlinx nicely takes care of that automatically.
    Paul

    Yeah . . . nothing is perfect.

    Don't get me wrong, I could switch that easily over to allow other panels to do stuff, this is just one of the ways I would do it. It is the easiest, and I don't know about you, but typically we don't have gobs of people running around setting their presets all at once, so it's not a problem

    But, in true fashion of the forum, it seems to always be a debate of most efficient code rather than most practical, straight forward, or "easiest" code. ;)
Sign In or Register to comment.