Limitations on Button Array in GET_LAST()
Reese Jacobs
Posts: 347
For certain Netlinx modules like satellite receivers and satellite radio tuners, I use button arrays and GET_LAST() for channel handling. In some cases where I am supporting a large number of channels such as DirecTV or Sirius radio, the button channel array can be quite large (70-80 entries).
VOLATILE INTEGER nChannelBtns[80] =
{
1, 2, 3, 4, 5, ....
}
BUTTON_EVENT[dv, nChannelBtns]
}
PUSH:
{
......
}
}
My question is whether or not there is a real or artifical limitation on the length of the button channel array (number of entries). I have seen several cases where button entries beyond entry #60 do not work. This is not always the case and I have not been able to determine the precise circumstances but it seems there is some undocumented limit on the number of entries that can be handled properly. If I split the button list into two lists with each list using its own event handler, it works just fine.
Also, I have created a standalone test program with a button array containing 100 entries and it works fine. However, in a large Netlinx program with numerous modules, I have encountered this button channel array limitation on several occasions.
Anyone else run into this issue? Thanks in advance.
VOLATILE INTEGER nChannelBtns[80] =
{
1, 2, 3, 4, 5, ....
}
BUTTON_EVENT[dv, nChannelBtns]
}
PUSH:
{
......
}
}
My question is whether or not there is a real or artifical limitation on the length of the button channel array (number of entries). I have seen several cases where button entries beyond entry #60 do not work. This is not always the case and I have not been able to determine the precise circumstances but it seems there is some undocumented limit on the number of entries that can be handled properly. If I split the button list into two lists with each list using its own event handler, it works just fine.
Also, I have created a standalone test program with a button array containing 100 entries and it works fine. However, in a large Netlinx program with numerous modules, I have encountered this button channel array limitation on several occasions.
Anyone else run into this issue? Thanks in advance.
0
Comments
Because of how I design my NetLinx code, I routinely have the lower 127 buttons be "generic" buttons that simply send the underlying I/R code for the controlled device based on the button number (i.e. button #1 on the touchpanel pulses I/R code #1 on the associated DVD player, say, to issue a PLAY command). This allows me to "divorce" I/R signals from my mainline code, ultimately controlled via the touchpanel. For that reason, I can add totally new buttons without touching my mainline code.
I routinely do things like:
Integer GenericChans[] =
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
14, 15, 16, 17, 18, 19, 20, 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, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
118, 119, 120, 121, 122, 123, 124, 125, 126, 127
};
And then, later in code:
Button_Event [MVP_SDTivo,GenericChans] // DirecTivo PULSE
{
PUSH: {
if (DoButtonPrologue (Button))
Pulse [DTIVO,Button.Input.Channel];
}
}
This works just dandy. And my program is huge; I have probably 8-10 different modules, compiling to over 500k of code.
I did find an issue here (years ago). Apparently, the NetLinx master code has some compiled constants that create limits (something like 1000 buttons or something), but only if you do it the way I'm doing it. I believe I used to do it differently (where I had a bunch of DEVCHANs), and that created issues. So the AMX firmware developer I was chatting with suggested I do it the way I specified above, so I've done it that way without problems.
Be aware that, in the above code, MVP_SDTivo is itself a DEV array. But that's okay.
Hope this helps,
-- Jeff
Jeff,
Thanks for the feedback. Like you, I do a lot of direct mapping using channel arrays so I can emit IR codes by assigning buttons to the TP without modifying code. As I said, some of the button arrays work great with large numbers of entries but some do not. I have not quite determined the key to the ones that fail. I am using about 18 modules directly and multiple copies of the DMS module (22 in all) with well over 1 Mbyte is code but I don't know if this makes a difference or not.
I knew that DEVCHANs could be an issue and that it is well documented in the Tech Notes that GET_LAST() does not work reliably with LEVELS particularly DEVLEV arrays although I have seen the same problem occur with LEVEL arrays as well. I generally code LEVEL_EVENT handlers that use GET_LAST() to check the return value and if 0 (meaning GET_LAST() failed), then a search of the DEV array is performed to determine the specific device that caused the event. This limitation is from an old Tech Note and I am not sure when it will get fixed in Netlinx - if ever. However, this is a different problem from the one I am having with the button arrays since in the button array case, button events are simply not generated for button entries beyond a certain point in the array.
Thanks again for the feedback - I will continue to try to narrow down the circumstances under which the button channel arrays fail.
Reese
DEVCHAN pairs that end up above that number just won't work.
Well, that certainly explains the problem since a few of the BUTTON_EVENTs that are failing for certain entries have between 15 and 20 devices which means only about 50-60 buttons will work to keep the total under 1024.
Thanks for the info -- this should really be documented somewhere such as a Tech Note or in the Netlinx programming manual or perhaps it is covered somewhere and I just never paid attention to it. I knew there was a limitation on the total number of BUTTON_EVENT combinations in the system but I did not recall seeing the 1024 limitation on a single event handler invocation. Thanks again.
Dave,
I did the same thing when I first discovered the problem not knowing at the time the specific limitation that was causing the problem. I am since very careful to design the event handlers keeping the limitation in mind. I guess this is something we need to be aware of particularly in large systems or applications where lots of devices or buttons are used.
With the new REBUILD_EVENT() Netlinx command available soon (if not already), I wonder if Netlinx will throw a real-time exception if the limit is exceeded? The compiler certainly does not provide a compile-time warning or error currently even though the tables for the event handlers are built at compile time.
Thanks for the input.
Reese
I guess REBUILD_EVENT underlying code is not new, it used to be called by the master after define start code had run for all modules. REBUILD_EVENT is just a way to call said native code from NetLinx code.
Which makes it unlikely some runtime exception will occur, given it's the same native code which does not throw any today!
And the compiler cannot give a warning because it does not know at compile time what the arrays will contain (it is execution dependant).
REBUILD EVENT works today with the latest firmware.
Fred
1. The number of BUTTON_EVENT, CHANNEL_EVENT, or LEVEL_EVENT triggers is limited to 4000 per event statement.
Notice this is the same number of possible channels per port on a G4 panel.
2. The number of effective DEV's is limited to 50, even if the DEV array is larger.
3. The number of effective INTEGER's is limited to 4000 divided by the length of the DEV array - even if the DEV array is larger than 50.
Note this means if you declare an event with a DEV array with 100 devices, you'll be limited to 4000/100 = 40 buttons (or channels or levels) per device.
4) If you try to SET_VIRTUAL_LEVEL_COUNT(<vdv>,5000) to more than a handful of virtual devices, your master won't boot. Proabably not worth worrying about as G4 devices are limited to 600 levels per port.
You folks really need to look into the GET_LAST() function.
I find it works flakey at best. (Sorry for the heavy tech explanation.) lol
Today for example I had a small program which had been working perfectly.
I added a more code and suddenly I had controls which ceased to work correctly. (none of which I had been working on)
What really got me was how the problem manifested itself.
Here is the code in question (hope it formats out ok)
// Define Varable section
// Spectra-III Controls
INTEGER chSpectra3Control_btns[] =
{
321, // Iris-Open
322, // Iris-Close
323, // Zoom-In
324, // Zoom-Out
325, // Focus-Far
326, // Focus-Near
327, //
328, //
329, //
330, //
331, // Store Preset
332 // Clear Preset
}
//
// Define Events Section
BUTTON_EVENT [vdvCommonTP,chMoonDanceControl_btns]
{
PUSH:
{
STACK_VAR INTEGER nIndexNumber
LOCAL_VAR CHAR sData[11]
nIndexNumber = (BUTTON.INPUT.CHANNEL - 420)
SELECT
{
ACTIVE(nIndexNumber = 1): // Momentary Wiper
{
// Feedback to TP
TO[vdvCommonTP,PUSH_CHANNEL]
// Turning on Wiper
sMoonDanceWiperMode = "$34"
sData = "$30,$30,$30,$30,sMoonDanceWiperMode,$30,$30,$30,$30,$30"
Call 'Build Moondance String' (1,ManualControl,sData)
WAIT 30
{
// Turning Off wiper
sMoonDanceWiperMode = "$30"
sData = "$30,$30,$30,$30,sMoonDanceWiperMode,$30,$30,$30,$30,$30"
Call 'Build Moondance String' (1,ManualControl,sData)
}
}
ACTIVE(nIndexNumber = 2): // Proportional Pan/Tilt
{
// Feedback to TP
[vdvCommonTP,PUSH_CHANNEL] = ![vdvCommonTP,PUSH_CHANNEL]
// IF Proportional Pan/Tilt is on
IF([vdvCommonTP,PUSH_CHANNEL])
sProportionalMode = "$38"
ELSE
sProportionalMode = "$30"
}
ACTIVE(nIndexNumber = 3): // High Speed Zoom Toggle
{
// Feedback to TP
[vdvCommonTP,PUSH_CHANNEL] = ![vdvCommonTP,PUSH_CHANNEL]
// IF High Speed Zoom is ON
IF([vdvCommonTP,PUSH_CHANNEL])
sZoomSpeed = "$32"
ELSE
sZoomSpeed = "$30"
}
ACTIVE(nIndexNumber = 4): // Normal/Digital Zoom Toggle
{
// Feedback to TP
[vdvCommonTP,PUSH_CHANNEL] = ![vdvCommonTP,PUSH_CHANNEL]
// IF Proportional Digital Zoom mode is on
IF([vdvCommonTP,PUSH_CHANNEL])
sZoomMode = "$32"
ELSE
sZoomMode = "$30"
}
ACTIVE(nIndexNumber = 5): // Zoom In
{
// Feedback to TP
TO[vdvCommonTP,PUSH_CHANNEL]
sData = "$32,$30,$33,sZoomSpeed,$30,sZoomMode,$30,$30,$30,$30"
Call 'Build Moondance String' (1,ManualControl,sData)
}
ACTIVE(nIndexNumber = 6): // Zoom Out
{
// Feedback to TP
TO[vdvCommonTP,PUSH_CHANNEL]
sData = "$33,$30,$33,sZoomSpeed,$30,sZoomMode,$30,$30,$30,$30"
Call 'Build Moondance String' (1,ManualControl,sData)
}
ACTIVE(nIndexNumber = 11): // Store Preset #
{
// Toggling Button State for both Feedback and used as a flag
[vdvCommonTP,PUSH_CHANNEL] = ![vdvCommonTP,PUSH_CHANNEL]
}
ACTIVE(nIndexNumber = 12): // Clear Preset #
{
// Toggling Button State for both Feedback and used as a flag
[vdvCommonTP,PUSH_CHANNEL] = ![vdvCommonTP,PUSH_CHANNEL]
}
}
}
Look at index 2
Its a simple toggle.
Yet what would happen is when the button was pushed, the button would toggle on like it should however the rest of the code would fail as though the button was not in the "ON" state.
If the button was pressed again it would not toggle off.
From this point on the button remained in the ON state.
When I replaced the GET_LAST() with
nIndexNumber = (BUTTON.INPUT.CHANNEL - 420)
It worked correctly again.
The concept of the GET_LAST function is great, if only it would be reliable.
Your example is slightly confusing as you indicate the content of the "chSpectra3Control_btns" while using "chMoonDanceControl_btns" in the BUTTON_EVENT and it also does not contain GET_LAST (one learns at the end it was replaced by (BUTTON.INPUT.CHANNEL - 420). But anyway...
I don't know for sure this applies to the problem you're experiencing. I have had good success with GET_LAST.
One thing for sure is that I would not use PUSH_CHANNEL in an event. You can't assume that when the BUTTON_EVENT is triggered, the channel is on or PUSH_CHANNEL is correct.
The Netlinx doc states "The [PUSH_CHANNEL] system variable contains the number of the channel that was just turned on due to an input change. The value remains valid for one pass through mainline". I don't know what this means in an event, but I do *not* read PUSH_CHANNEL is set to the button that triggered a BUTTON_EVENT while the event is handled.
Events ARE queued and handled one by one. If there are many events (or any other thing happening on the master), their treatment may be delayed. That means that WHEN the button_event code runs, the button may NOT be pushed any longer (i.e. [TP, channel] may be off, and I guess [TP, push_channel] also). With a very lightly loaded master and simple code, that almost never happens. But the higher the load the higher the risk the master is lagging behind reality.
I am not too sure how that could explain the behaviour you experienced, but I'd replace PUSH_CHANNEL by BUTTON.INPUT.CHANNEL which is guaranteed to be the value the event is about. I'd also add an ACTIVE(1) clause to the SELECT statement that sends some string to 0:0 so that you can have a log of the fact GET_LAST is behaving funky.
And finally in your case (supposing chMoonDanceControl_btns is similar to chSpectra3Control_btns), there is little benefit in using GET_LAST or do the nIndexNumber trick as you could simply check for ACTIVE(BUTTON.INPUT.CHANNEL == 421), ACTIVE(BUTTON.INPUT.CHANNEL == 422) and so forth. I can't see any advantage here of using GET_LAST, but maybe I am missing something.
Hope this helps
Fred
Since BUTTON.INPUT contains both the device and channel, the toggle could be written:
[BUTTON.INPUT] = ![BUTTON.INPUT]
I wrote a small test program to see if I could find the problem - it seems to run just fine. It's in the attached AXW.
Note, since I'm using GET_LAST on an INTEGER array of buttons, I use that for the toggle statement instead of BUTTON.INPUT. Just a matter of style...
I started this thread some time back and since its inception we have had some good postings outlining the limitations on DEV and Button/Channels arrays used for event processing.
I decided today to write a program that would settle the matter for me and I thought a post was in order since my results differ somewhat from what has been previously posted. My test program dynamically creates a DEV array consisting of 1-200 touchpanels along with a Button array consisting of from 1-400 buttons. The number of devices and buttons can be set at compile time. When the program is loaded and run, a TIMELINE continually executes DO_PUSH() statements to trigger the BUTTON_EVENT handler for all combinations of entries in the DEV/Button arrays. In turn, the BUTTON_EVENT tracks the largest touchpanel, the largest button, and the largest button on the largest touchpanel that successfully triggers the BUTTON_EVENT.
Today, I tested the program on firmware level 137 using REBUILD_EVENT() once the event triggers had been initialized. I played with many different combinations of touchpanel devices (20-200) along with button array sizes ranging from 20 to 400. In *all* cases, on this firmware level, the results were the same:
- The maximum number of triggers is limited to 4000 (device/button combinations)
- There is no upper limit on the number of devices (at least up to 200 work fine)
- The number of buttons/channels supported can be determined by dividing the number of devices into 4000 (round down to the next integer for the number of buttons/channels)
Previously, it had been documented that 50 was the upper limit on the number of devices but in actuality this is not the case. This seems to contradict Tech Note 383 from 11/04 and it also seems to contradict some testing done by Guy so I admit the results were surprising. It appears that the number of devices supported is considerably higher but that the trigger count of 4000 is correct at least on firmware level 137. The results may be different on another firmware level. I also utilized static device and button arrays to ensure that REBULD_EVENT() was not impacting the results and the results were identical. Since the results were different from what had been published previously, I thought I would update the thread. If anyone is interested in the test program, the workspace is attached (turn on Netlinx Diagnostics in Studio).
Reese
Perhaps that was the reason for the limit of 50 devices that I found???
Guy,
That's an interesting thought - I was originally going to run my test using virtual devices but decided instead to use real devices even though I obviously do not have 200 touchpanels to connect to the system. I will redo the test later this morning using virtual devices to see if that makes a difference and let you know. It would be good to have definitive information on the limitations for all device types. Also, I only tested BUTTON_EVENTs so I might extend the test later to CHANNEL_EVENTs to see if they behave consistently. Thanks,
Reese
Guy,
I modified the test program to allow the use of real devices or virtual devices for the BUTTON_EVENT test. I also added a test case for CHANNEL_EVENTs using the virtual devices as well as a DATA_EVENT test. Unless it is related to firmware revisions, I can't explain the differing results. In all of my tests, there does not appear to be a limitation on the number of the DEVs supported (real or virtual). Granted, I only tested to a maximum of 200 devices but that is a significant number - more than most will use in an event handler. I found that you could always predict the number of buttons or channels that would trigger an event by dividing the DEV count into 4000 (rounding down). I did not bump up against a limitation on DEVs at least in the 1-200 device range.
The DATA_EVENT handler also performed as expected providing an ONLINE event for all 200 devices when declared in a DEV array. There does not seem to be a problem with this event type either.
Anyway, I have re-posted the workspace with the modified test program if you are interested. Unless I made an egregious programming error which is always possible, I don't seem to be able to create the 50 device limit. The results do seem very predictable however and this is the most important thing to me. Having the test program will enable me to identify any differences that might exist in this area between firmware releases.
Thanks,
Reese