device arrays and functions
dnahman
Posts: 28
Hi there,
I seem to get intermittent results passing DEV arrays to functions depending on how
they are defined.
Suppose I have the following program:
DEFINE_VARIABLE
DEV vdTP = {TP1, TP2, TP3}
DEFINE_FUNCTION myFunc(DEV d[], integer b, char msg[]) {
SEND_COMMAND d, "'Text', ITOA(b), '-', msg"
}
DATA_EVENT[vdTP] {
STRING: {
stack_var DEV myDevArray[1]
myDevArray[1] = Data.Device
myFunc(vdTP, 100, 'message for 100')
myFunc(myDevArray, 101, 'message for 101')
}
}
In this case, 'message for 100' would appear on button 100 on all panels, but 'message for 101' wouldn't appear on the active Data.Device.
The only reason I'm using the stack_var myDevArray is so I can use the same functions for single panels and groups of panels. If there is another way to pass a single device or a device array to a user defined function (so the function can mimic the SEND_COMMAND feature) I'd happily use that).
Thanks for any help,
David
I seem to get intermittent results passing DEV arrays to functions depending on how
they are defined.
Suppose I have the following program:
DEFINE_VARIABLE
DEV vdTP = {TP1, TP2, TP3}
DEFINE_FUNCTION myFunc(DEV d[], integer b, char msg[]) {
SEND_COMMAND d, "'Text', ITOA(b), '-', msg"
}
DATA_EVENT[vdTP] {
STRING: {
stack_var DEV myDevArray[1]
myDevArray[1] = Data.Device
myFunc(vdTP, 100, 'message for 100')
myFunc(myDevArray, 101, 'message for 101')
}
}
In this case, 'message for 100' would appear on button 100 on all panels, but 'message for 101' wouldn't appear on the active Data.Device.
The only reason I'm using the stack_var myDevArray is so I can use the same functions for single panels and groups of panels. If there is another way to pass a single device or a device array to a user defined function (so the function can mimic the SEND_COMMAND feature) I'd happily use that).
Thanks for any help,
David
0
Comments
After the declaration of myDevArray and before you call myFunc, add the line:
SET_LENGTH_ARRAY(myDevArray,1)
and you should get the results you are looking for.
Woo hoo, big philosophical question. I hope I can answer it, let me try.
I refer to simple functions that do "not very much" as "wrapper" functions - they wrap around basic language / environment / close to the metal functions which seem simple but can get messy:
(a) It doesn't really cost very much to use a wrapper function instead of native code. You have to write the function as well, but you only have to write it once, and you can use it endlessly, year after year, confident in the knowledge that it will work.
(b) The name of the function can reflect what you are achieving, instead of what the code is literally doing; and it can use your consistent self-documenting high-level terminology, instead of the abbreviated low-level terminology that the compiler writer invented.
(c) The function can easily be changed do different things in different circumstances, and yet be called in the same way without the application code having to think about it or be aware of it.
eg G3 and G4 touchpanels have a number of arbitrarily different commands; the day you have to mix a legacy G3 panel into a G4 environment is the day you will be grateful that all touchpanel commands go through one or a finite set of wrappers.
(d) The function can be laden with self-checks, assertion checks, debug readouts. Just comment them out when you don't need them, and comment them in when you need to see what's happening.
Add them when you need them - you only have to write them once, and they generally don't need debugging, because they are themselves calls to reliable wrapper functions.
Assertion checking isn't much use post commissioning, but during the debug phase it's treasure. Now you can write your function calls as slapdashedly as you like, knowing that the function checks will pick up your blunders. You may feel that the processor load is uncalled for - and you may be right - but it's tiny.
You can even pass a string to the wrapper on every invocation, which documents the purpose of the call or the routine that is calling it. The string is passed by reference, so it has zero processor load (yes really: it costs no significant processor time to pass a literal string to a function), but in a busy event-driven system, it is gold when you are trying to debug a problem.
Granted, a very simple example, but if you're saying you'd rather use the function over the SEND_COMMAND line - I'm just not seeing the benefit except that it spells out what you're trying to do. The *only* benefit I could possibly ever see is wrapping (as said above) commands used for G3 & G4 panels. I know that it's a PITA to mix two different generation panels on a job, been there, done that.
I use functions when I have a portion of code that I'm going to over and over again, such as string manipulation (only the STRING_REPLACE and STRING_REMOVE functions found on the forum a while back), system management, pop-up/page tracking, etc.
It all boils down to style though and nothing is wrong with either way. But I would *guess* that it would drive a programmer at AMX nuts going through someone's code that references a function everytime you want to open up a page, or add two numbers together. But . . . it's all according to your style and ultimately doesn't matter. The main goal in every system is to make sure it works everytime the client hits a button.
As I have always said there are as many ways to program as there are programmers.
Where are the programming police when you need them?
You got to love the passion on this forum. Thanks for all the contributions.
Thanks for your reply. As you pointed out, the set_length_array is absolutely necessary.
But the other odd thing I have discovered is that when you populate myDevArray[1] with Button.Input.Device, or Data.Device it doesn't work, but if you populate it with a 10003:1:1 literal or a dvTP constant of the same value it does. But it turns out this has nothing to do with the DEV array, but something odd with Data.Device.
In my ONLINE event for the touchpanel, I verified that:
send_command dvTP, 'Text255-mytext' works,
but
send_command Data.Device, 'Text255-mytext' doesn't.
I've got to be missing something really simple here, but I can't figure out what. I'm sure this has worked for me in the past. I'm using v2.3.0.0 of the compiler.
Thanks for any insights!
--david
I used to have a function for when I wanted to send a message for all touchpanels in the system. Then when I dried off behind the ears, I realized NetLinx builds you in a quick and easy to do this: So I was able to just change my code in one place. A case where having a wrapper function made life much easier down the road. Even jjames' fnOpenPopup seems silly right now, but if it's being written inside a module, then the user could enter a check into this function to make sure the device the module is supposed to control is being used, before popping up the page in question. If only the MAX UI programmers had done this!
I was able to get your original example to work via the STRING: handler after the SET_LENGTH_ARRAY command was added. I was also able to get the ONLINE event to work correctly for DATA.DEVICE.
Can you post the offending code? I don't see anything wrong with what you've posted so far.
Joe
I found the problem -- I had put a wait (10) in my online handler while trying to debug the original problem with the stack_var array (which you pointed out in your first reply). I think what was happening was the value of Data.Device was getting corrupted by the time that the body of the wait(10) got executed, and the send_command was going to the wrong place.
So: same symptom, different cause from the original post.
I'd love it if AMX implemented something akin to a fork() or a POSIX pthread. I've learned and forgotten this same lesson using waits more times than I can count.
Thanks for your help,
David