REBUILD_EVENT()
vining
Posts: 4,368
Since the discussion a couple of days ago I decided to run some tests cuz R_E is still a little fuzzy. In the main code I use one button event that utilizes defined UIs and the catch all 0 just to verify the push and this event isn't part of the test.
if we start with arrays that aren't initialized:
if you init the arrays and set the lengths in the main code but only rebuild_event in the module the module button event will fire but the main's won't.
Now if you take that same define_start section and init the arrays, set the lengths but don't rebuild in that section but then create another array and in a different define_start intit the array, set the length but in this code section rebuild the events the event table are re-built for both the old and new array even though the rebuild event was enclosed in braces. The instructions state that only items used in events enclosed in the braces are rebuilt but it appears everything in the main is. The module still won't work until rebuild event is called in the module.
So basically rebuild event isn't limited to scope by braces and seems to rebuild everything in the main which should include, includes. Modules however are out of that scope and will require their own if it's needed.
IMHO if you're using a button array and then just populating it with a loop of sequential numbers then using the array is pointless and you're better off for many reason just using the catch all 0. If you're shifting non sequential channel numbers to index positions then that's a different story. I almost always initialize my UI arrays where defined so there aren't many reasons for needing to initialize arrays in define start and using rebuild event for button array related needs since if you're using non sequential button mapping you have no choice but to manually enter those values.
Testing code for anyone that wants to play:
(testing on an NI-700 running v3.50.430)
Main
Module:
DEFINE_EVENT //BUTTON_EVENT [vTP_Arry,nTestBtn_Arry] BUTTON_EVENT [vTP1,0] BUTTON_EVENT [vTP2,0] BUTTON_EVENT [vTP3,0] { PUSH: { STACK_VAR INTEGER nBtn; STACK_VAR INTEGER nUI_Indx; nBtn = BUTTON.INPUT.CHANNEL; nUI_Indx = (BUTTON.INPUT.DEVICE.NUMBER - 33000); SEND_STRING 0, "'FROM MAIN, BIC * UI-[ ',itoa(nUI_Indx),' ] PUSHED BTN-[ ',itoa(nBtn),' ] :DEBUG<',itoa(__LINE__),'>'"; } }then I create another button event that uses the arrays. I also create a module and pass these arrays into the module and then a button event inside the module to use these arrays.
MAIN BUTTON_EVENT [vTP_Arry,nTestBtn_Arry] { PUSH: { STACK_VAR INTEGER nBtn; STACK_VAR INTEGER nUI_Indx; nBtn = GET_LAST(nTestBtn_Arry); nUI_Indx = GET_LAST(vTP_Arry); SEND_STRING 0, "'FROM MAIN, GET_LAST * UI-[ ',itoa(nUI_Indx),' ] PUSHED BTN-[ ',itoa(nBtn),' ] :DEBUG<',itoa(__LINE__),'>'"; } } MODULE DEFINE_EVENT //BUTTON_EVENT [vTPs,nBtns] BUTTON_EVENT [vTPs,nBtns] { PUSH: { STACK_VAR INTEGER nBtn; STACK_VAR INTEGER nUI_Indx; nBtn = GET_LAST(nBtns); nUI_Indx = GET_LAST(vTPs); SEND_STRING 0, "'FROM MODULE, UI-[ ',itoa(nUI_Indx),' ] PUSHED BTN-[ ',itoa(nBtn),' ] :DEBUG<',itoa(__LINE__),'>'"; } }
if we start with arrays that aren't initialized:
DEFINE_VARIABLE //ARRAYS AND VARS VOLATILE INTEGER nTestBtn_Arry[NUM_TEST_BTNS]; VOLATILE DEV vTP_Arry[NUM_TEST_TPS];then initialize these arrays in define start, then use set_length_array() and then rebuild_events() only the main code will trigger a button event. Nothing from the module!
Line 29 (11:46:13):: FROM MAIN, BIC * UI-[ 1 ] PUSHED BTN-[ 1 ] :DEBUG<82> Line 30 (11:46:13):: FROM MAIN, GET_LAST * UI-[ 1 ] PUSHED BTN-[ 1 ] :DEBUG<99>So we also have to trigger the RE in the module to get that event table to function.
Line 32 (11:55:46):: FROM MODULE STARTING REBUILD_EVENTS :DEBUG<32> Line 33 (11:55:46):: FROM MODULE, FINSIHED REBUILD_EVENTS :DEBUG<34>then the same button push yields:
Line 34 (11:56:00):: FROM MAIN, BIC * UI-[ 1 ] PUSHED BTN-[ 1 ] :DEBUG<82> Line 35 (11:56:00):: FROM MAIN, GET_LAST * UI-[ 1 ] PUSHED BTN-[ 1 ] :DEBUG<99> Line 36 (11:56:00):: FROM MODULE, UI-[ 1 ] PUSHED BTN-[ 1 ] :DEBUG<20>if you init the arrays, rebuild the events in both the main and module but don't set the array lengths before calling rebuild_event a button push won't fire an event in the main or module which means the rebuild function uses length_array not max_length_array.
if you init the arrays and set the lengths in the main code but only rebuild_event in the module the module button event will fire but the main's won't.
Line 5 (14:10:25):: FROM MAIN, BIC * UI-[ 1 ] PUSHED BTN-[ 1 ] :DEBUG<82> Line 6 (14:10:25):: FROM MODULE, UI-[ 1 ] PUSHED BTN-[ 1 ] :DEBUG<20>Remember the MAIN, BIC always fires since it's defined and initialized during compile.
Now if you take that same define_start section and init the arrays, set the lengths but don't rebuild in that section but then create another array and in a different define_start intit the array, set the length but in this code section rebuild the events the event table are re-built for both the old and new array even though the rebuild event was enclosed in braces. The instructions state that only items used in events enclosed in the braces are rebuilt but it appears everything in the main is. The module still won't work until rebuild event is called in the module.
So basically rebuild event isn't limited to scope by braces and seems to rebuild everything in the main which should include, includes. Modules however are out of that scope and will require their own if it's needed.
IMHO if you're using a button array and then just populating it with a loop of sequential numbers then using the array is pointless and you're better off for many reason just using the catch all 0. If you're shifting non sequential channel numbers to index positions then that's a different story. I almost always initialize my UI arrays where defined so there aren't many reasons for needing to initialize arrays in define start and using rebuild event for button array related needs since if you're using non sequential button mapping you have no choice but to manually enter those values.
Testing code for anyone that wants to play:
(testing on an NI-700 running v3.50.430)
Main
PROGRAM_NAME='REBUILD_EVENT_TEST' DEFINE_DEVICE //VIRTUAL TPs vTP1 = 33001:1:0; vTP2 = 33002:1:0; vTP3 = 33003:1:0; DEFINE_CONSTANT //ARRAY SIZES NUM_TEST_BTNS = 20; NUM_TEST_TPS = 3; DEFINE_VARIABLE //ARRAYS AND VARS VOLATILE INTEGER nTestBtn_Arry[NUM_TEST_BTNS]; VOLATILE INTEGER nTestBtn_Arry2[NUM_TEST_BTNS]; VOLATILE DEV vTP_Arry[NUM_TEST_TPS]; VOLATILE INTEGER nRunTest = 0; VOLATILE INTEGER nModTest = 0; DEFINE_MODULE 'REBUIL_EVENT_MOD' R_E_mod1(vTP_Arry,nTestBtn_Arry,nModTest) #DEFINE ALLOW_START_ITEMS #IF_DEFINED ALLOW_START_ITEMS #DEFINE ALLOW_SET_LENGTHS //#DEFINE ALLOW_REBUILD_EVENT DEFINE_START { STACK_VAR INTEGER i; for(i = NUM_TEST_BTNS; i; i--) { nTestBtn_Arry[i] = i; } for(i = NUM_TEST_TPS; i; i--) { STACK_VAR INTEGER nDev; nDev = (33000 + i); vTP_Arry[i] = nDev:1:0; } SEND_STRING 0, "'FROM MAIN, INTIALIZED ARRAYS :DEBUG<',itoa(__LINE__),'>'"; #IF_DEFINED ALLOW_SET_LENGTHS SET_LENGTH_ARRAY(nTestBtn_Arry,NUM_TEST_BTNS); SET_LENGTH_ARRAY(vTP_Arry,NUM_TEST_TPS); SEND_STRING 0, "'FROM MAIN, SET LENGTH ARRAYS :DEBUG<',itoa(__LINE__),'>'"; #ELSE #WARN 'SET LENGTH ARRAY ITEMS EXCLUDED FROM COMPILE' #END_IF #IF_DEFINED ALLOW_REBUILD_EVENT SEND_STRING 0, "'FROM MAIN, STARTING REBUILD_EVENTS :DEBUG<',itoa(__LINE__),'>'"; REBUILD_EVENT() SEND_STRING 0, "'FROM MAIN, FINSIHED REBUILD_EVENTS :DEBUG<',itoa(__LINE__),'>'"; #ELSE #WARN 'REBUILD_EVENT EXCLUDED FROM COMPILE' #END_IF } #ELSE #WARN 'DEFINE START ITEMS EXCLUDED FROM COMPILE' #END_IF DEFINE_START { STACK_VAR INTEGER i; for(i = NUM_TEST_BTNS; i; i--) { nTestBtn_Arry2[i] = (NUM_TEST_BTNS + i); } SET_LENGTH_ARRAY(nTestBtn_Arry2,NUM_TEST_BTNS); REBUILD_EVENT(); } DEFINE_EVENT //BUTTON_EVENT [vTP_Arry,nTestBtn_Arry] BUTTON_EVENT [vTP1,0] BUTTON_EVENT [vTP2,0] BUTTON_EVENT [vTP3,0] { PUSH: { STACK_VAR INTEGER nBtn; STACK_VAR INTEGER nUI_Indx; nBtn = BUTTON.INPUT.CHANNEL; nUI_Indx = (BUTTON.INPUT.DEVICE.NUMBER - 33000); SEND_STRING 0, "'FROM MAIN, BIC * UI-[ ',itoa(nUI_Indx),' ] PUSHED BTN-[ ',itoa(nBtn),' ] :DEBUG<',itoa(__LINE__),'>'"; } } DEFINE_EVENT //BUTTON_EVENT [vTP_Arry,nTestBtn_Arry] BUTTON_EVENT [vTP_Arry,nTestBtn_Arry] { PUSH: { STACK_VAR INTEGER nBtn; STACK_VAR INTEGER nUI_Indx; nBtn = GET_LAST(nTestBtn_Arry); nUI_Indx = GET_LAST(vTP_Arry); SEND_STRING 0, "'FROM MAIN, GET_LAST * UI-[ ',itoa(nUI_Indx),' ] PUSHED BTN-[ ',itoa(nBtn),' ] :DEBUG<',itoa(__LINE__),'>'"; } } DEFINE_EVENT //BUTTON_EVENT [vTP_Arry,nTestBtn_Arry] BUTTON_EVENT [vTP_Arry,nTestBtn_Arry2] { PUSH: { STACK_VAR INTEGER nBtn; STACK_VAR INTEGER nUI_Indx; nBtn = GET_LAST(nTestBtn_Arry2); nUI_Indx = GET_LAST(vTP_Arry); SEND_STRING 0, "'FROM MAIN, GET_LAST * UI-[ ',itoa(nUI_Indx),' ] PUSHED BTN-[ ',itoa(nBtn),' ] :DEBUG<',itoa(__LINE__),'>'"; } } DEFINE_PROGRAM //TESTING CODE if(nRunTest) { SWITCH(nRunTest) { CASE 1: { SEND_STRING 0, "'FROM MAIN, STARTING REBUILD_EVENTS :DEBUG<',itoa(__LINE__),'>'"; REBUILD_EVENT() SEND_STRING 0, "'FROM MAIN, FINSIHED REBUILD_EVENTS :DEBUG<',itoa(__LINE__),'>'"; } CASE 2: { STACK_VAR INTEGER i; for(i = NUM_TEST_BTNS; i; i--) { nTestBtn_Arry[i] = i; } } CASE 3: { STACK_VAR INTEGER i; for(i = NUM_TEST_TPS; i; i--) { STACK_VAR INTEGER nDev; nDev = (33000 + i); vTP_Arry[i] = nDev:1:0; } } CASE 4: { vTP_Arry[1] = 33001:1:0; vTP_Arry[2] = 33002:1:0; vTP_Arry[3] = 33003:1:0; } CASE 5: { SET_LENGTH_ARRAY(nTestBtn_Arry,NUM_TEST_BTNS); } CASE 6: { SET_LENGTH_ARRAY(vTP_Arry,NUM_TEST_TPS); } CASE 7: { SET_LENGTH_ARRAY(nTestBtn_Arry,0); } CASE 8: { SET_LENGTH_ARRAY(vTP_Arry,0); } } nRunTest = 0; }
Module:
MODULE_NAME='REBUIL_EVENT_MOD' (DEV vTPs[],INTEGER nBtns[],INTEGER nModTest) DEFINE_CONSTANT //ARRAYS SIZES NUM_TEST_BTNS = 20; NUM_TEST_TPS = 3; DEFINE_EVENT //BUTTON_EVENT [vTPs,nBtns] BUTTON_EVENT [vTPs,nBtns] { PUSH: { STACK_VAR INTEGER nBtn; STACK_VAR INTEGER nUI_Indx; nBtn = GET_LAST(nBtns); nUI_Indx = GET_LAST(vTPs); SEND_STRING 0, "'FROM MODULE, UI-[ ',itoa(nUI_Indx),' ] PUSHED BTN-[ ',itoa(nBtn),' ] :DEBUG<',itoa(__LINE__),'>'"; } } DEFINE_PROGRAM //TESTING CODE if(nModTest) { SWITCH(nModTest) { CASE 1: { SEND_STRING 0, "'FROM MODULE STARTING REBUILD_EVENTS :DEBUG<',itoa(__LINE__),'>'"; REBUILD_EVENT() SEND_STRING 0, "'FROM MODULE, FINSIHED REBUILD_EVENTS :DEBUG<',itoa(__LINE__),'>'"; } CASE 2: { STACK_VAR INTEGER i; for(i = NUM_TEST_BTNS; i; i--) { nBtns[i] = i; } } CASE 3: { STACK_VAR INTEGER i; for(i = NUM_TEST_TPS; i; i--) { STACK_VAR INTEGER nDev; nDev = (3300 + i); vTPs[i] = nDev:1:0; } } CASE 4: { vTPs[1] = 33001:1:0; vTPs[2] = 33002:1:0; vTPs[3] = 33003:1:0; } CASE 5: { SET_LENGTH_ARRAY(nBtns,NUM_TEST_BTNS); } CASE 6: { SET_LENGTH_ARRAY(vTPs,NUM_TEST_TPS); } CASE 7: { SET_LENGTH_ARRAY(nBtns,0); } CASE 8: { SET_LENGTH_ARRAY(vTPs,0); } } nModTest = 0; }
0
Comments
I kinda went down this same path when I was trying to impliment a dynamic UI but kept running into conceptual paradoxes based upon how I 'thought' it should work and it actually worked. I performed similar test just to prove my own sanity to myself. It was the call to tech support that verified I wasn't nuts.
To be honest, I just gave up using R_E. it never quite worked as advertised and there are better approaches to getting around it anyway.
If it did work as advertised, it would sure make it a lot easier to make systems more dynamic and changeable on the fly. But alas and anon...
For me the only time I use R_E is when I increase virtual counts so if I increase my virtual level counts from the default 8 to say 24 then I'll plan on using an R_E after a delay. I don't know if that's actually required after increasing virtual counts but it seems like it should be but maybe the set virtual count function already has that built in. I guess to satisfy my own anal curiousity I'll test those today and see if I need R_E at all.
Using the previous code I add set level count in define start: so 1st of all my array isn't init where defined, I also created two level events in my main, one uses the define vTP and the other uses the array which gets init in define start. So upon program load I init my virtual array and set my virtual level count per virtual to 24 in define start.
Uisng emulate device I send level 1 value 40 emulating virteual dev 1. and only the level event that uses the vTP1,0..vTP3,0 triggers a level. The event that uses the array doesn't since it had no values when the event tables were built.
While here I test higher levels and they all seem to work in the event handler that uses vTP1,0..vTP3,0 which immediately tells me I don't need to use R_E just because I used set_virtual_level_count. I've always assume I would so I have been. So what they say about people who assume is true.
Now in this scenario where I init the virtual dev array in define start I would have to use R_E to get those event table to work but again why would anyone really want to init their arrays in define start.
Anyway so I call R_E in the main and now that event handler that uses the array init in define start triggers and then when I call R_E in the module which also uses the same array it triggers.
If you decide to run these tests you need to remember to change your level value each time cuz the master won't acknowledge the same level twice so it won't print to diagnostics again if the same level is sent again. I forgot and thought I broke it!
So the moral of the story is you don't need to call R_E because you increased virtual counts but like the button events if arrays that are used in event handlers are init anywhere other than where they're defined (define_variable) you'll need to set their length and rebuild the events. If you're using constant arrays they can only be init in define_constant so that's never an issue.
Since there's really no point in initializing arrays used in event handlers anywhere other than where they're defined and in most cases using button arrays at all is pointless unless as discussed rebuild event has very limited purpose and typically shouldn't be required. Like I said earlier I thought I needed it when I increased my virtual counts but that doesn't appear to be the case so I'm now I can be R_E free.
Trying to set level counts on a dynamic array of devices
Semi-obvious now that I think about it but this:
doesn't work but this:
does
So until the device is in the event table you can't increase their levels
....
hallelujah!
Jeff
I couldn't use the button event wildcard since I use the hold event with different times, repeats, etc depending on the device. I use the wild card for one event that I want to have happen on every button press on every device, but I use predefined arrays for everything else.
Paul
You're not wrong that it's processor-heavy but even on a complicated project I find it only runs for a few seconds. I did get it to lock up by putting it inside a loop that was modifying event tables - by pulling it out and running it once after all the loops it was all fine.
I'm not going to let that one slide. If you have a fairly complicated module that you need to modify down the line, you have all kinds of places where you can introduce bugs by copy-pasting code incorrectly or forgetting to add new entries to join arrays or device arrays etc. It's completely understandable if you're looking at it 12 months later.
I recently wrote a NetLinx program to control 10 PDUs - 10 virtual devices, each with 8 ports, each needing to be toggled on and off by a particular button on a touch panel. The configuration and join numbers are likely to change in the future. Do I want lines upon lines of constants? No way.
Out of 300 lines of code all of the configuration is maintained in:
- 10 variables containing the IP addresses of the PDUs
- a series of lines under DEFINE_START:
In one little block of code I am defining which outlets on which PDUs are tied to which joins, how many seconds each device needs to be off for before it is turned on again, and how many seconds before it should register as being finished turning on.
InitPDUOutlet() does a few things:
- Adds a new pdu outlet structure to the end of an array, containing the settings passed in, so we can use it later
- Creates a devchan inside that structure for the touch panel button
- Adds that devchan to the array that we're listening on
When a button press is detected from the devchan array, it goes through the array of structures to find the one that corresponds to the press. It then has all the parameters and knows what to do with it.
Luckily I can now forget about all that detail - and if I ever need to change a PDU Outlet I can just change one line and not introduce any bugs!
EDIT: In case anyone is interested in this kind of thing, this is a general programming principle called Don't Repeat Yourself: in short, that if you change one thing you shouldn't have to run around and update other bits of code to reflect that. DRY is really very hard to achieve with NetLinx - much easier with Java - but there are some small things you can do.
I think you can achieve the same thing without using r_e. I can see it can be useful in some instances, but being the processor hog it is is a deal breaker for me. I don't use devchans either so perhaps if I did I would be tempted to use it. Adding new entries to arrays is trivial so I guess I don't see the need. What's the point of the line dcOnOffListeners = dcOnOffListeners;? Is that a trick needed to make r_e work?
Paul
I did come across another case where I felt I should use it. If you are creating a virtual device that implements the listbox protocol it has to be able to receive table or view definitions on arbitrary ports. I suppose you could manually define a bunch of DATA_EVENT handlers and say to your user "Okay you have to use ports 1-16 and don't you dare change the virtual port count". The alternative is to add a new device to the DATA_EVENT handler on the fly when a listbox object is registered on that port then run REBUILD_EVENT. I went with the latter since I could.
Yes indeed. REBUILD_EVENT only affects variables that have been changed in the same function. If you only change the element of an array (as opposed to assigning a new array to the array variable), any event handlers using that array will not be updated.
Calling during start up is one thing but during runtime it takes the event handlers offline for x amount of time and the system can't perform any tasks like button pushes or channel triggers.
Also, this thread has a thorough discussion: Button_event and array definition question
With Netlinx I am less concerned about neat as functional. With an RTOS, my priority is CPU scheduling, since there is no built in scheduler and the CPU will happily run R_E for days if necessary.
I think the only time I would use R_E is in define_start. Otherwise it could impact latency in a running system. Just my opinion.
Ugh, I thought so. Thanks for the info, although I hope to never have to use it
Paul
I don't use R_E and prefer defining all button numbers in constant arrays, all button numbers are defined in the same spot so i won't accidentally use a channel twice. In addition i don't need button numbers for each function to be consecutive.