Get_Last alternatives
jjames
AMX Sustaining Engineer
Just curious . . . for those of you who don't use GET_LAST, what's your poison? I can think of two different ways of handling it; the first would be using BUTTON.INPUT.CHANNEL, and the other would have button events for every single button, one for volume up, one for volume down, etc, etc.
So - in true open forum fashion, what do you do? No need to jump immediately into the efficiency debate, let's hear (read) what you use first - THEN debate.
Feel free to post some watered-down code or at least a description to what you use, and why.
Remember, there IS NO wrong way of doing something here . . . unless of course you insist on using GET_LAST in a TIMELINE_EVENT.
So - in true open forum fashion, what do you do? No need to jump immediately into the efficiency debate, let's hear (read) what you use first - THEN debate.
Remember, there IS NO wrong way of doing something here . . . unless of course you insist on using GET_LAST in a TIMELINE_EVENT.
Comments
-
I use the Button.input.channel method. I also get the device/port/system as well of the thing that's causing the event. It is precisely the 'TImeline' issue that I choose this method. It requires no more or less effort. And... can't a Get_Last be fooled if another button event happens between the time something is going on?
-
I use both get_last and button.input.channel. I only use get_last for things that require common actions, such as source selection.
(*********************************************) (* INFO: Source selection *) (* *) (*********************************************) button_event[dv_TPs, WATCH_BTNS] { push: { i_index_watch = get_last (WATCH_BTNS) fn_activate_source (i_index_watch) } } (*********************************************) (* INFO: Manual screen control *) (* *) (*********************************************) button_event[dv_TPs, SCREEN_DN] button_event[dv_TPs, SCREEN_UP] { push: { switch(button.input.channel) { case SCREEN_DN: fn_screen (SET_PWR_ON) case SCREEN_UP: fn_screen (SET_PWR_OFF) } } } -
Well it's a mixture of both for different applications.
I was trying to think of 2 good examples for each option (get_last & b.i.c), but my brain is slow today and the best I can do is it depends on the maths I might be doing within the event...
I don't think I've done individual button events since I first worked with Netlinx, apart from during device testing / debug... -
I try to exclusively use either GET_LAST, or single button events (i.e. DV_TP,nPWR_OFF), I haven't used B.I.C. in quite a while. On occasion I'll throw in some code that I know works and it's using B.I.C. and won't put in any effort to change it, but that's rare. I do think it's time I need to rethink my way of coding though; it's good to update it from time to time and try new things.
-
About the only time I use single button events is when using an array of buttons results in my having to create a Switch..Case type implementation. I don't always use single events, but most of the time I go with single events because of two reasons: 1. Less work the processor has to do (no Switch statement) 2. I can collapse code to make it easier to view and modify.
Jeff
P.S.
I use Get_Last a lot because I need an indexed value of the push. It just makes the math easier
-
I occasionally just use button.input.channel (I call it BUTTICH ;-) for simple one-off stuff. But 90+% of the time, I have a good reason to use GET_LAST() and indexing.
-
The main reason I dont' like the Get_last is that there is room for error in index the buttons.
For example let's say you have a button array like this
nButtons={1,2,3,5,6}
example:BUTTON_EVENT[dev_TP_1,nBUTTONS] { PUSH: { nTP_Button_pushed_BIC=BUTTON.INPUT.CHANNEL nTP_Button_pushed_GL=get_last(nButtons) // DO SOMETHING } }
Now, if you hit button 5 on the touch panel, nTP_Buttonpushed_BIC will equal 5 but nTP_Button_pushed_GL will equal 4 since the number 5 happens 4th on the list. I find the numeric data more advantageous than where it falls in my array order. I just choose my TP button numbers accordingly. for transport controls, I just tend to make the button numbers match up with the IR file or setup my serial commands in the same order as the AMX standard for transports.
If I have an enormous button array, I don't want to do the counting on my fingers and toes to figure out where button 367 actually falls in the list of buttons. -
Interesting discussion. I hadn't really thought about what I do or why before. As I think about it I seem to use Get Last only when I am trying to find out what touch panel was used. Otherwise I use button.input.channel.
-
Eric,
I see where you're going with that, and once you mentioned BIC to IR mapping, it reminded me, I do that as well. I use GET_LAST for things such as source selection, camera selection, room selection, any type of selection really. But for straight through commands, I will use BIC. I do agree, it is much easier to do SEND_COMMAND dvDEVICE,"'SP',BUTTON.INPUT.CHANNEL" than mapping it all out in arrays. -
This all got me to thinking. If GET_LAST is really that bad, why not just write a function that simulates GET_LAST? Here is one that should work for an array of channels: (I even added one that lets you set an offset
)DEFINE_FUNCTION INTEGER myGetLast(INTEGER nCHANNEL, INTEGER nARRAY[]){ STACK_VAR INTEGER x; STACK_VAR INTEGER y; y=LENGTH_ARRAY(nARRAY); FOR(x=1;x<=y;x++){ IF(nCHANNEL == nARRAY[x]) RETURN x; } RETURN 0; } DEFINE_FUNCTION INTEGER myGetLastOffset(INTEGER nCHANNEL, INTEGER nARRAY[],INTEGER nOFFSET){ STACK_VAR INTEGER x; STACK_VAR INTEGER y; y=LENGTH_ARRAY(nARRAY); FOR(x=1;x<=y;x++){ IF(nCHANNEL == nARRAY[x]) RETURN (x+nOFFSET); } RETURN 0; }
You could also write one that works for devices:DEFINE_FUNCTION INTEGER myGetLastDev(DEV nCHANNEL, DEV nARRAY[]){ STACK_VAR INTEGER x; STACK_VAR INTEGER y; y=LENGTH_ARRAY(nARRAY); FOR(x=1;x<=y;x++){ IF(nCHANNEL.NUMBER == nARRAY[x].NUMBER and nCHANNEL.PORT == nARRAY[x].PORT and nCHANNEL.SYSTEM == nARRAY[x].SYSTEM) RETURN x; } RETURN 0; }
You would have to call the correct function, but at least you could call them from everywhere in code without worrying. Unless I am not thinking clearly here (which is very possible). For some reason, the above just seems too simple. I wonder if it's because GET_LAST is overloaded????
Jeff -
Spire_Jeff wrote: »This all got me to thinking. If GET_LAST is really that bad,
Did I miss something? What's wrong with Get_Last? -
My thoughts exactly. I've never had a problem with GET_LAST and I use it all the time.TonyAngelo wrote: »Did I miss something? What's wrong with Get_Last? -
I think the problem is one that manifests itself only occasionally and only in certain circumstances (namely in HOLDs if I recall correctly). I use it all the time myself, but given that there have been a few documented situations in which its use is not recommended, I've been thinking about switching.
Jeff
P.S.
I was only able to find documentation in technotes about problems with GET_LAST being used in level events for the devices. I do know that there is also a problem with Holds. -
I don?t want to sound argumentative (not that it?s ever stopped me beforeericmedley wrote:The main reason I dont' like the Get_last is that there is room for error in index the buttons.
) but there is no room for error in the index of buttons. Using your code as an example:nButtons={1,2,3,5,6} BUTTON_EVENT[dev_TP_1,nBUTTONS] { PUSH: { nTP_Button_pushed_BIC=BUTTON.INPUT.CHANNEL nTP_Button_pushed_GL=get_last(nButtons) // DO SOMETHING } }get_last(nButtons) does not and is not supposed to return the button number that was pushed, it returns the index of the array. If you want to use GET_LAST to get the actual button that was pushed you would need to change:
nTP_Button_pushed_GL=get_last(nButtons)
to:
nTP_Button_pushed_GL=nButtons[get_last(nButtons)]
which is just the long way around of saying:
BUTTON.INPUT.CHANNEL
There is nothing wrong with GET_LAST itself, just as there is nothing wrong with BUTTON.INPUT.CHANNEL. In your case if you want the actual button number to dictate how the code runs then BUTTON.INPUT.CHANNEL is the most direct route. GET_LAST and BUTTON.INPUT.CHANNEL are two completely different animals. Which one to ride all depends on where you want to go. I?m not suggesting that you (or anyone else) should use GET_LAST; my only point is that there is nothing wrong with it when used correctly.
I personally find that too restricting. I normally choose to take the opposite approach and would rather setup a simple mapping scheme instead of tying myself to the actual TP button numbers themselves. I prefer to use the index of the array (therefore I use GET_LAST) as a pointer to the data I actually want to use. It?s a little more work but I find it to be more flexible. I do something like the following:ericmedley wrote:I find the numeric data more advantageous than where it falls in my array order. I just choose my TP button numbers accordingly. for transport controls, I just tend to make the button numbers match up with the IR file or setup my serial commands in the same order as the AMX standard for transports.DEFINE_DEVICE dvCable= 5001:9:0 dvTP = 10001:1:0 DEFINE_CONSTANT INTEGER nCableButtons[] = { 100,101,102,103,104,110,112 } INTEGER nCableMap[] = { 1, // Play //1 //100 2, // Stop //2 //101 3, // Pause //3 //102 6, // FFwd //4 //103 7, // Rev //5 //104 51, // Instant Reply//6 //110 8 // Record //7 //112 } DEFINE_EVENT BUTTON_EVENT[dvTp,nCableButtons] { PUSH: { //pushing TP button 110 will send out IR code 51 - Instant Replay SEND_COMMAND dvCable,"'SP',nCableMap[GET_LAST(nCableButtons)]" } }Using this approach I don?t need to care what the actual TP buttons are and I can change, add, or remove as I please. The TP buttons don?t have to be consecutive; I can use whatever is available for the job.
Again, I?m not suggesting you are anyone should take this approach; I?m simply offering an alternative. I can certainly see cases where the button numbers can be used to drive the code but that?s not my natural instinct. -
GET_LAST
I think that tech note is out of date. I can?t speak from first hand experience as I haven?t tested it yet but I believe that issue has been resolved for a while now.Spire_Jeff wrote:I was only able to find documentation in technotes about problems with GET_LAST being used in level events for the devices.
A HOLD is a type of WAIT, therefore all bets are off if using GET_LAST in that instance.Spire_Jeff wrote:I do know that there is also a problem with Holds.
I wonder why GET_LAST is getting such a bad rap recently, it?s such a friendly little function that faithfully does what it?s supposed to do. I?ll stick by your side GET_LAST.
-
Interesting that a question from my side, leads to a discussion of the use of GET_LAST
-
Joe Hebert wrote:
I Concur!I wonder why GET_LAST is getting such a bad rap recently, it?s such a friendly little function that faithfully does what it?s supposed to do. I?ll stick by your side GET_LAST. -
Joe Hebert wrote: »The TP buttons don?t have to be consecutive; I can use whatever is available for the job.
I agree, I haven't said there's anything wrong with using get_last. In fact, my post is somewhat in defense of your statement. I've heard people on this forum more-or-less say that it's (using get_last)the only accepted way of setting up and manipulating a button array.
I just don't personally use it myself. It's just the way I organize things.
Also, I quoted the text above to make the statement that you don't have to have consecutive buttons if you choose to use the BIC method. They can be in any order you with and in no particular grouping. I don't know if that's what you were implying. So, if not, then ignore this comment.
-
I've used the scheme that you use in the past; and to be honest, I felt that it was too cumbersome and convoluted. That's not to say that I would NEVER use something like that again, but . . . I don't know. When I think of button number 22, I think of channel up, and 26 - I think of mute. 44? Menu. It's just a habit of mine I guess. I can definitely see the advantage of it, but it was too much of a pain to jump back and forth and look at things, looking for the channel and then looking for which IR code it's spitting out.
I guess I sometimes like the straight through kind of code over the this-leads-to-this-leads-to-this type of code. But, every situation calls for something different.
-
I forgot to pose this question last night as I wrote the functions, but I was wondering what would happen if someone accidentally created a button array that has multiple instances of the same value. In my functions, given multiple occurrences of the same value, the index of the first position of occurrence would be returned. I was wondering if GET_LAST did something different or if it was just an overloaded version of my functions. I thought it might create multiple events that get triggered, but I wasn't sure. I wrote this chunk of code to test it:
DEFINE_CONSTANT INTEGER nUSER_BTN_TEST[] = {1,2,3,4,1,2,3,4,5,1,1,2,3,4,6} DEFINE_EVENT BUTTON_EVENT[dvTEST,nUSER_BTN_TEST]{ PUSH:{ LOCAL_VAR INTEGER x; x++; SEND_STRING 0,"'Button Push:',ITOA(BUTTON.INPUT.CHANNEL),' Iteration:',ITOA(x),' Get_Last:',ITOA(GET_LAST(nUSER_BTN_TEST))"; } HOLD [3,REPEAT]:{ LOCAL_VAR INTEGER x; x++; SEND_STRING 0,"'Button Hold:',ITOA(BUTTON.INPUT.CHANNEL),' Iteration:',ITOA(x),' Get_Last:',ITOA(GET_LAST(nUSER_BTN_TEST))"; } RELEASE:{ LOCAL_VAR INTEGER x; x++; SEND_STRING 0,"'Button Release:',ITOA(BUTTON.INPUT.CHANNEL),' Iteration:',ITOA(x),' Get_Last:',ITOA(GET_LAST(nUSER_BTN_TEST))"; } }
As I expected, emulating a push on channel 1 generated 4 PUSH: events and 4 RELEASE: events. The thing that was interesting to me was the HOLD: Events. Only one instance of a HOLD: is created which reveals the correlation between HOLDs and WAITs. Here is the output generated by a push:
Line 13 (10:40:28):: CIpHold::AddHold - Duplicate
Line 14 (10:40:28):: CIpHold::AddHold - Duplicate
Line 15 (10:40:28):: Button Push:2 Iteration:5 Get_Last:2
Line 16 (10:40:28):: Button Push:2 Iteration:6 Get_Last:2
Line 17 (10:40:28):: Button Push:2 Iteration:7 Get_Last:2
Line 18 (10:40:28):: Button Hold:2 Iteration:2 Get_Last:2
Line 19 (10:40:29):: Button Hold:2 Iteration:3 Get_Last:2
I posted this info simply because I wanted to know how things happened and I decided to share the results in the slim chance that someone might find it enlightening or even interesting
Jeff -
Alright, let me see if I'm gathering all of this correctly:
BUTTON.INPUT.CHANNEL is superior in the sense that it is, from a programmer's perspective, the most secure of the two options. The downside is that the codes cannot therefore be changed in some more organized constant; all such changes must be made in code, and made sure not to be upsetting anything.
GET_LAST() is superior in that it allows for a greater amount of organizational control. Additionally, if it's method is fully understood, than there's virtually nothing which can be accomplished via the alternative option. It, however, has two potential downfalls: First being that it's the only overloaded function (I think) within all of Netlinx code, and therefore has in its peculiar function the likeliness to be less reliable. Secondly, there runs the issue that it cannot (or becomes vulnerable to not) function correctly in such things as WAITS, TIMELINES, and HOLDS.
Obviously, if one understands the limit of both, then solid programming can come from either direction. Still, I'm very interested if Spire_Jeff's concept of creating a function to otherwise avoid the overloading issue would be a feasible solution to the whole "problem."
I personally use "GET_LAST" a great deal, and have had no immediate problems - so long as I don't assume it to do something it doesn't, or push it to its logical limitations. Still, if it could be made to be more secure by writing one's own function to handle it all, then awesome. What do y'all think? -
jason_the_adams wrote: »Alright, let me see if I'm gathering all of this correctly:
BUTTON.INPUT.CHANNEL is superior in the sense that it is, from a programmer's perspective, the most secure of the two options. The downside is that the codes cannot therefore be changed in some more organized constant; all such changes must be made in code, and made sure not to be upsetting anything.
GET_LAST() is superior in that it allows for a greater amount of organizational control. Additionally, if it's method is fully understood, than there's virtually nothing which can be accomplished via the alternative option. It, however, has two potential downfalls: First being that it's the only overloaded function (I think) within all of Netlinx code, and therefore has in its peculiar function the likeliness to be less reliable. Secondly, there runs the issue that it cannot (or becomes vulnerable to not) function correctly in such things as WAITS, TIMELINES, and HOLDS.
Obviously, if one understands the limit of both, then solid programming can come from either direction. Still, I'm very interested if Spire_Jeff's concept of creating a function to otherwise avoid the overloading issue would be a feasible solution to the whole "problem."
I personally use "GET_LAST" a great deal, and have had no immediate problems - so long as I don't assume it to do something it doesn't, or push it to its logical limitations. Still, if it could be made to be more secure by writing one's own function to handle it all, then awesome. What do y'all think?
All these shortcomings can be simply overcome by either assigning your result from GET_LAST() or the contents of BUTTON.INPUT.CHANNEL to a variable and sending that to your function. No alternatives are needed. -
DHawthorne wrote: »All these shortcomings can be simply overcome by either assigning your result from GET_LAST() or the contents of BUTTON.INPUT.CHANNEL to a variable and sending that to your function. No alternatives are needed.
Well, in actuality, that's presently what I do, which satisfies most of the problems. Still, GET_LAST does have somewhat of an overload to it, which in Netlinx is worthy of a raised eyebrow. And while this works fine in the instance of a WAIT statement, it doesn't fix the issue of GET_LAST causing problems in HOLD events - which, admittedly, I've never had problems with, but I hear when it does happen it is in fact worse than a sharp stick in the eye. -
DHawthorne wrote: »All these shortcomings can be simply overcome by either assigning your result from GET_LAST() or the contents of BUTTON.INPUT.CHANNEL to a variable and sending that to your function. No alternatives are needed.
I think that the HOLD problem with GET_LAST() will still exist even if you assign it to a variable. The problem arises in HOLD events because they are more like WAITs. The problem will only manifest itself if you are in the middle of a hold event on one panel and a user on a different panel pushes a button from the same array as I recall.
Jeff -
Spire_Jeff wrote: »I think that the HOLD problem with GET_LAST() will still exist even if you assign it to a variable. The problem arises in HOLD events because they are more like WAITs. The problem will only manifest itself if you are in the middle of a hold event on one panel and a user on a different panel pushes a button from the same array as I recall.
Jeff
I think too that if you use a local variable this creates the problem with the hold/wait/etc...
I always go ahead and store the BIC values in a regular variable array, tag that particular event with a 'serial number' of sorts that identifies the cell in the array and then retire that cell once it's done. That way the data never gets interupted by another event happening. It's not like it's a big memory hog or something. It's just an integer.
I have to admit, that's been confusing me in this discussion: not knowing why things get lost once you do a hold or wait or timeline... I got it this morning. If you use this method it's a non-issue. -
Ok, here are a couple more interesting things on this (at least to me
):
First, I had to modify the myGetLastDev function slightly to account for devices declared with a system of 0. Here are the new functions:DEFINE_FUNCTION INTEGER myGetLast(INTEGER nCHANNEL, INTEGER nARRAY[]){ STACK_VAR INTEGER x; STACK_VAR INTEGER y; y=LENGTH_ARRAY(nARRAY); FOR(x=1;x<=y;x++){ IF(nCHANNEL == nARRAY[x]) RETURN x; } RETURN 0; } DEFINE_FUNCTION INTEGER myGetLastDev(DEV nCHANNEL, DEV nARRAY[]){ STACK_VAR INTEGER x; STACK_VAR INTEGER y; y=LENGTH_ARRAY(nARRAY); FOR(x=1;x<=y;x++){ IF(nCHANNEL.NUMBER == nARRAY[x].NUMBER and nCHANNEL.PORT == nARRAY[x].PORT and (nCHANNEL.SYSTEM == nARRAY[x].SYSTEM or (!nARRAY[x].SYSTEM and nCHANNEL.SYSTEM == GET_SYSTEM_NUMBER())) ) RETURN x; } RETURN 0; }
Here is the test code I used to try this out. I am using DO_PUSH_TIMED to simulate other buttons being pushed during a hold:DEFINE_DEVICE dvTEST1 = 10001:1:0; dvTEST2 = 10002:1:1; dvTEST3 = 10003:1:0; dvTEST4 = 10004:1:1; dvTEST5 = 10005:1:0; DEFINE_CONSTANT INTEGER nUSER_BTN_TEST[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} DEFINE_EVENT BUTTON_EVENT[dvTPs,nUSER_BTN_TEST]{ PUSH:{ LOCAL_VAR INTEGER x,y; x = GET_LAST(dvTPs); y = GET_LAST(nUSER_BTN_TEST); SEND_STRING 0,"'###############################################################################'"; SEND_STRING 0,"'|[PUSH:]'"; SEND_STRING 0,"'| GET_LAST() -Dev:',ITOA(GET_LAST(dvTPs)),' Actual Dev:', ITOA(BUTTON.INPUT.DEVICE.NUMBER),':',ITOA(BUTTON.INPUT.DEVICE.PORT),':',ITOA(BUTTON.INPUT.DEVICE.SYSTEM), ' | Actual Button Push:',ITOA(BUTTON.INPUT.CHANNEL),' Get_Last Index:',ITOA(GET_LAST(nUSER_BTN_TEST))"; SEND_STRING 0,"'| Variable() -Dev:',ITOA(x),' Actual Dev:', ITOA(BUTTON.INPUT.DEVICE.NUMBER),':',ITOA(BUTTON.INPUT.DEVICE.PORT),':',ITOA(BUTTON.INPUT.DEVICE.SYSTEM), ' | Actual Button Push:',ITOA(BUTTON.INPUT.CHANNEL),' Get_Last Index:',ITOA(y)"; SEND_STRING 0,"'| my() -Dev:',ITOA(myGetLastDev(BUTTON.INPUT.DEVICE,dvTPs)),' Actual Dev:', ITOA(BUTTON.INPUT.DEVICE.NUMBER),':',ITOA(BUTTON.INPUT.DEVICE.PORT),':',ITOA(BUTTON.INPUT.DEVICE.SYSTEM), ' | Actual Button Push:',ITOA(BUTTON.INPUT.CHANNEL),' myGetLast Index:',ITOA(myGetLast(BUTTON.INPUT.CHANNEL,nUSER_BTN_TEST))"; } HOLD [20,REPEAT]:{ LOCAL_VAR INTEGER x,y; x = GET_LAST(dvTPs); y = GET_LAST(nUSER_BTN_TEST); SEND_STRING 0,"'*******************************************************************************'"; SEND_STRING 0,"'|[HOLD:]'"; SEND_STRING 0,"'| GET_LAST() -Dev:',ITOA(GET_LAST(dvTPs)),' Actual Dev:', ITOA(BUTTON.INPUT.DEVICE.NUMBER),':',ITOA(BUTTON.INPUT.DEVICE.PORT),':',ITOA(BUTTON.INPUT.DEVICE.SYSTEM), ' | Actual Button Hold:',ITOA(BUTTON.INPUT.CHANNEL),' Get_Last Index:',ITOA(GET_LAST(nUSER_BTN_TEST))"; SEND_STRING 0,"'| Variable() -Dev:',ITOA(x),' Actual Dev:', ITOA(BUTTON.INPUT.DEVICE.NUMBER),':',ITOA(BUTTON.INPUT.DEVICE.PORT),':',ITOA(BUTTON.INPUT.DEVICE.SYSTEM), ' | Actual Button Hold:',ITOA(BUTTON.INPUT.CHANNEL),' Get_Last Index:',ITOA(y)"; SEND_STRING 0,"'| my() -Dev:',ITOA(myGetLastDev(BUTTON.INPUT.DEVICE,dvTPs)),' Actual Dev:', ITOA(BUTTON.INPUT.DEVICE.NUMBER),':',ITOA(BUTTON.INPUT.DEVICE.PORT),':',ITOA(BUTTON.INPUT.DEVICE.SYSTEM), ' | Actual Button Hold:',ITOA(BUTTON.INPUT.CHANNEL),' myGetLast Index:',ITOA(myGetLast(BUTTON.INPUT.CHANNEL,nUSER_BTN_TEST))"; DO_PUSH_TIMED(dvTPs[RANDOM_NUMBER(5)+1],nUSER_BTN_TEST[RANDOM_NUMBER(15)+1],5); } RELEASE:{ LOCAL_VAR INTEGER x,y; x = GET_LAST(dvTPs); y = GET_LAST(nUSER_BTN_TEST); SEND_STRING 0,"'-=============================================================================-'"; SEND_STRING 0,"'|[RELEASE:]'"; SEND_STRING 0,"'| GET_LAST() -Dev:',ITOA(GET_LAST(dvTPs)),' Actual Dev:', ITOA(BUTTON.INPUT.DEVICE.NUMBER),':',ITOA(BUTTON.INPUT.DEVICE.PORT),':',ITOA(BUTTON.INPUT.DEVICE.SYSTEM), ' | Actual Button Release:',ITOA(BUTTON.INPUT.CHANNEL),' Get_Last Index:',ITOA(GET_LAST(nUSER_BTN_TEST))"; SEND_STRING 0,"'| Variable() -Dev:',ITOA(x),' Actual Dev:', ITOA(BUTTON.INPUT.DEVICE.NUMBER),':',ITOA(BUTTON.INPUT.DEVICE.PORT),':',ITOA(BUTTON.INPUT.DEVICE.SYSTEM), ' | Actual Button Release:',ITOA(BUTTON.INPUT.CHANNEL),' Get_Last Index:',ITOA(y)"; SEND_STRING 0,"'| my() -Dev:',ITOA(myGetLastDev(BUTTON.INPUT.DEVICE,dvTPs)),' Actual Dev:', ITOA(BUTTON.INPUT.DEVICE.NUMBER),':',ITOA(BUTTON.INPUT.DEVICE.PORT),':',ITOA(BUTTON.INPUT.DEVICE.SYSTEM), ' | Actual Button Release:',ITOA(BUTTON.INPUT.CHANNEL),' myGetLast Index:',ITOA(myGetLast(BUTTON.INPUT.CHANNEL,nUSER_BTN_TEST))"; SEND_STRING 0,"'/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/'"; } }
and here are the results:Line 1 (17:54:29):: ############################################################################### Line 2 (17:54:29):: |[PUSH:] Line 3 (17:54:29):: | GET_LAST() -Dev:3 Actual Dev:10003:1:1 | Actual Button Push:5 Get_Last Index:5 Line 4 (17:54:29):: | Variable() -Dev:3 Actual Dev:10003:1:1 | Actual Button Push:5 Get_Last Index:5 Line 5 (17:54:29):: | my() -Dev:3 Actual Dev:10003:1:1 | Actual Button Push:5 myGetLast Index:5 Line 6 (17:54:31):: ******************************************************************************* Line 7 (17:54:31):: |[HOLD:] Line 8 (17:54:31):: | GET_LAST() -Dev:3 Actual Dev:10003:1:1 | Actual Button Hold:5 Get_Last Index:5 Line 9 (17:54:31):: | Variable() -Dev:3 Actual Dev:10003:1:1 | Actual Button Hold:5 Get_Last Index:5 Line 10 (17:54:31):: | my() -Dev:3 Actual Dev:10003:1:1 | Actual Button Hold:5 myGetLast Index:5 Line 11 (17:54:31):: ############################################################################### Line 12 (17:54:31):: |[PUSH:] Line 13 (17:54:31):: | GET_LAST() -Dev:4 Actual Dev:10004:1:1 | Actual Button Push:6 Get_Last Index:6 Line 14 (17:54:31):: | Variable() -Dev:4 Actual Dev:10004:1:1 | Actual Button Push:6 Get_Last Index:6 Line 15 (17:54:31):: | my() -Dev:4 Actual Dev:10004:1:1 | Actual Button Push:6 myGetLast Index:6 Line 16 (17:54:32):: -=============================================================================- Line 17 (17:54:32):: |[RELEASE:] Line 18 (17:54:32):: | GET_LAST() -Dev:4 Actual Dev:10004:1:1 | Actual Button Release:6 Get_Last Index:6 Line 19 (17:54:32):: | Variable() -Dev:4 Actual Dev:10004:1:1 | Actual Button Release:6 Get_Last Index:6 Line 20 (17:54:32):: | my() -Dev:4 Actual Dev:10004:1:1 | Actual Button Release:6 myGetLast Index:6 Line 21 (17:54:32):: /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ Line 22 (17:54:33):: ******************************************************************************* Line 23 (17:54:33):: |[HOLD:] Line 24 (17:54:33):: | GET_LAST() -Dev:4 Actual Dev:10003:1:1 | Actual Button Hold:5 Get_Last Index:6 Line 25 (17:54:33):: | Variable() -Dev:4 Actual Dev:10003:1:1 | Actual Button Hold:5 Get_Last Index:6 Line 26 (17:54:33):: | my() -Dev:3 Actual Dev:10003:1:1 | Actual Button Hold:5 myGetLast Index:5 Line 27 (17:54:33):: ############################################################################### Line 28 (17:54:33):: |[PUSH:] Line 29 (17:54:33):: | GET_LAST() -Dev:4 Actual Dev:10004:1:1 | Actual Button Push:2 Get_Last Index:2 Line 30 (17:54:33):: | Variable() -Dev:4 Actual Dev:10004:1:1 | Actual Button Push:2 Get_Last Index:2 Line 31 (17:54:33):: | my() -Dev:4 Actual Dev:10004:1:1 | Actual Button Push:2 myGetLast Index:2 Line 32 (17:54:34):: -=============================================================================- Line 33 (17:54:34):: |[RELEASE:] Line 34 (17:54:34):: | GET_LAST() -Dev:4 Actual Dev:10004:1:1 | Actual Button Release:2 Get_Last Index:2 Line 35 (17:54:34):: | Variable() -Dev:4 Actual Dev:10004:1:1 | Actual Button Release:2 Get_Last Index:2 Line 36 (17:54:34):: | my() -Dev:4 Actual Dev:10004:1:1 | Actual Button Release:2 myGetLast Index:2 Line 37 (17:54:34):: /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ Line 38 (17:54:35):: ******************************************************************************* Line 39 (17:54:35):: |[HOLD:] Line 40 (17:54:35):: | GET_LAST() -Dev:4 Actual Dev:10003:1:1 | Actual Button Hold:5 Get_Last Index:2 Line 41 (17:54:35):: | Variable() -Dev:4 Actual Dev:10003:1:1 | Actual Button Hold:5 Get_Last Index:2 Line 42 (17:54:35):: | my() -Dev:3 Actual Dev:10003:1:1 | Actual Button Hold:5 myGetLast Index:5 Line 43 (17:54:37):: ******************************************************************************* Line 44 (17:54:37):: |[HOLD:] Line 45 (17:54:37):: | GET_LAST() -Dev:4 Actual Dev:10003:1:1 | Actual Button Hold:5 Get_Last Index:2 Line 46 (17:54:37):: | Variable() -Dev:4 Actual Dev:10003:1:1 | Actual Button Hold:5 Get_Last Index:2 Line 47 (17:54:37):: | my() -Dev:3 Actual Dev:10003:1:1 | Actual Button Hold:5 myGetLast Index:5 Line 48 (17:54:37):: ############################################################################### Line 49 (17:54:37):: |[PUSH:] Line 50 (17:54:37):: | GET_LAST() -Dev:3 Actual Dev:10003:1:1 | Actual Button Push:14 Get_Last Index:14 Line 51 (17:54:37):: | Variable() -Dev:3 Actual Dev:10003:1:1 | Actual Button Push:14 Get_Last Index:14 Line 52 (17:54:37):: | my() -Dev:3 Actual Dev:10003:1:1 | Actual Button Push:14 myGetLast Index:14 Line 53 (17:54:38):: -=============================================================================- Line 54 (17:54:38):: |[RELEASE:] Line 55 (17:54:38):: | GET_LAST() -Dev:3 Actual Dev:10003:1:1 | Actual Button Release:14 Get_Last Index:14 Line 56 (17:54:38):: | Variable() -Dev:3 Actual Dev:10003:1:1 | Actual Button Release:14 Get_Last Index:14 Line 57 (17:54:38):: | my() -Dev:3 Actual Dev:10003:1:1 | Actual Button Release:14 myGetLast Index:14 Line 58 (17:54:38):: /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ Line 59 (17:54:39):: ******************************************************************************* Line 60 (17:54:39):: |[HOLD:] Line 61 (17:54:39):: | GET_LAST() -Dev:3 Actual Dev:10003:1:1 | Actual Button Hold:5 Get_Last Index:14 Line 62 (17:54:39):: | Variable() -Dev:3 Actual Dev:10003:1:1 | Actual Button Hold:5 Get_Last Index:14 Line 63 (17:54:39):: | my() -Dev:3 Actual Dev:10003:1:1 | Actual Button Hold:5 myGetLast Index:5 Line 64 (17:54:39):: ############################################################################### Line 65 (17:54:39):: |[PUSH:] Line 66 (17:54:39):: | GET_LAST() -Dev:4 Actual Dev:10004:1:1 | Actual Button Push:2 Get_Last Index:2 Line 67 (17:54:39):: | Variable() -Dev:4 Actual Dev:10004:1:1 | Actual Button Push:2 Get_Last Index:2 Line 68 (17:54:39):: | my() -Dev:4 Actual Dev:10004:1:1 | Actual Button Push:2 myGetLast Index:2 Line 69 (17:54:40):: -=============================================================================- Line 70 (17:54:40):: |[RELEASE:] Line 71 (17:54:40):: | GET_LAST() -Dev:4 Actual Dev:10004:1:1 | Actual Button Release:2 Get_Last Index:2 Line 72 (17:54:40):: | Variable() -Dev:4 Actual Dev:10004:1:1 | Actual Button Release:2 Get_Last Index:2 Line 73 (17:54:40):: | my() -Dev:4 Actual Dev:10004:1:1 | Actual Button Release:2 myGetLast Index:2 Line 74 (17:54:40):: /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ Line 75 (17:54:41):: -=============================================================================- Line 76 (17:54:41):: |[RELEASE:] Line 77 (17:54:41):: | GET_LAST() -Dev:3 Actual Dev:10003:1:1 | Actual Button Release:5 Get_Last Index:5 Line 78 (17:54:41):: | Variable() -Dev:3 Actual Dev:10003:1:1 | Actual Button Release:5 Get_Last Index:5 Line 79 (17:54:41):: | my() -Dev:3 Actual Dev:10003:1:1 | Actual Button Release:5 myGetLast Index:5 Line 80 (17:54:41):: /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
As you can see, any use of the GET_LAST() function in a hold is a potential problem. I did this test because I am considering switching a bunch of my modules away from GET_LAST only because there are a few places where a HOLD event is used and I would rather be uniform in my approach. By using the 2 new functions, I can fairly quickly and easily replace my existing GET_LAST code.
Does anyone see any flaw in my testing or in my functions? I'd really hate to make the switch only to find some weird new problem I introduce
Yes, I could store BIC values in an array, but that sounds like more work than just using the replacement functions. It also seems like I might need to rework some of the code to deal with BIC data.
Jeff -
First, I wish we could declare local variables at the begining of BUTTON_EVENT that could be with in the scope of the entire BUTTON_EVENT and not just the individual PUSH, HOLD & RELEASE.
Like this!BUTTON_EVENT [dvTP_Arry,nAP_BtnArry] { LOCAL_VAR INTEGER nLastInPut ; PUSH: { //push code } HOLD[2,REPEAT]: { //hold code } RELEASE: { //release code } }Not being able to do this always seemed to bug me cuz now I need a global var where if this were possible I wouldn't.DEFINE_VARIABLE VOLATILE INTEGER nBTN ; BUTTON_EVENT [dvTP_Arry,nAP_BtnArry] { PUSH: { STACK_VAR CHAR cDevice[11] ; LOCAL_VAR INTEGER nLastInPut ; nBTN = GET_LAST(nAP_BtnArry) dvCurPushTP = BUTTON.INPUT.DEVICE ; cDevice = fnDEV_TO_STRING(dvCurPushTP) ; fnDebug("'Device:',cDevice,'-CHANNEL ',itoa(nAP_BtnArry[nBTN]),' Line-<',ITOA(__LINE__),'>'") ; if(nHold_In_Progress) { fnDoMSGDisplay(dvCurPushTP,'Please Wait ** System Busy',MSG_TYPE_WARNING,AP_UNIT_0) ; nHold_MSG_Sent = 1 ; } else } HOLD[2,REPEAT]: { nHold_In_Progress = 1 ; SELECT { ACTIVE (nBTN >= BTN_VOL_UP && nBTN <= BTN_VOL_RMP_DWN): RELEASE: { nHold_In_Progress = 0 ;
I prefer the blocking method. Do the GET_LAST in the PUSH and set a blocking var in the hold that prevents the PUSH code blocking from running and changing values while the HOLD is in progress for the duration of the HOLD.
Yes, I put in a "Please Wait" pop up message just in case and I don't care if any one dis-agrees with this approach (Paul) but my thinking is that it will never get called any way.
Just for clarification, does GET_LAST hold the last value for each event table or does GET_LAST return the last value across the entire system? In other words if I use a GET_LAST in a hold or wait for BUTTON_EVENT [dvTP_Arry,nAP_BtnArry] will it only return the last channel push in that EVENT_TABLE or will it use the last channel pushed any where and see what index that channel number may be in nAP_BtnArry. -
DEFINE_VARIABLE VOLATILE INTEGER nBTN ; BUTTON_EVENT [dvTP_Arry,nAP_BtnArry] { PUSH: { STACK_VAR CHAR cDevice[11] ; LOCAL_VAR INTEGER nLastInPut ; nBTN = GET_LAST(nAP_BtnArry) dvCurPushTP = BUTTON.INPUT.DEVICE ; cDevice = fnDEV_TO_STRING(dvCurPushTP) ; fnDebug("'Device:',cDevice,'-CHANNEL ',itoa(nAP_BtnArry[nBTN]),' Line-<',ITOA(__LINE__),'>'") ; if(nHold_In_Progress) { fnDoMSGDisplay(dvCurPushTP,'Please Wait ** System Busy',MSG_TYPE_WARNING,AP_UNIT_0) ; nHold_MSG_Sent = 1 ; } else } HOLD[2,REPEAT]: { nHold_In_Progress = 1 ; SELECT { ACTIVE (nBTN >= BTN_VOL_UP && nBTN <= BTN_VOL_RMP_DWN): RELEASE: { nHold_In_Progress = 0 ;
I prefer the blocking method. Do the GET_LAST in the PUSH and set a blocking var in the hold that prevents the PUSH code blocking from running and changing values while the HOLD is in progress for the duration of the HOLD.
....
Just for clarification, does GET_LAST hold the last value for each event table or does GET_LAST return the last value across the entire system? In other words if I use a GET_LAST in a hold or wait for BUTTON_EVENT [dvTP_Arry,nAP_BtnArry] will it only return the last channel push in that EVENT_TABLE or will it use the last channel pushed any where and see what index that channel number may be in nAP_BtnArry.
I believe the way you have the Push: Event defined here, there is a potential to run into the problem I described. The reason is because you are executing the GET_LAST function every time the button is pushed and this portion of the code is not conditional based upon the lock out. If you moved the GET_LAST inside the ELSE portion of the IF(nHold_In_Progress) ... ELSE statement, this should work properly.
As for the GET_LAST applying to the event or the system, I will see if I can test that out tomorrow.
Jeff -
Spire_Jeff wrote:
You are so very right! Sometimes we see but are still blind.I believe the way you have the Push: Event defined here, there is a potential to run into the problem I described. -
Just for clarification, does GET_LAST hold the last value for each event table or does GET_LAST return the last value across the entire system? In other words if I use a GET_LAST in a hold or wait for BUTTON_EVENT [dvTP_Arry,nAP_BtnArry] will it only return the last channel push in that EVENT_TABLE or will it use the last channel pushed any where and see what index that channel number may be in nAP_BtnArry.
Now that I've had some sleep, I think I understand what you are asking. GET_LAST monitors the each array separately (I think). So, if you have the following:INTEGER nUSER_BTN_TEST1[] = {21,22,23,24,25,26,27,28,29,30,31,32,33,34,35} INTEGER nUSER_BTN_TEST2[] = {41,42,43,44,45,46,47,48,49,50,51,52,53,54,55} INTEGER nUSER_BTN_TEST3[] = {21,22,23,24,25,26,27,28,29,30,31,32,33,34,35, 36,37,38,39,40,41,42,43,44,45,46,47,48,49,50, 51,52,53,54,55,56,57,58,59,60,61,62,63,64,65, 66,67,68,69,70,71,72,73,74,75,76,77,78,79,80 } . . . . BUTTON_EVENT[dvTPs,nUSER_BTN_TEST3]{ PUSH:{ SEND_STRING 0,"'Button ',ITOA(BUTTON.INPUT.CHANNEL),' was pushed! ********************'"; } RELEASE:{ SEND_STRING 0,"'Button ',ITOA(BUTTON.INPUT.CHANNEL),' was released! ******************'"; } } BUTTON_EVENT[dvTPs,43]{ PUSH:{ SEND_STRING 0,"'Button 43: GET_LAST(Array1) = ',ITOA(GET_LAST(nUSER_BTN_TEST1))"; SEND_STRING 0,"'Button 43: GET_LAST(Array2) = ',ITOA(GET_LAST(nUSER_BTN_TEST2))"; } } BUTTON_EVENT[dvTPs,32]{ PUSH:{ SEND_STRING 0,"'Button 32(Instance 1): GET_LAST(Array1) = ',ITOA(GET_LAST(nUSER_BTN_TEST1))"; SEND_STRING 0,"'Button 32(Instance 1): GET_LAST(Array2) = ',ITOA(GET_LAST(nUSER_BTN_TEST2))"; } } BUTTON_EVENT[dvTPs,nUSER_BTN_TEST1]{ PUSH: SEND_STRING 0,"'nUSER_BTN_TEST1: GET_LAST() = ',ITOA(GET_LAST(nUSER_BTN_TEST1))"; } BUTTON_EVENT[dvTPs,nUSER_BTN_TEST2]{ PUSH: SEND_STRING 0,"'nUSER_BTN_TEST2: GET_LAST() = ',ITOA(GET_LAST(nUSER_BTN_TEST2))"; } BUTTON_EVENT[dvTPs,32]{ PUSH:{ SEND_STRING 0,"'Button 32(Instance 2): GET_LAST(Array1) = ',ITOA(GET_LAST(nUSER_BTN_TEST1))"; SEND_STRING 0,"'Button 32(Instance 2): GET_LAST(Array2) = ',ITOA(GET_LAST(nUSER_BTN_TEST2))"; } } BUTTON_EVENT[dvTPs,44]{ PUSH:{ SEND_STRING 0,"'Button 44: GET_LAST(Array1) = ',ITOA(GET_LAST(nUSER_BTN_TEST1))"; SEND_STRING 0,"'Button 44: GET_LAST(Array2) = ',ITOA(GET_LAST(nUSER_BTN_TEST2))"; } } BUTTON_EVENT[dvTPs,39]{ PUSH:{ SEND_STRING 0,"'Button 39: GET_LAST(Array1) = ',ITOA(GET_LAST(nUSER_BTN_TEST1))"; SEND_STRING 0,"'Button 39: GET_LAST(Array2) = ',ITOA(GET_LAST(nUSER_BTN_TEST2))"; } }
Here is the output to demonstrate the GET_LAST Functionality:Line 1 (11:00:34):: Button 21 was pushed! ******************** Line 2 (11:00:34):: nUSER_BTN_TEST1: GET_LAST() = 1 Line 3 (11:00:34):: Button 21 was released! ****************** Line 4 (11:00:41):: Button 25 was pushed! ******************** Line 5 (11:00:41):: nUSER_BTN_TEST1: GET_LAST() = 5 Line 6 (11:00:41):: Button 25 was released! ****************** Line 7 (11:00:47):: Button 32 was pushed! ******************** Line 8 (11:00:47):: Button 32(Instance 1): GET_LAST(Array1) = 12 Line 9 (11:00:47):: Button 32(Instance 1): GET_LAST(Array2) = 0 Line 10 (11:00:47):: nUSER_BTN_TEST1: GET_LAST() = 12 Line 11 (11:00:47):: Button 32(Instance 2): GET_LAST(Array1) = 12 Line 12 (11:00:47):: Button 32(Instance 2): GET_LAST(Array2) = 0 Line 13 (11:00:47):: Button 32 was released! ****************** Line 14 (11:00:57):: Button 41 was pushed! ******************** Line 15 (11:00:57):: nUSER_BTN_TEST2: GET_LAST() = 1 Line 16 (11:00:57):: Button 41 was released! ****************** Line 17 (11:01:03):: Button 44 was pushed! ******************** Line 18 (11:01:03):: nUSER_BTN_TEST2: GET_LAST() = 4 Line 19 (11:01:03):: Button 44: GET_LAST(Array1) = 12 Line 20 (11:01:03):: Button 44: GET_LAST(Array2) = 4 Line 21 (11:01:03):: Button 44 was released! ****************** Line 22 (11:01:11):: Button 32 was pushed! ******************** Line 23 (11:01:11):: Button 32(Instance 1): GET_LAST(Array1) = 12 Line 24 (11:01:11):: Button 32(Instance 1): GET_LAST(Array2) = 4 Line 25 (11:01:11):: nUSER_BTN_TEST1: GET_LAST() = 12 Line 26 (11:01:11):: Button 32(Instance 2): GET_LAST(Array1) = 12 Line 27 (11:01:11):: Button 32(Instance 2): GET_LAST(Array2) = 4 Line 28 (11:01:11):: Button 32 was released! ****************** Line 29 (11:01:25):: Button 31 was pushed! ******************** Line 30 (11:01:25):: nUSER_BTN_TEST1: GET_LAST() = 11 Line 31 (11:01:25):: Button 31 was released! ****************** Line 32 (11:01:28):: Button 32 was pushed! ******************** Line 33 (11:01:28):: Button 32(Instance 1): GET_LAST(Array1) = 12 Line 34 (11:01:28):: Button 32(Instance 1): GET_LAST(Array2) = 4 Line 35 (11:01:28):: nUSER_BTN_TEST1: GET_LAST() = 12 Line 36 (11:01:28):: Button 32(Instance 2): GET_LAST(Array1) = 12 Line 37 (11:01:28):: Button 32(Instance 2): GET_LAST(Array2) = 4 Line 38 (11:01:29):: Button 32 was released! ****************** Line 39 (11:03:16):: Button 39 was pushed! ******************** Line 40 (11:03:16):: Button 39: GET_LAST(Array1) = 12 Line 41 (11:03:16):: Button 39: GET_LAST(Array2) = 4 Line 42 (11:03:16):: Button 39 was released! ****************** Line 43 (11:03:21):: Button 38 was pushed! ******************** Line 44 (11:03:21):: Button 38 was released! ****************** Line 45 (11:03:28):: Button 39 was pushed! ******************** Line 46 (11:03:28):: Button 39: GET_LAST(Array1) = 12 Line 47 (11:03:28):: Button 39: GET_LAST(Array2) = 4 Line 48 (11:03:28):: Button 39 was released! ******************
One thing that I found interesting is that GET_LAST seems to update the value before it goes through the events. If you look at the events for button 32, the first event is not declared using the array, it is just a constant. Given this fact, I thought there was a chance that GET_LAST might not update until it hit an event declared with the array. It seems that I was wrong
Hope this helps someone else, but it has definitely helped my understanding of GET_LAST() .... more importantly, why I need to stop using it in some of my modules
Jeff
Categories
- All Categories
- 2.5K AMX General Discussion
- 922 AMX Technical Discussion
- 514 AMX Hardware
- 502 AMX Control Products
- 3 AMX Video Distribution Products
- 9 AMX Networked AV (SVSI) Products
- AMX Workspace & Collaboration Products
- 3.4K AMX Software
- 151 AMX Resource Management Suite Software
- 386 AMX Design Tools
- 2.4K NetLinx Studio
- 135 Duet/Cafe Duet
- 248 NetLinx Modules & Duet Modules
- 57 AMX RPM Forum
- 228 MODPEDIA - The Public Repository of Modules for Everyone
- 943 AMX Specialty Forums
- 2.6K AMXForums Archive
- 2.6K AMXForums Archive Threads
- 1.5K AMX Hardware
- 432 AMX Applications and Solutions
- 249 Residential Forum
- 182 Tips and Tricks
- 146 AMX Website/Forums

