Dynamic variables
Petar
Posts: 14
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:
In 'fnPOWER" I want to do the following:
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.
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.
0
Comments
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:
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.
Small correction. get_last does work anywhere, but it is recommended to be used in the button event only.
Paul
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.
Jeff
Okay . . . to argue back . . . .
all I read was blah-blah-blah-blah-blah-blah.
"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
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.
http://amxforums.com/showthread.php?t=1670&highlight=get_last+level+events
Joe Hebert wrote:
I can't say I've tried it so I don't know how valid this is, Joe, what's the verdict?
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:
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:
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.
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?
Paul
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.
Can you give an example of where? Just curious.
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.
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.
You doubt with much certainty? Is that even possible?
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.
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:
Personally I would rather write it like this:
Well, to answer a question in the begining:
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
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: 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: 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?
This is what I meant with the WAITs; you'd simply do this:
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.
I'm one of those pessimists that think it's the end of the world, don't get me started. Haha!
If as you say that all the WAITs are cancelled except for the last one then if you have the following code:
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.
Just a technicality... but you could also make it a variable as well.
VOLATILE INTEGER nSourceButtons[] = {1,2,3,4,5}
What you need is to create a DEV array of all your panels, and a matching DEV array of your actual devices.
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.
and make it shorter.
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:
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.
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);
}
}
}
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
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.)
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.