Netlinx port codes
I have a project with over 40 identical devices to control.
My idea was to use same design for all BUT with different channel ports, then in the code just do a switch depending on the device port and coding is done.
My question is:
I defined over 40 touch panels in DEFINE_DEVICE (ugly and big chance of mistake).
Can i define one device that will catch all that is coming from a single touch panel with no need of defining it 40 times with different ports?
Example:
DEFINE_DEVICE
TP_PORT1 = 10001:1:0
TP_PORT2 = 10001:2:0
TP_PORT3 = 10001:3:0
... and so on till ±40
can it be done like
TP= 10001:0:0 - i know 0 is setup port so didnt even tried it like that
Hope you can understand what i wanna do
Thanks,
Alan
My idea was to use same design for all BUT with different channel ports, then in the code just do a switch depending on the device port and coding is done.
My question is:
I defined over 40 touch panels in DEFINE_DEVICE (ugly and big chance of mistake).
Can i define one device that will catch all that is coming from a single touch panel with no need of defining it 40 times with different ports?
Example:
DEFINE_DEVICE
TP_PORT1 = 10001:1:0
TP_PORT2 = 10001:2:0
TP_PORT3 = 10001:3:0
... and so on till ±40
can it be done like
TP= 10001:0:0 - i know 0 is setup port so didnt even tried it like that
Hope you can understand what i wanna do
Thanks,
Alan
0
Comments
You can use multiple arrays or a structure depending on what you're comfortable with. When you select a device to control just set the tracking array value for that UI index to the index of the device to be controlled. That index will then correspond to the index of the Virtual or dev to be controlled then you just need one port and one touch panel page.
This should get you started. It will control all 40 devices from a single touchpanel page/popup/port. The whole "separate port per device thing" is a horrible method to teach in AMX training. It is very wasteful both in processor resources and in touchpanel creation/editing labor time.
This method can control all 1-way devices in a system whether they are the same or not, just use different panel pages with corresponding button channels and labels.
(***********************************************************) (* DEVICE NUMBER DEFINITIONS GO BELOW *) (***********************************************************) DEFINE_DEVICE dvDeviceControlTP1 = 10001:1:0 dvDeviceControlTP2 = 10002:1:0 dvDeviceControlTP3 = 10003:1:0 dvDevice1 = 5001:1:0 dvDevice2 = 5001:2:0 // Do this to define all identicle devices dvDevice40 = 5001:40:0 (***********************************************************) (* CONSTANT DEFINITIONS GO BELOW *) (***********************************************************) DEFINE_CONSTANT integer maxNumTPs = 3 // Array of touch panels dev dvTouchPanels[] = { dvDeviceControlTP1, dvDeviceControlTP2, dvDeviceControlTP3 } // Array of identicle devices to control dev dvDevicestoControl[] = { dvDevice1, dvDevice2, // add all devices here dvDevice40 } // These are for selecting current device // They can be a different TP port or simply channels // That are above the highest channel you use for the devices integer deviceSelectButtons[] = { 301,302,303,304,305,306,307,308,309,310, 311,312,313,314,315,316,317,318,319,320, 321,322,323,324,325,326,327,328,329,330, 331,332,333,334,335,336,337,338,339,340 } (***********************************************************) (* VARIABLE DEFINITIONS GO BELOW *) (***********************************************************) DEFINE_VARIABLE integer currentDevice[maxNumTPs] (***********************************************************) (* THE EVENTS GO BELOW *) (***********************************************************) DEFINE_EVENT // This is where you select which device the TP is going to control // It is normally a source select page or whatever button_event[dvTouchPanels,deviceSelectButtons] { push: { stack_var integer touchPanelIndex stack_var integer deviceIndex touchPanelIndex = get_last(dvTouchPanels) deviceIndex = get_last(deviceSelectButtons) currentDevice[touchPanelIndex] = deviceIndex // Here you can send individual device names and page flips } } // This is a "catch-all" button event for the actual device controlling buttons button_event[dvTouchPanels,0] { push: { stack_var integer touchPanelIndex stack_var integer buttonChannel touchPanelIndex = get_last(dvTouchPanels) buttonChannel = button.input.channel if(buttonChannel < deviceSelectButtons[1]) { to[dvDevicestoControl[currentDevice],buttonChannel] } // You can use push, to, min_to, send_command, [device],"'SP',chan", etc } // hold and release events go here }Seems i wasn't good in explaining what i want to do.
Maybe this code will clear a bit what i was talking about
PROGRAM_NAME='test' DEFINE_DEVICE extron = 5001:1:0 // some extron matrix switcher 8x8 dvTP1 = 10001:1:0 // touch panel 1 port1 dvTP2 = 10001:2:0 // touch panel 2 port2 dvTP3 = 10001:3:0 // ...i want to skip this defining if possible... dvTP4 = 10001:4:0 dvTP5 = 10001:5:0 dvTP6 = 10001:6:0 dvTP7 = 10001:7:0 dvTP8 = 10001:8:0 // touch panel 2 port8 DEFINE_VARIABLE integer temp_port integer temp_butt integer extron_butt[]={ 1,2,3,4,5,6,7,8 } DEFINE_EVENT BUTTON_EVENT[dvTP1,extron_butt] BUTTON_EVENT[dvTP2,extron_butt] BUTTON_EVENT[dvTP3,extron_butt] BUTTON_EVENT[dvTP4,extron_butt] BUTTON_EVENT[dvTP5,extron_butt] BUTTON_EVENT[dvTP6,extron_butt] BUTTON_EVENT[dvTP7,extron_butt] BUTTON_EVENT[dvTP8,extron_butt] { PUSH: { temp_port=button.input.device.port temp_butt=button.input.channel send_string extron,"itoa(temp_butt),'*',itoa(temp_port),'!'" } }in this case i just use 8 ports so its not that much, but as i said when you have 40 ports in use then it becomes a different story.
my question was just the part about DEFINE_DEVICE - is there a way to define device so that i can register it in BUTTON_EVENT and see the port of the pushed button.
Thanks once more for your previous answer. It game me some nice ideas
you just need a DEV array of your touch panels, then grab which panel its from if you want it to act indepently.
DEV dvTPs[] = { dvTP1, dvTP2,dvTP3,.... } .. BUTTON_EVENT[dvTPs,extron_butt] { PUSH: { local_var INTEGER nPanel local_var INTEGER nButton nPanel = GET_LAST(dvTPs) nButton = GET_LAST(extron_butt) .... } }maybe this is what you are trying to find out??
Thanks
With the examples provided you only need to define 1 DPS per touch panel to control any of the 40 devices. You will need to define 40 different DPS's for each device though and then put all of them into one DEV array. Do the same for virtual DEVs if you using them for modules, if not forget the virtuals.
Then on your TP create your device selection buttons, an earlier example suggested buttons 301-340. Create a variable to track what DEV the TP is controllong. Now when you decide to control device 4 the user pushes button 304, in your button event handle the push and subract 300 from the button value and store the remainder, 4, in yor dev tracking var. now when any other buttons are pushed other than buttons 301-340 you route it to the dev array index position held by your dev tracking var.
There are Netlinx Master memory considerations when defining different ports on a touch panel. We allocate memory for all of the possible channels/levels/ports etc. for EACH port defined PER panel. As your system grows, and the number of panels increases, this can become an issue and is not a good practice to get into if you are just starting out. While it may seem faster, the suggested approaches are far superior from a code logic standpoint.
As Pete pointed out, defining the first port on your panel would be preferred here. Using a flag to track the last source selected from your button array of 40 source options is the cleanest, and most memory friendly approach. Additionally, it is preferred from a TPDesign approach. Under your proposed approach, if you wanted to add a button to a page, you would have to add it to each page. Then, you would need to change the port on that button across 40 pages. The panel file would be un-necessarily large and time consuming to manage.
The concept of GET_LAST will be very helpful to comprehend as it relates to index values and arrays - button or device.
Let's use an example where you have 40 IR controlled devices, and in Pete's code you already defined your device array and your button array for source selection. In my example, I would define an additional array that mapped button channels to IR file channels (if you push a nDeviceTransportButtons, pulse the corresponding IR channel in nDeviceIRChannels).
Your event statement could look something like this:
BUTTON_EVENT [dvTouchPanels, nDeviceTransportButtons] { PUSH: { PULSE[dvDevicestoControl[currentDevice[GET_LAST(dvTouchPanels)]], nDeviceIRChannels[GET_LAST(nDeviceTransportButtons)]; } }The benefit here is that the event statement is fairly fixed moving forward. If you add a panel to the system, add it to the array definition for panels. If you add a button, add it to the button array and the corresponding IR channel array. This would allow you to scale this rapidly and with minimal TPDesign work.
The last explanation by vining is exactly what you should do.
A DPS is much more costly (in terms of memory & efficiency) than it is to use a single char (byte).
Edit: Chris' way would also be a way to do it; as you can see, there are many "right" ways to do it.
Sublime Text 2 is a code editor that has a "multiple cursor" function that I use for the same purpose. Sometimes it is faster than excel.
Two tools, same results.
That's like saying the front brake is the most dangerous part on a motorcycle. Learning how to use it properly will turn it into the safest part on that bike.
I think you will find in developing truly scalable and maintainable code for large system deployments that limiting yourself to a single port on a touchpanel will be... limiting.
So, I understand how to control devices, but what is not obvious to me is how to control the feedback using the one-page method. If, for example, there are 40 panels and 40 devices and a button is pushed on one of the panels and this button push requires a change in feedback, how is this feedback updated to other panels which are currently controlling the same device? I'm not seeing a simple way to do this but with the multi-port method, it's real easy.
If you track what source is in use per zone, and what panel is CURRENTLY operating what zone, you can tell both where to send control pushes from the panels and where to direct feedback from devices. Now you can let any panel control any zone too... several at a time can control the same room now, and different ones later. All get what they need and control only what they are supposed to, all with a single control page per source TYPE.
This is more programming work, but creates a more intelligent system that can expand and change without agonizing TP revision. And the logic is reusable, as is the TP.
When i have feedback to send to my UIs i run it through a for loop on my tracking array and if the UI index position holds the value of the instance or dev then feedback is sent.
The tracking array gets set to the device or instance when you go on page and changed to zero or the next page when exited.
Ok, I can see that. It's somewhat more complicated, but you really only have to do it once and then it's cut and paste.
Here's an example of my VT function:
DEFINE_FUNCTION fnFB_DoSend_VT(INTEGER iUI_Indx,INTEGER iCHAN,CHAR iStrMSG[]) { STACK_VAR INTEGER n; STACK_VAR INTEGER nTPCount; STACK_VAR INTEGER nLoopStart; STACK_VAR CHAR cUNI_STR_2[MAX_4096]; STACK_VAR WIDECHAR cUNI_STR_1[MAX_4096]; if(iUI_Indx) { nTPCount = iUI_Indx; nLoopStart = iUI_Indx; } else { nTPCount = sSBS.sPlayer.nNum_UIs; nLoopStart = 1; } //fnDevMod_DeBug("'SEND_VT: ',iStrMSG,' :DEBUG<',itoa(__LINE__),'>'"); if(length_string(iStrMSG)) { cUNI_STR_1 = WC_DECODE(iStrMSG,WC_FORMAT_UTF8,1); cUNI_STR_2 = WC_ENCODE(cUNI_STR_1,WC_FORMAT_TP,1); } for(n = nLoopStart; n <= nTPCount; n++) { if(nUI_ActiveArry[n] == sSBS.sPlayer.nDev_Instance)//means it on this SB & ACTIVE on page { SWITCH(nUI_TypeArry[n]) { CASE UI_TYPE_iPHONE: CASE UI_TYPE_iTOUCH: CASE UI_TYPE_iPAD: CASE UI_TYPE_G4: CASE UI_TYPE_R4: CASE UI_TYPE_MIO_DMS: { if(length_string(cUNI_STR_2)) { STACK_VAR CHAR cUniSend[MAX_4096]; STACK_VAR INTEGER nAppend; cUniSend = cUNI_STR_2; WHILE(LENGTH_STRING(cUniSend)) { STACK_VAR CHAR cTMP[UNI_STR_SIZE]; if(LENGTH_STRING(cUniSend) > UNI_STR_SIZE) { cTMP = GET_BUFFER_STRING(cUniSend,UNI_STR_SIZE); } else { cTMP = GET_BUFFER_STRING(cUniSend,LENGTH_STRING(cUniSend)); } if(nAppend) { SEND_COMMAND dvUI_SBSArry[n],"'^BAU-',ITOA(iCHAN),',0,',cTMP"; //Append Unicode to Modero Panels } else { SEND_COMMAND dvUI_SBSArry[n],"'^UNI-',ITOA(iCHAN),',0,',cTMP"; //Unicode to Modero Panels } //fnDevMod_DeBug("'SEND_COMMAND UNI, APPEND-[ ',itoa(nAppend),' ], CHNL-[ ',itoa(iCHAN),' ], STR-[ ',cTMP,' ] :DEBUG<',ITOA(__LINE__),'>'"); nAppend++; } } else//we need to clear text fields too, duh! { SEND_COMMAND dvUI_SBSArry[n], "'^TXT-',itoa(iCHAN),',0,',cUNI_STR_2"; } } CASE UI_TYPE_G3: { //////fnDevMod_DeBug("'^TXT- TP INDX: ',itoa(n),', CHNL: ',itoa(iCHAN),' :DEBUG<',ITOA(__LINE__),'>'"); SEND_COMMAND dvUI_SBSArry[n], "'^TXT-',itoa(iCHAN),',0,',iStrMSG"; } CASE UI_TYPE_METKP: CASE UI_TYPE_UNKNOWN: CASE UI_TYPE_VIRTUAL: { //DO NOTHING } } } } RETURN; }Now anywhere in my device module code I need to send feedback I just pass it the parameters the functions calls for. If a UI just came on page my function call will look like this: And I pass the actual UI index so feedback is only sent to that UI. But when the device sends me data and I need to send to all the UIs that may be on this dev page/instance I have a constant called UI_UPDATE_ACTIVE which has a value of 0 like this: Now the function will go through the entire array and send to every UI on this dev/instance.I also pass these parameters through from function to function so when a UI comes online the UI index is passed from here:
fnFB_Update_UI(nUI_Indx,FORCE,NOCLEAR); to DEFINE_FUNCTION fnFB_Update_UI(INTEGER iUI_Indx,INTEGER iForce,INTEGER iClear) { fnDevMod_DeBug("'fnFB_Update_UI, UI-[ ',itoa(iUI_Indx),' ], LVL-[ ',itoa(sSBS.sBrowseState.nLevel),' ], REMOTE-[ ',itoa(sSBS.sNowPlaying.nRemote),' ] :DEBUG<',ITOA(__LINE__),'>'"); fnFB_Btn_isApp_SHO(iUI_Indx,sSBS.sNowPlaying.nRemote); fnFB_SetCoverArtPath(iUI_Indx,iForce,iClear);//takes the longest so go first fnFB_CurPlyLists(iUI_Indx,iForce);//zero refreshes both arrays fnFB_CurPlyListIndx(iUI_Indx,iForce); fnFB_DeBugBtn(iUI_Indx,iForce); fnFB_BrowseList(iUI_Indx,iForce,iClear); //fnFB_CurQList(iUI_Indx,iForce,iClear); //fnFB_IR_Volume(iUI_Indx,iForce); //fnFB_Mixer(iUI_Indx,iForce); fnFB_PlayerName(iUI_Indx,iForce); fnFB_Power(iUI_Indx,iForce); fnFB_Repeat(iUI_Indx,iForce); fnFB_Shuffle(iUI_Indx,iForce); fnFB_SongTime(iUI_Indx,iForce); //fnFB_SleepTime(iUI_Indx,iForce); fnFB_Transport(iUI_Indx,iForce); fnFB_UpdateScrollBar(iUI_Indx,iForce); fnFB_UpdateNowViewing(iUI_Indx,iForce); fnFB_ABRepeat(iUI_Indx,iForce); fnFB_DoSend_VT(iUI_Indx,SBS_BTN_FAV_0,' '); if(sSBS.sBrowseState.nLevel > 1) { fnFB_DoSend_UI_CMD(iUI_Indx,"'^SHO-',itoa(SBS_BTN_SHOW_LIST),',1'"); fnFB_DoSend_UI_CMD(iUI_Indx,"'^SHO-',itoa(SBS_BTN_BACK_LEVEL),',1'"); } else { fnFB_DoSend_UI_CMD(iUI_Indx,"'^SHO-',itoa(SBS_BTN_SHOW_LIST),',0'"); fnFB_DoSend_UI_CMD(iUI_Indx,"'^SHO-',itoa(SBS_BTN_BACK_LEVEL),',0'"); } }which in turn calls all these functions but either the UI index value or UI_UPDATE_ACTIVE to update all active UIs is passed along the way until it reaches the sending function.Here's my channel function:
DEFINE_FUNCTION fnFB_DoSend_CHNL(INTEGER iUI_Indx,INTEGER iChannel,INTEGER iCHState) { STACK_VAR INTEGER n; STACK_VAR INTEGER nTPCount; STACK_VAR INTEGER nLoopStart; if(iUI_Indx) { nTPCount = iUI_Indx; nLoopStart = iUI_Indx; } else { nTPCount = sSBS.sPlayer.nNum_UIs; nLoopStart = 1; } //////fnDevMod_DeBug("'fnFB_DoSend_CHNL: Running FB! :DEBUG<',ITOA(__LINE__),'>'"); for(n = nLoopStart; n <= nTPCount; n++) { if(nUI_ActiveArry[n] == sSBS.sPlayer.nDev_Instance) { SWITCH(nUI_TypeArry[n]) { CASE UI_TYPE_iPHONE: CASE UI_TYPE_iTOUCH: CASE UI_TYPE_iPAD: CASE UI_TYPE_G4: CASE UI_TYPE_R4: CASE UI_TYPE_MIO_DMS: CASE UI_TYPE_G3: CASE UI_TYPE_METKP: CASE UI_TYPE_VIRTUAL: CASE UI_TYPE_UNKNOWN://leave this SWITCHin in CASE we choose to limit number of channels { //to specific types of devices (no online or comms status for keypads, etc) SWITCH(iCHState) { CASE CH_OFF://0 { OFF[dvUI_SBSArry[n],iChannel]; } CASE CH_ON://1 { ON[dvUI_SBSArry[n],iChannel]; } CASE CH_PULSE://2 { PULSE[dvUI_SBSArry[n],iChannel]; } CASE CH_TOGGLE://3 { [dvUI_SBSArry[n],iChannel] = (![dvUI_SBSArry[n],iChannel]); } } } } } } RETURN; }With these I usually perform the evaluation in the parameter like:Again iUI_Indx could be an actually index position of a UI or 0 for UI_UPDATE_ACTIVE depending on what initiated the need for feedback.
But once you've done one it is just copy and past for my different devices. FYI, I still use multiple ports, usually a port per device type but only one port/UI Page set per type no matter how many instances are actually used. IMHO it does get a bit trickier to the only 2-3 ports per per system for everything that allot folks here do. I find both ways have their advantages and drawbacks so there hasn't been enough incentive to go the 2-3 port route so in my UI I can still have nearly 30 ports defined but only one port per type of device. The OP had 40 identical devices so for me that's just one port to control using 40 instances on a module and one UI page set.
STACK_VAR _tp
_tp = tp_Module
_tp.port = 1
SEND_COMMAND _tp ....
Most devices aren't chatty enough to really be a problem, depending on what you are doing. I don't typically worry about whether the user is looking at the HVAC page to send feedback that the temperature in the room has gone up one degree, I just send it. For the chattier devices I will worry about it but they are few and far between. A Sonos module for instance would be a good example of a device where you pretty much have to limit the feedback on a need to know basis.
Paul
I have 7 panels and 20 devices, so to set all this up in Define Device:
dvTPa01 = 10001:01:0;
dvTPa02 = 10001:02:0;
dvTPa03 = 10001:03:0;
dvTPa04 = 10001:04:0;
dvTPa05 = 10001:05:0;
dvTPa06 = 10001:06:0;
dvTPa07 = 10001:07:0;
dvTPa08 = 10001:08:0;
Then in DEFINE_VARIABLE I have
DEV dTP_MAIN[] = { dvTPa01, dvTPb01,dvTPc01,dvTPd01,dvTPe01,dvTPf01,dvTPg01 };
so when I do a BUTTON_EVENT
BUTTON_EVENT[dTP_MAIN,nbtKickoff] //nbtKickoff[] = { 91,92,93,94 }
So when user from touch panel 1 presses button 91 , then BUTTON_EVENT gets recognized.
But issue I have -- I have 7 panels and 20 devices, I have to define all these in DEFINE_DEVICE and DEFINE_VARIABLE (like above). Well I figured out a way to load all my panels with all devices in DEFINE_START section but this when I refer to the panels after loaded in DEFINE_START it does not recognize when Button is pushed. I think it is because it needs to be loaded at compile time and DEFINE_START is afterwards.
Does anyone know a way to load all Panels to all devices without hardcoding everything like above so system can recognize - maybe somewhere else besides DEFINE_START
It worked using REBUILD_EVENT();
But after I dynamically built my Dev array I had to set it equal to itself to get it to work (saw from another post) - like below:
dTP_MAIN = dTP_MAIN; //- main menu
dTP_THEAT = dTP_THEAT; //- theater
dTP_MUSIC = dTP_MUSIC; //- MUSIC
REBUILD_EVENT(); //rebuild the internal Event table
Thanks for your help!!!
Thanks
Paul