Home AMX User Forum AMX General Discussion

GET_LAST w/ virtual dev array

I just ran into a problem using GET_LAST w/ a virtual dev array. I assumed using GET_LAST would work to retrieve the index postion of the virual device initiating the online event or level event but it doesn't work as I expected. My virtuals all use the same dev.number and they vary by port only and from what I gather GET_LAST only checks the dev.number to return the array index not the entire DPS. Not a big problem since I just changed my get_last to var = data.device.port since they are in the same sequential order and numbers but I would have thought when using get_last on a dev array it would check the entire DPS not just the DEV.Number. I often like to keep like devices for module instances using the same dev number and just set_virtual_port_counts then rebuild. It just seemed easier but now I'm not so sure.

Comments

  • viningvining Posts: 4,368
    I was just going through some old code and found this problem again. I figure I can't be the only one who uses virtual dev arrays that use the same dev.number and different dev.port numbers.
    DEFINE_DEVICE //VIRTUAL KEYPADS. ONE FOR EACH ZONE (AP OUTPUT) IN SYSTEM.  16 ZONES / 16 VIRTUALS + A DUMMY OR BASE VIRTUAL FOR FILLERS
    
    #IF_NOT_DEFINED vMET_KPs //need 1 for each output zone
    vMETKP_Base		= 33048:0:0 ; //  based used to set virtual port count & act as a dummy in the array when real device doesn't exist
    vMETKP_1		= 33048:1:0 ; //  zone 1	1 *)'Media Room',    
    vMETKP_2		= 33048:2:0 ; //  zone 2        2 *)'Family Room',   
    vMETKP_3		= 33048:3:0 ; //  zone 3        3 *)'1st FL GBed',   
    vMETKP_4		= 33048:4:0 ; //  zone 4        4 *)'Foyer',         
    vMETKP_5		= 33048:5:0 ; //  zone 5        5 *)'Dining Room',   
    vMETKP_6		= 33048:6:0 ; //  zone 6        6 *)'Patio (R)',     
    vMETKP_7		= 33048:7:0 ; //  zone 7        7 *)'Patio (L)',     
    vMETKP_8		= 33048:8:0 ; //  zone 8        8 *)'Master Bed',   
    vMETKP_9		= 33048:9:0 ; //  zone 9        9 *)'Master Bath',   
    vMETKP_10		= 33048:10:0 ; // zone 10       10*)'MB Porch',    
    vMETKP_11		= 33048:11:0 ; // zone 11       11*)'Purple Bed',    
    vMETKP_12		= 33048:12:0 ; // zone 12       12*)'Tan Bed',       
    vMETKP_13		= 33048:13:0 ; // zone 13       13*)'Hall Bath',   
    vMETKP_14		= 33048:14:0 ; // zone 14       14*)'Blue Bed',    
    vMETKP_15		= 33048:15:0 ; // zone 15       15*)'Guest''s Porch',
    vMETKP_16		= 33048:16:0 ; // zone 16       16*)'3rd Floor',     
    #END_IF
    
    DEFINE_VARIABLE //DEV vMETKP_Arry[METKP_NUM_AP_ZONES]					     
    					     
    VOLATILE DEV vMETKP_Arry[METKP_NUM_AP_ZONES] =
    					     
    					     {
    					     vMETKP_1,		//  zone 1	    1 *)'Media Room',    
    					     vMETKP_2,  	//  zone 2          2 *)'Family Room',   
    					     vMETKP_3,  	//  zone 3          3 *)'1st FL GBed',   
    					     vMETKP_4,  	//  zone 4          4 *)'Foyer',         
    					     vMETKP_5,  	//  zone 5          5 *)'Dining Room',   
    					     vMETKP_6,  	//  zone 6          6 *)'Patio (R)',     
    					     vMETKP_7,  	//  zone 7          7 *)'Patio (L)',     
    					     vMETKP_8,  	//  zone 8          8 *)'Master Bed',   
    					     vMETKP_9,  	//  zone 9          9 *)'Master Bath',   
    					     vMETKP_10, 	//  zone 10         10*)'MB Porch',    
    					     vMETKP_11, 	//  zone 11         11*)'Purple Bed',    
    					     vMETKP_12,		//  zone 12 	    12*)'Tan Bed',       			     
    					     vMETKP_13, 	//  zone 13         13*)'Hall Bath',   
    					     vMETKP_14, 	//  zone 14         14*)'Blue Bed',    
    					     vMETKP_15, 	//  zone 15         15*)'Guest''s Porch',
    					     vMETKP_16  	//  zone 16         16*)'3rd Floor',     
    					     }	
    
    DEFINE_START    //SET_VIRTUAL_LEVEL_COUNT(vMETKP_Base,METKP_NUM_AP_ZONES) ;
    
    SET_VIRTUAL_PORT_COUNT(vMETKP_Base,METKP_NUM_AP_ZONES) ;
    
    DEFINE_EVENT    //DATA_EVENT[vMETKP_Arry]
    
    DATA_EVENT[vMETKP_Arry]
    
         {
         ONLINE:
    	  {
    	  STACK_VAR INTEGER vKP_Indx ;
    	  STACK_VAR INTEGER nDeviceID ;
    	  STACK_VAR INTEGER vKP_Indx_Port ;
    	  
    	  vKP_Indx = GET_LAST(vMETKP_Arry) ;
    	  vKP_Indx_Port = DATA.DEVICE.PORT ;
    	  fnMETKP_DeBug("'KP Online-1 vKP_Indx=',itoa(vKP_Indx),', DPS-Indx:',fnDEV_TO_STRING(vMETKP_Arry[vKP_Indx]),', DPS-Data.Device:',fnDEV_TO_STRING(DATA.DEVICE),' :DEBUG <',ITOA(__LINE__),'>'") ;
    	  fnMETKP_DeBug("'KP Online-2 vKP_Indx_Num=',itoa(vKP_Indx_Port),', DPS-Indx_Num-',fnDEV_TO_STRING(vMETKP_Arry[vKP_Indx_Port]),', DPS-Data.Device:',fnDEV_TO_STRING(DATA.DEVICE),' :DEBUG <',ITOA(__LINE__),'>'") ;
    	  if(dvMETKP_Arry[vKP_Indx] != vMETKP_Base)
    	       {//if the corresponding real device is a dummy don't set the virtuals levels, no need!
    	       SET_VIRTUAL_LEVEL_COUNT(vMETKP_Arry[vKP_Indx],METKP_NUM_FB_LVLs) ;
    	       }
    	  }
         }
    




    Diagnostics:
    Line     50 (18:00:49)::  CIpEvent::OnLine 33048:1:1
    Line     51 (18:00:49)::  MET_KPs.axi DEBUG: KP Online-1 vKP_Indx=1, DPS-Indx:33048:1:0, DPS-Data.Device:33048:1:1 :DEBUG <355>
    Line     52 (18:00:49)::  MET_KPs.axi DEBUG: KP Online-2 vKP_Indx_Num=1, DPS-Indx_Num-33048:1:0, DPS-Data.Device:33048:1:1 :DEBUG <356>
    Line    818 (18:01:57)::  CIpEvent::OnLine 33048:2:1
    Line    819 (18:01:57)::  CIpEvent::OnLine 33048:3:1
    Line    820 (18:01:57)::  CIpEvent::OnLine 33048:4:1
    Line    821 (18:01:57)::  CIpEvent::OnLine 33048:5:1
    Line    822 (18:01:57)::  CIpEvent::OnLine 33048:6:1
    Line    823 (18:01:57)::  CIpEvent::OnLine 33048:7:1
    Line    824 (18:01:57)::  CIpEvent::OnLine 33048:8:1
    Line    825 (18:01:57)::  CIpEvent::OnLine 33048:9:1
    Line    826 (18:01:57)::  CIpEvent::OnLine 33048:10:1
    Line    827 (18:01:57)::  CIpEvent::OnLine 33048:11:1
    Line    828 (18:01:57)::  CIpEvent::OnLine 33048:12:1
    Line    829 (18:01:57)::  CIpEvent::OnLine 33048:13:1
    Line    830 (18:01:57)::  CIpEvent::OnLine 33048:14:1
    Line    831 (18:01:57)::  CIpEvent::OnLine 33048:15:1
    Line    832 (18:01:57)::  CIpEvent::OnLine 33048:16:1
    Line    835 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-1 vKP_Indx=16, DPS-Indx:33048:16:0, DPS-Data.Device:33048:2:1 :DEBUG <355>
    Line    836 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-2 vKP_Indx_Num=2, DPS-Indx_Num-33048:2:0, DPS-Data.Device:33048:2:1 :DEBUG <356>
    Line    837 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-1 vKP_Indx=16, DPS-Indx:33048:16:0, DPS-Data.Device:33048:3:1 :DEBUG <355>
    Line    838 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-2 vKP_Indx_Num=3, DPS-Indx_Num-33048:3:0, DPS-Data.Device:33048:3:1 :DEBUG <356>
    Line    839 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-1 vKP_Indx=16, DPS-Indx:33048:16:0, DPS-Data.Device:33048:4:1 :DEBUG <355>
    Line    840 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-2 vKP_Indx_Num=4, DPS-Indx_Num-33048:4:0, DPS-Data.Device:33048:4:1 :DEBUG <356>
    Line    841 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-1 vKP_Indx=16, DPS-Indx:33048:16:0, DPS-Data.Device:33048:5:1 :DEBUG <355>
    Line    842 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-2 vKP_Indx_Num=5, DPS-Indx_Num-33048:5:0, DPS-Data.Device:33048:5:1 :DEBUG <356>
    Line    843 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-1 vKP_Indx=16, DPS-Indx:33048:16:0, DPS-Data.Device:33048:6:1 :DEBUG <355>
    Line    844 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-2 vKP_Indx_Num=6, DPS-Indx_Num-33048:6:0, DPS-Data.Device:33048:6:1 :DEBUG <356>
    Line    845 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-1 vKP_Indx=16, DPS-Indx:33048:16:0, DPS-Data.Device:33048:7:1 :DEBUG <355>
    Line    846 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-2 vKP_Indx_Num=7, DPS-Indx_Num-33048:7:0, DPS-Data.Device:33048:7:1 :DEBUG <356>
    Line    847 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-1 vKP_Indx=16, DPS-Indx:33048:16:0, DPS-Data.Device:33048:8:1 :DEBUG <355>
    Line    848 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-2 vKP_Indx_Num=8, DPS-Indx_Num-33048:8:0, DPS-Data.Device:33048:8:1 :DEBUG <356>
    Line    849 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-1 vKP_Indx=16, DPS-Indx:33048:16:0, DPS-Data.Device:33048:9:1 :DEBUG <355>
    Line    850 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-2 vKP_Indx_Num=9, DPS-Indx_Num-33048:9:0, DPS-Data.Device:33048:9:1 :DEBUG <356>
    Line    851 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-1 vKP_Indx=16, DPS-Indx:33048:16:0, DPS-Data.Device:33048:10:1 :DEBUG <355>
    Line    852 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-2 vKP_Indx_Num=10, DPS-Indx_Num-33048:10:0, DPS-Data.Device:33048:10:1 :DEBUG <356>
    Line    853 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-1 vKP_Indx=16, DPS-Indx:33048:16:0, DPS-Data.Device:33048:11:1 :DEBUG <355>
    Line    854 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-2 vKP_Indx_Num=11, DPS-Indx_Num-33048:11:0, DPS-Data.Device:33048:11:1 :DEBUG <356>
    Line    855 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-1 vKP_Indx=16, DPS-Indx:33048:16:0, DPS-Data.Device:33048:12:1 :DEBUG <355>
    Line    856 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-2 vKP_Indx_Num=12, DPS-Indx_Num-33048:12:0, DPS-Data.Device:33048:12:1 :DEBUG <356>
    Line    857 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-1 vKP_Indx=16, DPS-Indx:33048:16:0, DPS-Data.Device:33048:13:1 :DEBUG <355>
    Line    858 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-2 vKP_Indx_Num=13, DPS-Indx_Num-33048:13:0, DPS-Data.Device:33048:13:1 :DEBUG <356>
    Line    859 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-1 vKP_Indx=16, DPS-Indx:33048:16:0, DPS-Data.Device:33048:14:1 :DEBUG <355>
    Line    860 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-2 vKP_Indx_Num=14, DPS-Indx_Num-33048:14:0, DPS-Data.Device:33048:14:1 :DEBUG <356>
    Line    861 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-1 vKP_Indx=16, DPS-Indx:33048:16:0, DPS-Data.Device:33048:15:1 :DEBUG <355>
    Line    862 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-2 vKP_Indx_Num=15, DPS-Indx_Num-33048:15:0, DPS-Data.Device:33048:15:1 :DEBUG <356>
    Line    863 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-1 vKP_Indx=16, DPS-Indx:33048:16:0, DPS-Data.Device:33048:16:1 :DEBUG <355>
    Line    864 (18:01:57)::  MET_KPs.axi DEBUG: KP Online-2 vKP_Indx_Num=16, DPS-Indx_Num-33048:16:0, DPS-Data.Device:33048:16:1 :DEBUG <356>
    

    Also notice that GET_LAST starts at the end of the array while looking for a match not the beginning. Since GET_LAST apparently only cares about dev.number of the DPS it see index postion 16 as the match every time. So if you're going to use virtuals like this don't use nVirtual_Indx= GET_LAST(vVirtual_Arry) ; use nVirtual_Indx= DATA.DEVICE.PORT instead.
  • a_riot42a_riot42 Posts: 1,624
    I can't see any benefit to using the same virtual device number and changing the port. If you are trying to differentiate between devices, then give them different device numbers. It's not like there is any shortage of them. Doing that kind of thing seems to me to be just asking for trouble.
    Paul.
  • viningvining Posts: 4,368
    a_riot42 wrote: »
    I can't see any benefit to using the same virtual device number and changing the port. If you are trying to differentiate between devices, then give them different device numbers. It's not like there is any shortage of them. Doing that kind of thing seems to me to be just asking for trouble.
    Paul.

    Well someone at AMX must have thought it was a useful idea/method and that's why they gave us the SET_VRTUAL_PORT_COUNT command to allow us to do this. Changing the dev.number or dev.port gives each virtual a different D:P:S. The olny downside I see isn't really to do with changing ports instead of numbers but that GET_LAST on dev arrays doesn't actually compare all the devs paremeters. It only compares dev.numbers and not the complete D:P:S.

    What if you have devs on different masters with the same D:P: and different system numbers and you build an array of these devs on the different masters GET_LAST won't work on them either. I think using GET_LAST on dev arrays the function should check the entire D:P:S not just the first parameter of the 3.

    If GET_LAST worked properly then using this method makes following code a bit easier IMHO. If you working on code and you know the dev.number and want to look at the virtual for say KP 16 then that's the port, easy to follow, not 33026 (starting number) + 16 = ??.
  • a_riot42a_riot42 Posts: 1,624
    vining wrote: »
    Well someone at AMX must have thought it was a useful idea/method and that's why they gave us the SET_VRTUAL_PORT_COUNT command to allow us to do this.

    I didn't say it shouldn't be allowed. Obviously there are times when you absolutely need the capability of having a virtual device with multiple ports. But it sounds like what you are doing is using the port number to differentiate between virtual devices rather than the device number, and now finding the unintended consequences of that. If they are different devices then they should have different device numbers. If its the same device with multiple ports then that is how is should be coded. Again, I see no advantage in doing this. If there is one, let us know.
    vining wrote: »
    What if you have devs on different masters with the same D:P: and different system numbers and you build an array of these devs on the different masters GET_LAST won't work on them either. I think using GET_LAST on dev arrays the function should check the entire D:P:S not just the first parameter of the 3.

    I wouldn't do that. Different devices get different device numbers unless you like opening Pandora's box just to see what's inside. Its these types of programming errors that can cause very difficult, hard to trace bugs and I am just not that masochistic.
    vining wrote: »
    If GET_LAST worked properly then using this method makes following code a bit easier IMHO. If you working on code and you know the dev.number and want to look at the virtual for say KP 16 then that's the port, easy to follow, not 33026 (starting number) + 16 = ??.

    Seems like a strained example. When using get_last I don't care about anything but an index. As long as the correct index comes back then I am happy. I generally don't do arithmetic on virtual device numbers ever. The way I address my virtuals is at the very end of programming before installation I highlight the whole column and hit Alt+M. The numbers are more or less arbitrary and IMHO adding significance to arbitrariness is asking for trouble. It sounds like you might be one of those programmers that thinks its clever to use the channel number of a button to recall a TV station with the excuse that you can tell the station just by looking at the channel number. Down this road lies madness.
    Paul.
  • Joe HebertJoe Hebert Posts: 2,159
    vining,

    As the appointed goodwill ambassador of GET_LAST(), I feel the need to defend her honor.

    GET_LAST() with an array of virtual devices even with the same device number and different ports works fine for me. DUET modules use this method all the time.

    You didn’t post any code that we could actually compile and test but I think the problem is most likely in your code someplace and not GET_LAST().

    Here is the code that I tested against:
    DEFINE_DEVICE
    
    vdvPort1	= 33001:1:0	
    vdvPort2	= 33001:2:0	
    vdvPort3	= 33001:3:0	
    vdvPort4	= 33001:4:0	
    vdvPort5	= 33001:5:0	
    vdvPort6	= 33001:6:0	
    vdvPort7	= 33001:7:0	
    vdvPort8	= 33001:8:0	
    vdvPort9	= 33001:9:0	
    vdvPort10	= 33001:10:0	
    vdvPort11	= 33001:11:0	
    vdvPort12	= 33001:12:0	
    vdvPort13	= 33001:13:0	
    vdvPort14	= 33001:14:0	
    vdvPort15	= 33001:15:0	
    vdvPort16	= 33001:16:0	                 
    
    DEFINE_VARIABLE
    					     
    VOLATILE DEV vdvArray[] = {
       vdvPort1,
       vdvPort2,
       vdvPort3,
       vdvPort4,
       vdvPort5,
       vdvPort6,
       vdvPort7,
       vdvPort8,
       vdvPort9,
       vdvPort10,
       vdvPort11,
       vdvPort12,
       vdvPort13,
       vdvPort14,
       vdvPort15,
       vdvPort16
              
    }	
    
    DEFINE_START
    
    SET_VIRTUAL_PORT_COUNT(vdvPort1,16) ;
    
    DEFINE_EVENT
    
    DATA_EVENT[vdvArray] {
    
       ONLINE: {
    
          SEND_STRING 0, "'Index=',ITOA(GET_LAST(vdvArray)),': Port=',ITOA(DATA.DEVICE.PORT)"
    	  
       }
    }
    

    And here are the results which are exactly as I would expect them to be. A virtual device comes online and then a debug statement using GET_LAST() is printed correctly and then the next device comes online and a debug statement and so on.
    Line     10 :: CIpInterpreter::Run - Execute Startup Code - 02:58:26
    Line     11 :: CIpEvent::OnLine 33001:1:10 - 02:58:27
    Line     12 :: Memory Available = 36180552 <31072> - 02:58:27
    Line     13 :: Index=1: Port=1 - 02:58:27
    Line     14 :: CIpEvent::OnLine 0:1:10 - 02:58:27
    Line     15 :: CIpEvent::OnLine 5001:1:10 - 02:58:27
    Line     16 :: CIpEvent::OnLine 5001:2:10 - 02:58:27
    Line     17 :: CIpEvent::OnLine 5001:3:10 - 02:58:27
    Line     18 :: CIpEvent::OnLine 5001:4:10 - 02:58:27
    Line     19 :: CIpEvent::OnLine 5001:5:10 - 02:58:27
    Line     20 :: CIpEvent::OnLine 32001:1:10 - 02:58:27
    Line     21 :: CIpEvent::OnLine 10111:1:10 - 02:58:27
    Line     22 :: CIpEvent::OffLine 10111:1:10 - 02:58:27
    Line     23 :: CIpEvent::OnLine 10111:1:10 - 02:58:27
    Line     24 :: CIpEvent::OnLine 33001:2:10 - 02:58:27
    Line     25 :: Index=2: Port=2 - 02:58:27
    Line     26 :: CIpEvent::OnLine 33001:3:10 - 02:58:27
    Line     27 :: Index=3: Port=3 - 02:58:27
    Line     28 :: CIpEvent::OnLine 33001:4:10 - 02:58:27
    Line     29 :: Index=4: Port=4 - 02:58:27
    Line     30 :: CIpEvent::OnLine 33001:5:10 - 02:58:27
    Line     31 :: Index=5: Port=5 - 02:58:27
    Line     32 :: CIpEvent::OnLine 33001:6:10 - 02:58:27
    Line     33 :: Index=6: Port=6 - 02:58:27
    Line     34 :: CIpEvent::OnLine 33001:7:10 - 02:58:27
    Line     35 :: Index=7: Port=7 - 02:58:27
    Line     36 :: CIpEvent::OnLine 33001:8:10 - 02:58:27
    Line     37 :: Index=8: Port=8 - 02:58:27
    Line     38 :: CIpEvent::OnLine 33001:9:10 - 02:58:27
    Line     39 :: Index=9: Port=9 - 02:58:27
    Line     40 :: CIpEvent::OnLine 33001:10:10 - 02:58:27
    Line     41 :: Index=10: Port=10 - 02:58:27
    Line     42 :: CIpEvent::OnLine 33001:11:10 - 02:58:27
    Line     43 :: Index=11: Port=11 - 02:58:27
    Line     44 :: CIpEvent::OnLine 33001:12:10 - 02:58:27
    Line     45 :: Index=12: Port=12 - 02:58:27
    Line     46 :: CIpEvent::OnLine 33001:13:10 - 02:58:27
    Line     47 :: Index=13: Port=13 - 02:58:27
    Line     48 :: CIpEvent::OnLine 33001:14:10 - 02:58:27
    Line     49 :: Index=14: Port=14 - 02:58:27
    Line     50 :: CIpEvent::OnLine 33001:15:10 - 02:58:27
    Line     51 :: Index=15: Port=15 - 02:58:27
    Line     52 :: CIpEvent::OnLine 33001:16:10 - 02:58:27
    Line     53 :: Index=16: Port=16 - 02:58:27
    
    If you swap vdvPort1 and vdvPort16 in the vdvArray then you’ll see that index 16 port 1 comes online first and then 2-15 and the last device to come online is index 1 port 16. Everything seems to report as expected.

    If you look at the first three lines of your output the 2 debug messages look correct after port 1 comes online. But then the rest of the 16 ports come online before we see any more of your debug messages. There is something fishy going on like maybe a wait or something someplace after the first device comes online? It’s hard to tell since you’re not showing us enough of the picture.

    If you run the code I posted are your results different than mine?
  • viningvining Posts: 4,368
    Joe Hebert wrote: »
    vining,

    As the appointed goodwill ambassador of GET_LAST(), I feel the need to defend her honor.

    GET_LAST() with an array of virtual devices even with the same device number and different ports works fine for me. DUET modules use this method all the time.
    I definitely won't rule out something in my code causing this but I don't know what that might be. I also don't know what causes the delay between port 1 and the rest of the ports either. I have no waits that would affect this but maybe I have that virtual on port 1 defined somewhere else so that came online first?

    Here's the entire .axi. I have to run out now and play so I'll look through my code and see if maybe I duplicated a virtual and then test your code if I find nothing.

    There was one or two times that I thought the code (GET_LAST) worked properly and my fears subsided, then I ran this last test which obviously didn't work. I'd love nothing more than to have GET_LAST work on the entire D:P:S but even if I duplicated the virtual or had a wait somewhere how can you explain the results of side by side comparison. It obviously isn't working in this example and I can think of anything that could cause this in code.
    PROGRAM_NAME='VAV_MET_KPs'
    (***********************************************************)
    (*  FILE CREATED ON: 09/09/2007  AT: 09:26:51              *)
    (***********************************************************)
    (*  FILE_LAST_MODIFIED_ON: 07/30/2008  AT: 09:52:07        *)
    (***********************************************************)
    
    
    DEFINE_DEVICE //METREAU KEYPADS. ONE FOR EACH PHYSICAL KEYPAD
    
    #DEFINE METKP_AXI
    
    #IF_NOT_DEFINED MET_KPs   //MET-6Ns USE 2 DEVICE IDs 
    dvMETKP_1		= 162:1:0 ; //master bath	
    dvMETKP_2		= 164:1:0 ; //master porch door
    dvMETKP_3		= 166:1:0 ; //purple bed
    dvMETKP_4		= 168:1:0 ; //blue bed room
    dvMETKP_5		= 170:1:0 ; //blue bed porch
    dvMETKP_6		= 172:1:0 ; //2nd fl hall bath
    dvMETKP_7		= 174:1:0 ; //pasqaule's room
    dvMETKP_8		= 176:1:0 ; //
    dvMETKP_9		= 178:1:0 ; //
    dvMETKP_10		= 180:1:0 ; //
    dvMETKP_11		= 182:1:0 ; //...................
    #END_IF
    
    DEFINE_DEVICE //VIRTUAL KEYPADS. ONE FOR EACH ZONE (AP OUTPUT) IN SYSTEM.  16 ZONES / 16 VIRTUALS + A DUMMY OR BASE VIRTUAL FOR FILLERS
    
    #IF_NOT_DEFINED vMET_KPs //need 1 for each output zone
    vMETKP_Base		= 33048:0:0 ; //  based used to set virtual port count & act as a dummy in the array when real device doesn't exist
    vMETKP_1		= 33048:1:0 ; //  zone 1	1 *)'Media Room',    
    vMETKP_2		= 33048:2:0 ; //  zone 2        2 *)'Family Room',   
    vMETKP_3		= 33048:3:0 ; //  zone 3        3 *)'1st FL GBed',   
    vMETKP_4		= 33048:4:0 ; //  zone 4        4 *)'Foyer',         
    vMETKP_5		= 33048:5:0 ; //  zone 5        5 *)'Dining Room',   
    vMETKP_6		= 33048:6:0 ; //  zone 6        6 *)'Patio (R)',     
    vMETKP_7		= 33048:7:0 ; //  zone 7        7 *)'Patio (L)',     
    vMETKP_8		= 33048:8:0 ; //  zone 8        8 *)'Master Bed',   
    vMETKP_9		= 33048:9:0 ; //  zone 9        9 *)'Master Bath',   
    vMETKP_10		= 33048:10:0 ; // zone 10       10*)'MB Porch',    
    vMETKP_11		= 33048:11:0 ; // zone 11       11*)'Purple Bed',    
    vMETKP_12		= 33048:12:0 ; // zone 12       12*)'Tan Bed',       
    vMETKP_13		= 33048:13:0 ; // zone 13       13*)'Hall Bath',   
    vMETKP_14		= 33048:14:0 ; // zone 14       14*)'Blue Bed',    
    vMETKP_15		= 33048:15:0 ; // zone 15       15*)'Guest''s Porch',
    vMETKP_16		= 33048:16:0 ; // zone 16       16*)'3rd Floor',     
    #END_IF
    
    DEFINE_CONSTANT //NUMBER KPs & AP ZONES
    
    METKP_NUM_KPs		= 11 ;
    METKP_NUM_AP_ZONES	= 16 ;
    
    DEFINE_CONSTANT //BUTTON POINTERS
    
    METKP_BTN_1		= 1 ;
    METKP_BTN_2		= 2 ;
    METKP_BTN_3		= 3 ;
    METKP_BTN_4		= 4 ;
    METKP_BTN_5		= 5 ;
    METKP_BTN_6		= 6 ;
    METKP_BTN_7		= 7 ;
    METKP_BTN_8		= 8 ;
    METKP_BTN_9		= 9 ;
    METKP_BTN_10		= 10 ;
    METKP_BTN_11		= 11 ;
    METKP_BTN_12		= 12 ;
    METKP_BTN_13		= 13 ;
    METKP_BTN_14		= 14 ;
    
    METKP_BTN_UP		= 7 ;
    METKP_BTN_DN		= 8 ;
    METKP_BTN_LEFT		= 9 ;
    METKP_BTN_RIGHT		= 10 ;
    METKP_BTN_CENTER	= 11 ;
    METKP_BTN_ROTATE_R	= 12 ;
    METKP_BTN_ROTATE_L	= 13 ;
    
    DEFINE_CONSTANT //AP KP BUTTON POINTERS
    
    //KEYPADS 
    KP_AP_POWER_BTN 	= 1 ;
    KP_AP_VOLDN_BTN 	= 2 ;
    KP_AP_VOLUP_BTN 	= 3 ;
    KP_AP_VMUTE_BTN 	= 4 ;
    KP_AP_SRC_BCK_BTN 	= 5 ;
    KP_AP_SRC_FWD_BTN 	= 6 ;
    KP_AP_SPARE_BTN_7 	= 7 ;
    KP_AP_SPARE_BTN_8 	= 8 ;
    KP_AP_SPARE_BTN_9 	= 9 ;
    KP_AP_MUTE_ALL     	= 10 ;
    
    KP_AP_BTN_1  		= 11 ;
    KP_AP_BTN_2  		= 12 ;
    KP_AP_BTN_3  		= 13 ;
    KP_AP_BTN_4  		= 14 ;
    KP_AP_BTN_5  		= 15 ;
    KP_AP_BTN_6  		= 16 ;
    KP_AP_BTN_7  		= 17 ;
    KP_AP_BTN_8  		= 18 ;
    KP_AP_BTN_9  		= 19 ;
    KP_AP_BTN_10 		= 20 ;
                            
    KP_AP_BTN_SRC_1  	= 21 ;
    KP_AP_BTN_SRC_2  	= 22 ;
    KP_AP_BTN_SRC_3  	= 23 ;
    KP_AP_BTN_SRC_4  	= 24 ;
    KP_AP_BTN_SRC_5  	= 25 ;
    KP_AP_BTN_SRC_6  	= 26 ;
    KP_AP_BTN_SRC_7  	= 27 ;
    KP_AP_BTN_SRC_8  	= 28 ;
    KP_AP_BTN_SRC_9  	= 29 ;
    KP_AP_BTN_SRC_10 	= 30 ;
    KP_AP_BTN_SRC_11   	= 31 ;
    KP_AP_BTN_SRC_12   	= 32 ;
    KP_AP_BTN_SRC_13   	= 33 ;
    KP_AP_BTN_SRC_14   	= 34 ;
    KP_AP_BTN_SRC_15   	= 35 ;
    KP_AP_BTN_SRC_16   	= 36 ;
    KP_AP_BTN_SRC_17   	= 37 ;
    KP_AP_BTN_SRC_18   	= 38 ;
                            
    DEFINE_CONSTANT //LEVEL POINTER
    METKP_NUM_FB_LVLs	= 14 ;
    //from keypad via dvMETKP_Arry to this file
    METKP_LVL_DISPLY	= 1 ;//KP device level window for volume level display
    METKP_LVL_WHEEL		= 2 ;//KP volume level from wheel to this file for coversion and sending to module.
    
    //from module via vMETKP_Arry to this file
    METKP_LVL_FBVOL   	= 1 ;//used to send volume FB from module to this file for FB
    METKP_LVL_FBMUT   	= 2 ;//used to send  mute  FB from module to this file for FB
    METKP_LVL_FBSRC   	= 3 ;//used to send source FB from module to this file for FB
    
    //from this file to vMETKP_Arry 
    METKP_LVL_TX_VOL   	= 8 ;//used to send volume level from keypad wheel to the module to set the AP volume
    
    DEFINE_TYPE     //_sMETKPs
    
    STRUCTURE _sMETKPs
    
         {
         INTEGER nVol ;
         INTEGER nMute ;
         INTEGER nSrc ;
         }
    
    DEFINE_VARIABLE //_sMETKPs sMETKPs[METKP_NUM_AP_ZONES]
    
    PERSISTENT _sMETKPs sMETKPs[METKP_NUM_AP_ZONES] ;
    
    DEFINE_VARIABLE //INTEGER nDeBug_KPs   INTEGER nMETKP_OK_LVL
    
    VOLATILE INTEGER nMETKP_DeBug = 1 ;
    #WARN 'nDeBug_KPs set to 1'
    
    VOLATILE INTEGER nMETKP_OK_LVL = 0 ;
    
    DEFINE_VARIABLE //INTEGER nMETKP_TypeArry[METKP_NUM_KPs] //no type then offline, type = online
    
    VOLATILE INTEGER nMETKP_TypeArry[METKP_NUM_AP_ZONES] ;
    
    DEFINE_VARIABLE //DEV dvMETKP_Arry[METKP_NUM_AP_ZONES]
    
    VOLATILE DEV dvMETKP_Arry[METKP_NUM_AP_ZONES] =  //ADD DUMMIES AND PUT REAL KPS IN ORDER OF ZONES
    					        //no KP in zone, then put in the dummy (base virtual)
    					     { //notice the real device order. They match zones not DPS order
    					     vMETKP_Base,	//  zone 1	    1 *)'Media Room',    
    					     vMETKP_Base,     	//  zone 2          2 *)'Family Room',   
    					     vMETKP_Base,     	//  zone 3          3 *)'1st FL GBed',   
    					     vMETKP_Base,     	//  zone 4          4 *)'Foyer',         
    					     vMETKP_Base,     	//  zone 5          5 *)'Dining Room',   
    					     vMETKP_Base,     	//  zone 6          6 *)'Patio (R)',     
    					     vMETKP_Base,     	//  zone 7          7 *)'Patio (L)',     
    					     vMETKP_Base,    	//  zone 8          8 *)'Master Bed',   
    					     dvMETKP_1,     	//  zone 9          9 *)'Master Bath',   
    					     dvMETKP_2,     	//  zone 10         10*)'MB Porch',    
    					     dvMETKP_3,         //  zone 11         11*)'Purple Bed',    
    					     dvMETKP_7,         //  zone 12 	    12*)'Tan Bed',       
    					     dvMETKP_6,         //  zone 13         13*)'Hall Bath',   
    					     dvMETKP_4,		//  zone 14         14*)'Blue Bed',    
    					     dvMETKP_5,         //  zone 15         15*)'Guest''s Porch',
    					     vMETKP_Base     	//  zone 16         16*)'3rd Floor',     
    					     }   
    
    DEFINE_VARIABLE //DEV vMETKP_Arry[METKP_NUM_AP_ZONES]					     
    					     
    VOLATILE DEV vMETKP_Arry[METKP_NUM_AP_ZONES] =
    					     
    					     {
    					     vMETKP_1,		//  zone 1	    1 *)'Media Room',    
    					     vMETKP_2,  	//  zone 2          2 *)'Family Room',   
    					     vMETKP_3,  	//  zone 3          3 *)'1st FL GBed',   
    					     vMETKP_4,  	//  zone 4          4 *)'Foyer',         
    					     vMETKP_5,  	//  zone 5          5 *)'Dining Room',   
    					     vMETKP_6,  	//  zone 6          6 *)'Patio (R)',     
    					     vMETKP_7,  	//  zone 7          7 *)'Patio (L)',     
    					     vMETKP_8,  	//  zone 8          8 *)'Master Bed',   
    					     vMETKP_9,  	//  zone 9          9 *)'Master Bath',   
    					     vMETKP_10, 	//  zone 10         10*)'MB Porch',    
    					     vMETKP_11, 	//  zone 11         11*)'Purple Bed',    
    					     vMETKP_12,		//  zone 12 	    12*)'Tan Bed',       			     
    					     vMETKP_13, 	//  zone 13         13*)'Hall Bath',   
    					     vMETKP_14, 	//  zone 14         14*)'Blue Bed',    
    					     vMETKP_15, 	//  zone 15         15*)'Guest''s Porch',
    					     vMETKP_16  	//  zone 16         16*)'3rd Floor',     
    					     }			     
    
    DEFINE_VARIABLE //INTEGER nMETKP_APBtnArry[]
    
    VOLATILE INTEGER nMETKP_APBtnArry[] = //these can't change unless the module is changed!
         {	
         1,   // Power Zone On/Off
         2,   // Mute
         3,   // Direct Input Select 10*)'XM Radio',   
         4,   // Direct Input Select 9 *)'AM/FM 2', 
         5,   // Source BACK
         6,   // Source FWD
         7,   // 
         8,   // 
         9,   // 
         10,  // 
         11,  //    
         12,  // Vol Up   
         13,  // Vol Dwn    
         14   //  
         }    // only 14 button on metreau keypads
    //     15,  //    
    //     16,  //    
    //     17,  // 
    //     18,  // 
    //     19,  // //MUTE ALL
    //     20,  // 
    //     21,  // Direct Input Select 1_*)'Off',       
    //     22,  // Direct Input Select 2 )' Vortex 1',   
    //     23,  // Direct Input Select 3 *)'Vortex 2',	   
    //     24,  // Direct Input Select 4 *)'Bar CD',     
    //     25,  // Direct Input Select 5 *)'Media DVD/CD 
    //     26,  // Direct Input Select 6 *)'MBed DVD/CD' 
    //     27,  // Direct Input Select 7 *)'Bar iPod',   
    //     28,  // Direct Input Select 8 *)'AM/FM 1',   ,
    //     29,  // Direct Input Select 9 *)'AM/FM 2',    
    //     30,  // Direct Input Select 10*)'XM Radio',   
    //     31,  // Direct Input Select 11*)'Cable Music' 
    //     32,  // Direct Input Select 12*)'Sound B',    
    //     33,  // Direct Input Select 13// 
    //     34,  // Direct Input Select 14// 
    //     35,  // Direct Input Select 15// 
    //     36,  // Direct Input Select 16// 
    //     37,  // Direct Input Select 17// 
    //     38   // Direct Input Select 18// 
    //     } 
    
    DEFINE_FUNCTION fnMETKP_DeBug(CHAR iStr[])
    
         {
         if(nMETKP_DeBug)
    	  {
    	  SEND_STRING 0,"'MET_KPs.axi DEBUG: ',iStr"  ;
    	  }
                 
         RETURN  ;
         } 
    
    DEFINE_FUNCTION fnMETKP_DoFB(INTEGER iKP_Zone,INTEGER iLVL_Chnl,INTEGER iLVL_Value)
    
         {
         SWITCH(iLVL_Chnl)
    	  {
    	  CASE METKP_LVL_FBVOL:
    	       {
    	       sMETKPs[iKP_Zone].nVol = iLVL_Value ; 
    	       if(!nMETKP_OK_LVL)
    		    {
    		    if(sMETKPs[iKP_Zone].nSrc > 1)
    			 {
    			 SEND_LEVEL dvMETKP_Arry[iKP_Zone],METKP_LVL_DISPLY,TYPE_CAST(iLVL_Value * 2.55) ;
    			 }
    		    else
    			 {
    			 SEND_LEVEL dvMETKP_Arry[iKP_Zone],METKP_LVL_DISPLY,0 ;
    			 }
    		    }
    	       }
    	  CASE METKP_LVL_FBMUT:
    	       {
    	       sMETKPs[iKP_Zone].nMute = iLVL_Value ;
    	       [dvMETKP_Arry[iKP_Zone],METKP_LVL_FBVOL ] = sMETKPs[iKP_Zone].nMute ;
    	       [dvMETKP_Arry[iKP_Zone],1] = (sMETKPs[iKP_Zone].nSrc > 1) ;
    	       }
    	  CASE METKP_LVL_FBSRC:
    	       {
    	       sMETKPs[iKP_Zone].nSrc = iLVL_Value ;
    	       [dvMETKP_Arry[iKP_Zone],1] = (sMETKPs[iKP_Zone].nSrc > 1) ;
    	       [dvMETKP_Arry[iKP_Zone],3] = (sMETKPs[iKP_Zone].nSrc == 10) ;
    	       [dvMETKP_Arry[iKP_Zone],4] = (sMETKPs[iKP_Zone].nSrc == 11) ;
    	       }
    	  }
             
         RETURN ;
         } 
    
    DEFINE_FUNCTION fnMETKP_UpdateKP(INTEGER iKP_Indx)
    
         {
         STACK_VAR INTEGER n ;
         //STACK_VAR INTEGER i ;
         STACK_VAR INTEGER nKPCount ; 
         STACK_VAR INTEGER nLoopStart ; 
         
         if(iKP_Indx)
    	  {
    	  nKPCount = iKP_Indx ;
    	  nLoopStart = iKP_Indx ;
    	  }
         else
    	  {
    	  nKPCount = METKP_NUM_AP_ZONES ;
    	  nLoopStart = 1 ;
    	  }
    	  
         for(n = nLoopStart ; n <= nKPCount ; n++)
    	  {
    	  if(dvMETKP_Arry[n] != vMETKP_Base)
    	       {
    	       fnMETKP_DoFB(n,METKP_LVL_FBVOL,sMETKPs[n].nVol) ;
    	       fnMETKP_DoFB(n,METKP_LVL_FBMUT,sMETKPs[n].nMute) ;
    	       fnMETKP_DoFB(n,METKP_LVL_FBSRC,sMETKPs[n].nSrc) ;
    	       
    	       //for(i = 1 ; i <= METKP_NUM_FB_LVLs ; i++)
    //		    {
    //		    //fnMETKP_DoFB(INTEGER iKP_Zone,INTEGER iLVL_Chnl,INTEGER iLVL_Value)
    //		    }
    	       
    	       }
    	  }
         }
    
    DEFINE_START    //SET_VIRTUAL_LEVEL_COUNT(vMETKP_Base,METKP_NUM_AP_ZONES) ;
    
    SET_VIRTUAL_PORT_COUNT(vMETKP_Base,METKP_NUM_AP_ZONES) ;
    
    DEFINE_EVENT    //DATA_EVENT[vMETKP_Arry]
    
    DATA_EVENT[vMETKP_Arry]
    
         {
         ONLINE:
    	  {
    	  STACK_VAR INTEGER nVirtKP_Indx ;
    	  
    	  nVirtKP_Indx = DATA.DEVICE.PORT ;
    	  fnMETKP_DeBug("'KP Online- Indx=',itoa(nVirtKP_Indx),', DPS-:',fnDEV_TO_STRING(vMETKP_Arry[nVirtKP_Indx]),' :DEBUG <',ITOA(__LINE__),'>'") ;
    	  
    	  if(dvMETKP_Arry[nVirtKP_Indx] != vMETKP_Base)
    	       {//if the corresponding real device is a dummy don't set the virtuals levels, no need!
    	       SET_VIRTUAL_LEVEL_COUNT(vMETKP_Arry[nVirtKP_Indx],METKP_NUM_FB_LVLs) ;
    	       }
    	  }
         }
    
    DEFINE_EVENT    //DATA_EVENT[dvMETKP_Arry]
    
    DATA_EVENT[dvMETKP_Arry]
    
         {
         ONLINE:
    	  {
    	  STACK_VAR INTEGER nKP_Indx ;
    	  STACK_VAR INTEGER nDeviceID ;
    	  
    	  nKP_Indx = GET_LAST(dvMETKP_Arry) ;
    	  
    	  if(dvMETKP_Arry[nKP_Indx] != vMETKP_Base)
    	       {//if the corresponding real device is a dummy don't set the virtuals levels, no need!
    	       nMETKP_TypeArry[nKP_Indx] = (DEVICE_ID(dvMETKP_Arry[nKP_Indx]) - 168) ;
    	       //Set Brightness level for all LEDs (pushbuttons and levels indicator bar), for both On and Off states.
    	       SEND_COMMAND dvMETKP_Arry[nKP_Indx],'@BRT-24,0' ;//@BRT-<on brightness (0-32)>,<off brightness (0-32)>
    	       
    	       //Set Brightness level for Navigation Wheel LED, for both On and Off states.
    	       SEND_COMMAND dvMETKP_Arry[nKP_Indx],'@WBRT-30,0' ;//‘@WBRT-<on brightness (0-32)>,<off brightness (0-32)>’
    	       
    	       SEND_COMMAND dvMETKP_Arry[nKP_Indx],'BMODE-0' ;
    	       (*
    	       Sets the bargraph mode:
    	       Syntax:
    	       'BMODE-<bargraph mode 0-9>'
    	       Sets the specified bargraph to operate in one of the following modes:
    	       0 = (default) normal bar mode
    	       1 = normal dot mode (only one peak LED on at a time)
    	       2 = special bar mode (a level of 1-32 still has first LED on)
    	       3 = special dot mode (a level of 1-32 still has first LED on)
    	       4 = inverse normal bar mode
    	       5 = inverse normal dot mode
    	       6 = inverse special bar mode
    	       7 = inverse special dot mode
    	       8 = individual element, discrete mode
    	       9 = inverse individual element, discrete mode
    	       Example:
    	       SEND_COMMAND keypad,'BMODE-0'
    	       Sets the bargraph mode to default mode.
    	       *)
    	       fnMETKP_UpdateKP(nKP_Indx) ;
    	       }
    	  }
         OFFLINE:
    	  {
    	  STACK_VAR INTEGER nKP_Indx ;
    	  
    	  nKP_Indx = GET_LAST(dvMETKP_Arry) ;
    	  nMETKP_TypeArry[nKP_Indx] = 0 ;
    	  }
         }
         
    DEFINE_EVENT    //BUTTON_EVENT [dvMETKP_Arry,nMETKP_APBtnArry]
    
    BUTTON_EVENT [dvMETKP_Arry,nMETKP_APBtnArry]
         
         {
         PUSH :
    	  {
    	  STACK_VAR INTEGER nBtn ;
    	  STACK_VAR INTEGER nKP_Indx ;
    	  	    
    	  nBtn = GET_LAST(nMETKP_APBtnArry) ;
    	  nKP_Indx = GET_LAST(dvMETKP_Arry) ;
    	 
    	  SWITCH(nMETKP_TypeArry[nKP_Indx])
    	       {
    	       CASE METKP_TYPE_MET6N://BUTTONS 1-13
    		    {
    		    fnMETKP_DeBug("'KP MET6N Zone-',itoa(nKP_Indx),', DPS-',fnDEV_TO_STRING(dvMETKP_Arry[nKP_Indx]),', PUSHED_CHNL-',itoa(nBtn),' :DEBUG <',ITOA(__LINE__),'>'") ;
    		    
    		    if(sMETKPs[nKP_Indx].nSrc > 1 || nBtn == METKP_BTN_1) //already on or the power btn
    			 {
    			 SELECT ///    CHANGE ANY BUTTONS YOU MUST ALSO CHANGE THE RELEASE  !!!!!!
    			      {
    			      ACTIVE(nBtn == METKP_BTN_1):
    				   {
    				   DO_PUSH_TIMED(vMETKP_Arry[nKP_Indx],KP_AP_POWER_BTN,DO_PUSH_TIMED_5S_MAX) ;
    				   }
    			      ACTIVE(nBtn == METKP_BTN_2):
    				   {
    				   DO_PUSH_TIMED(vMETKP_Arry[nKP_Indx],KP_AP_VMUTE_BTN,DO_PUSH_TIMED_5S_MAX) ;
    				   }
    			      ACTIVE(nBtn == METKP_BTN_3):
    				   {
    				   DO_PUSH_TIMED(vMETKP_Arry[nKP_Indx],KP_AP_BTN_SRC_10 ,DO_PUSH_TIMED_5S_MAX) ;
    				   }
    			      ACTIVE(nBtn == METKP_BTN_4):
    				   {
    				   DO_PUSH_TIMED(vMETKP_Arry[nKP_Indx],KP_AP_BTN_SRC_11 ,DO_PUSH_TIMED_5S_MAX) ;
    				   }
    			      ACTIVE(nBtn == METKP_BTN_5):
    				   {
    				   TO[dvMETKP_Arry[nKP_Indx],nBtn] ;
    				   DO_PUSH_TIMED(vMETKP_Arry[nKP_Indx],KP_AP_SRC_BCK_BTN ,DO_PUSH_TIMED_5S_MAX) ;
    				   }
    			      ACTIVE(nBtn == METKP_BTN_6):
    				   {
    				   TO[dvMETKP_Arry[nKP_Indx],nBtn] ;
    				   DO_PUSH_TIMED(vMETKP_Arry[nKP_Indx],KP_AP_SRC_FWD_BTN,DO_PUSH_TIMED_5S_MAX) ;
    				   }
    				   
    			      ACTIVE(nBtn == METKP_BTN_7): {DO_PUSH_TIMED(vMETKP_Arry[nKP_Indx],KP_AP_VOLUP_BTN,DO_PUSH_TIMED_5S_MAX) ;}  	//METKP_BTN_UP	
    			      ACTIVE(nBtn == METKP_BTN_8): {DO_PUSH_TIMED(vMETKP_Arry[nKP_Indx],KP_AP_VOLDN_BTN,DO_PUSH_TIMED_5S_MAX) ;}  	//METKP_BTN_DN	
    			      ACTIVE(nBtn == METKP_BTN_9): {DO_PUSH_TIMED(vMETKP_Arry[nKP_Indx],KP_AP_VOLDN_BTN,DO_PUSH_TIMED_5S_MAX) ;}  	//METKP_BTN_LEFT	
    			      ACTIVE(nBtn == METKP_BTN_10):{DO_PUSH_TIMED(vMETKP_Arry[nKP_Indx],KP_AP_VOLUP_BTN,DO_PUSH_TIMED_5S_MAX) ;} 	//METKP_BTN_RIGHT	
    			      ACTIVE(nBtn == METKP_BTN_11):{}	//METKP_BTN_CENTER
    			     
    			      ACTIVE(nBtn == METKP_BTN_12 || nBtn == METKP_BTN_13)://METKP_BTN_ROTATE_R //METKP_BTN_ROTATE_L
    				   {                         
    				   nMETKP_OK_LVL = 1 ;
    				   }
    			      ACTIVE(1)://Do Nothing, impossible to occur!
    				   {
    				   fnMETKP_DeBug("'PUSHED_CHNL-',itoa(nBtn ),', NON SUPPORTED CHANNEL, HOW? :DEBUG <',ITOA(__LINE__),'>'") ;
    				   }
    			      }
    			 }
    		    }
    	       CASE METKP_TYPE_MET7://BUTTONS 1-8
    		    {
    		    fnMETKP_DeBug("'KP MET7 Zone-',itoa(nKP_Indx),', DPS-',fnDEV_TO_STRING(dvMETKP_Arry[nKP_Indx]),', PUSHED_CHNL-',itoa(nBtn),' :DEBUG <',ITOA(__LINE__),'>'") ;
    			 
    		    SELECT
    			 {
    			 ACTIVE(nBtn == METKP_BTN_1):{}
    			 ACTIVE(nBtn == METKP_BTN_2):{}
    			 ACTIVE(nBtn == METKP_BTN_3):{}
    			 ACTIVE(nBtn == METKP_BTN_4):{}
    			 ACTIVE(nBtn == METKP_BTN_5):{}
    			 ACTIVE(nBtn == METKP_BTN_6):{}
    			 ACTIVE(nBtn == METKP_BTN_7):{}		
    			 ACTIVE(nBtn == METKP_BTN_8):{}        	
    			 ACTIVE(1)://Do Nothing, impossible to occur!
    			      {
    			      fnMETKP_DeBug("'PUSHED_CHNL-',itoa(nBtn ),', NON SUPPORTED CHANNEL, HOW? :DEBUG <',ITOA(__LINE__),'>'") ;
    			      }
    			 }
    		    }
    	       CASE METKP_TYPE_MET13://BUTTONS 1-14
    		    {
    		    fnMETKP_DeBug("'KP MET13 Zone-',itoa(nKP_Indx),', DPS-',fnDEV_TO_STRING(dvMETKP_Arry[nKP_Indx]),', PUSHED_CHNL-',itoa(nBtn),' :DEBUG <',ITOA(__LINE__),'>'") ;
    		    
    		    SELECT
    			 {
    			 ACTIVE(nBtn == METKP_BTN_1):{}
    			 ACTIVE(nBtn == METKP_BTN_2):{}
    			 ACTIVE(nBtn == METKP_BTN_3):{}
    			 ACTIVE(nBtn == METKP_BTN_4):{}
    			 ACTIVE(nBtn == METKP_BTN_5):{}
    			 ACTIVE(nBtn == METKP_BTN_6):{}
    			 ACTIVE(nBtn == METKP_BTN_7):{}	       
    			 ACTIVE(nBtn == METKP_BTN_8):{}        
    			 ACTIVE(nBtn == METKP_BTN_9):{}        
    			 ACTIVE(nBtn == METKP_BTN_10):{}       
    			 ACTIVE(nBtn == METKP_BTN_11):{}       
    			 ACTIVE(nBtn == METKP_BTN_12):{}       
    			 ACTIVE(nBtn == METKP_BTN_13):{}       
    			 ACTIVE(nBtn == METKP_BTN_14):{}       
    			 ACTIVE(1)://Do Nothing, impossible to occur!
    			      {
    			      fnMETKP_DeBug("'PUSHED_CHNL-',itoa(nBtn ),', NON SUPPORTED CHANNEL, HOW? :DEBUG <',ITOA(__LINE__),'>'") ;
    			      }
    			 }
    		    }
    	       DEFAULT:
    		    {
    		    fnMETKP_DeBug("'KP Err! No KP Type! Zone-',itoa(nKP_Indx),', DPS-',fnDEV_TO_STRING(dvMETKP_Arry[nKP_Indx]),', PUSHED_CHNL-',itoa(nBtn),' :DEBUG <',ITOA(__LINE__),'>'") ;
    		    }
    	       }
    	  }
         RELEASE:
    	  {
    	  STACK_VAR INTEGER nBtn ;
    	  STACK_VAR INTEGER nKP_Indx ;
    	  	    
    	  nBtn = GET_LAST(nMETKP_APBtnArry) ;
    	  nKP_Indx = GET_LAST(dvMETKP_Arry) ;
    	  
    	  SWITCH(nMETKP_TypeArry[nKP_Indx])
    	       {
    	       CASE METKP_TYPE_MET6N://BUTTONS 1-13
    		    {
    		    fnMETKP_DeBug("'KP MET6N Zone-',itoa(nKP_Indx),', DPS-',fnDEV_TO_STRING(dvMETKP_Arry[nKP_Indx]),', RELEASED_CHNL-',itoa(nBtn),' :DEBUG <',ITOA(__LINE__),'>'") ;
    		    
    		    SELECT
    			 {
    			 ACTIVE(nBtn == METKP_BTN_1):
    			      {
    			      DO_RELEASE(vMETKP_Arry[nKP_Indx],KP_AP_POWER_BTN) ;
    			      }
    			 ACTIVE(nBtn == METKP_BTN_2):
    			      {
    			      DO_RELEASE(vMETKP_Arry[nKP_Indx],KP_AP_VMUTE_BTN) ;
    			      }
    			 ACTIVE(nBtn == METKP_BTN_3):
    			      {
    			      DO_RELEASE(vMETKP_Arry[nKP_Indx],KP_AP_BTN_SRC_10) ;
    			      }
    			 ACTIVE(nBtn == METKP_BTN_4):
    			      {
    			      DO_RELEASE(vMETKP_Arry[nKP_Indx],KP_AP_BTN_SRC_11) ;
    			      }
    			 ACTIVE(nBtn == METKP_BTN_5):
    			      {
    			      DO_RELEASE(vMETKP_Arry[nKP_Indx],KP_AP_SRC_BCK_BTN) ;
    			      }
    			 ACTIVE(nBtn == METKP_BTN_6):
    			      {
    			      DO_RELEASE(vMETKP_Arry[nKP_Indx],KP_AP_SRC_FWD_BTN) ;
    			      }
    			      
    			 ACTIVE(nBtn == METKP_BTN_7): {DO_RELEASE(vMETKP_Arry[nKP_Indx],KP_AP_VOLUP_BTN) ;}	//METKP_BTN_UP	
    			 ACTIVE(nBtn == METKP_BTN_8): {DO_RELEASE(vMETKP_Arry[nKP_Indx],KP_AP_VOLDN_BTN) ;}   	//METKP_BTN_DN	
    			 ACTIVE(nBtn == METKP_BTN_9): {DO_RELEASE(vMETKP_Arry[nKP_Indx],KP_AP_VOLDN_BTN) ;}   	//METKP_BTN_LEFT	
    			 ACTIVE(nBtn == METKP_BTN_10):{DO_RELEASE(vMETKP_Arry[nKP_Indx],KP_AP_VOLUP_BTN) ;}  	//METKP_BTN_RIGHT
    			 ACTIVE(nBtn == METKP_BTN_11):{}  	//METKP_BTN_CENTER
    			
    			 ACTIVE(nBtn == METKP_BTN_12 || nBtn == METKP_BTN_13):	//METKP_BTN_ROTATE_R  //METKP_BTN_ROTATE_L
    			      {                         
    			      nMETKP_OK_LVL = 0 ;
    			      }			      
    			 ACTIVE(1)://Do Nothing, impossible to occur!
    			      {
    			      fnMETKP_DeBug("'PUSHED_CHNL-',itoa(nBtn ),', NON SUPPORTED CHANNEL, HOW? :DEBUG <',ITOA(__LINE__),'>'") ;
    			      }
    			 }
    		    }
    	       CASE METKP_TYPE_MET7://BUTTONS 1-8
    		    {
    		    fnMETKP_DeBug("'KP MET7 Zone-',itoa(nKP_Indx),', DPS-',fnDEV_TO_STRING(dvMETKP_Arry[nKP_Indx]),', RELEASED_CHNL-',itoa(nBtn),' :DEBUG <',ITOA(__LINE__),'>'") ;
    		    SELECT
    			 {
    			 ACTIVE(nBtn == METKP_BTN_1):{}
    			 ACTIVE(nBtn == METKP_BTN_2):{}
    			 ACTIVE(nBtn == METKP_BTN_3):{}
    			 ACTIVE(nBtn == METKP_BTN_4):{}
    			 ACTIVE(nBtn == METKP_BTN_5):{}
    			 ACTIVE(nBtn == METKP_BTN_6):{}
    			 ACTIVE(nBtn == METKP_BTN_7):{}		
    			 ACTIVE(nBtn == METKP_BTN_8):{}  
    			 ACTIVE(1)://Do Nothing, impossible to occur!
    			      {
    			      fnMETKP_DeBug("'RELEASE_CHNL-',itoa(nBtn ),', NON SUPPORTED CHANNEL, HOW? :DEBUG <',ITOA(__LINE__),'>'") ;
    			      }
    			 }
    		    }
    	       CASE METKP_TYPE_MET13://BUTTON 1-14
    		    {
    		    fnMETKP_DeBug("'KP MET13 Zone-',itoa(nKP_Indx),', DPS-',fnDEV_TO_STRING(dvMETKP_Arry[nKP_Indx]),', RELEASED_CHNL-',itoa(nBtn),' :DEBUG <',ITOA(__LINE__),'>'") ;
    		    SELECT
    			 {
    			 ACTIVE(nBtn == METKP_BTN_1):{}
    			 ACTIVE(nBtn == METKP_BTN_2):{}
    			 ACTIVE(nBtn == METKP_BTN_3):{}
    			 ACTIVE(nBtn == METKP_BTN_4):{}
    			 ACTIVE(nBtn == METKP_BTN_5):{}
    			 ACTIVE(nBtn == METKP_BTN_6):{}
    			 ACTIVE(nBtn == METKP_BTN_7):{} 	
    			 ACTIVE(nBtn == METKP_BTN_8):{}         
    			 ACTIVE(nBtn == METKP_BTN_9):{}         
    			 ACTIVE(nBtn == METKP_BTN_10):{}        
    			 ACTIVE(nBtn == METKP_BTN_11):{}        
    			 ACTIVE(nBtn == METKP_BTN_12):{}	
    			 ACTIVE(nBtn == METKP_BTN_13):{}	
    			 ACTIVE(nBtn == METKP_BTN_14):{}
    			 ACTIVE(1)://Do Nothing, impossible to occur!
    			      {
    			      fnMETKP_DeBug("'RELEASE_CHNL-',itoa(nBtn ),', NON SUPPORTED CHANNEL, HOW? :DEBUG <',ITOA(__LINE__),'>'") ;
    			      }
    			 }
    		    }
    	       DEFAULT:
    		    {
    		    fnMETKP_DeBug("'KP Err! No KP Type! Zone-',itoa(nKP_Indx),', DPS-',fnDEV_TO_STRING(dvMETKP_Arry[nKP_Indx]),', RELEASED_CHNL-',itoa(nBtn),' :DEBUG <',ITOA(__LINE__),'>'") ;
    		    }
    	       }
    	  }
         }
         
    DEFINE_EVENT    //LEVEL_EVENT LEVEL_EVENT [vMETKP_Arry,0]
         
    LEVEL_EVENT [dvMETKP_Arry,METKP_LVL_WHEEL]//= 2         
    
         {
         STACK_VAR INTEGER nKP_Zone ;
         LOCAL_VAR INTEGER nKP_LevelWheel ;
         
         nKP_LevelWheel = LEVEL.VALUE ;
         if(nMETKP_OK_LVL)
    	  {
    	  nKP_Zone = GET_LAST(dvMETKP_Arry) ;
    	  fnMETKP_DeBug("'KP Zone-',itoa(nKP_Zone),', DPS-',fnDEV_TO_STRING(dvMETKP_Arry[nKP_Zone]),', RX LVL CHNL-',itoa(LEVEL.INPUT.LEVEL),', VALUE-',itoa(LEVEL.VALUE),':DEBUG <',ITOA(__LINE__),'>'") ;  
         
    	  SEND_LEVEL dvMETKP_Arry[nKP_Zone],METKP_LVL_DISPLY,LEVEL.VALUE ;
    	  SEND_LEVEL vMETKP_Arry[nKP_Zone],METKP_LVL_TX_VOL,TYPE_CAST(LEVEL.VALUE / 2.55) ;//convert to base 100
    	  }
         else
    	  {
    	  fnMETKP_DeBug("'KP Zone-',itoa(nKP_Zone),', RX LVL ERR! nMETKP_OK_LVL = 0, CHNL-',itoa(LEVEL.INPUT.LEVEL),', VALUE-',itoa(LEVEL.VALUE),':DEBUG <',ITOA(__LINE__),'>'") ;  
    	  }
         }     
    
    DEFINE_EVENT    //LEVEL_EVENT [vMETKP_Arry,1,2,3        
    
    LEVEL_EVENT [vMETKP_Arry,METKP_LVL_FBVOL]//= 1 ;
    LEVEL_EVENT [vMETKP_Arry,METKP_LVL_FBMUT]//= 2 ;
    LEVEL_EVENT [vMETKP_Arry,METKP_LVL_FBSRC]//= 3 ;         
                              
         {
         STACK_VAR INTEGER nKP_Zone ;
         
         nKP_Zone = LEVEL.SOURCEDEV.PORT ;
         fnMETKP_DeBug("'KP-',itoa(nKP_Zone),', DPS-',fnDEV_TO_STRING(vMETKP_Arry[nKP_Zone]),', RX LVL CHNL-',itoa(LEVEL.INPUT.LEVEL),', VALUE-',itoa(LEVEL.VALUE),':DEBUG <',ITOA(__LINE__),'>'") ;  
         
         fnMETKP_DoFB(nKP_Zone,LEVEL.INPUT.LEVEL,LEVEL.VALUE) ;
         }     
    
    DEFINE_PROGRAM  //EMPTY
         
    (*(VAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAV)*)
    (*(                         THE END                         )*)
    (*(VAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAV)*)     
    

    a-riot42 wrote:
    I generally don't do arithmetic on virtual device numbers ever. The way I address my virtuals is at the very end of programming before installation I highlight the whole column and hit Alt+M. The numbers are more or less arbitrary and IMHO adding significance to arbitrariness is asking for trouble. It sounds like you might be one of those programmers that thinks its clever to use the channel number of a button to recall a TV station with the excuse that you can tell the station just by looking at the channel number. Down this road lies madness.
    I wasn't reffering to doing math on a virtual in code but rather in my head when doing diagnotics and setting up notification options. I know if I want to watch zone 16's virtual and can just type that in with out thinking too hard otherwise I don't care what numbers they are.
  • DHawthorneDHawthorne Posts: 4,584
    If you temporarily assign different device numbers instead of different ports on the identical code, that should tell you if the whole port issue is a red herring or not. But really, as long as the port is different, it should come in as an entirely different device.

    My other thought is that something else happening during the initialization sequences is goofing up the online events; overrunning a buffer or stalling the message queue.
  • rfletcherrfletcher Posts: 217
    vining wrote: »
    I definitely won't rule out something in my code causing this but I don't know what that might be. I also don't know what causes the delay between port 1 and the rest of the ports either. I have no waits that would affect this but maybe I have that virtual on port 1 defined somewhere else so that came online first?

    I think I know why the there's a delay for port's higher than 1 coming online. The online event doesn't trigger until you call set_virtual_port_count(), so those devices don't come online until define_start runs.

    I used to use multiple ports on virtual devices, but I ran into some weird issues with certain AMX modules, including one that refused to function at all on anything other than port 1. I haven't tried it in a few years, though I'm tempted to do so again since all of the few AMX-sourced modules I use at this point are Duet based, and I don't recall having any problems with any of my own modules. It'd make things a bit neater.
    a_riot42 wrote: »
    I didn't say it shouldn't be allowed. Obviously there are times when you absolutely need the capability of having a virtual device with multiple ports. But it sounds like what you are doing is using the port number to differentiate between virtual devices rather than the device number, and now finding the unintended consequences of that. If they are different devices then they should have different device numbers. If its the same device with multiple ports then that is how is should be coded. Again, I see no advantage in doing this. If there is one, let us know.

    When you get down to it, virtually everything you aren't controlling over IP is a device with multiple ports. It's either a NI series controller of some kind or a netlinx control card of some kind, or a touchpanel of some kind. The distinction you're drawing here is artificial.
  • a_riot42a_riot42 Posts: 1,624
    rfletcher wrote: »
    When you get down to it, virtually everything you aren't controlling over IP is a device with multiple ports. It's either a NI series controller of some kind or a netlinx control card of some kind, or a touchpanel of some kind. The distinction you're drawing here is artificial.

    I don't follow. A controller device has a device ID of 5001 and you access the different ports using the D:P:S nomenclature but since its the same device the device number doesn't change. If you open the online tree you will see each virtual device has a different device number, not the same device number with a different port. I don't think the distinction between a device, port and system is artificial at least not in my experience. A dev is a structure with these three fields I believe, and using them for purposes other than what they were designed could lead to a lot of hair pulling. Netlinx gives you enough rope to hang yourself, but that doesn't mean you should.
    Paul
  • a_riot42a_riot42 Posts: 1,624
    vining wrote: »
    a-riot42 wrote:

    I wasn't reffering to doing math on a virtual in code but rather in my head when doing diagnotics and setting up notification options. I know if I want to watch zone 16's virtual and can just type that in with out thinking too hard otherwise I don't care what numbers they are.

    If you number your keypad virtuals at say 33101 through 33116 then doesn't that accomplishe the same thing without having to contort the DPS paradigm?
    Paul
  • viningvining Posts: 4,368
    a_riot42 wrote: »
    If you number your keypad virtuals at say 33101 through 33116 then doesn't that accomplishe the same thing without having to contort the DPS paradigm?
    Paul
    Yeah, that would also work but changing ports isn't contorting the DPS paradigm and should work. Obviously as Joe illustrated it does work and as he mentioned it is the paradigm used in many Duet modules by the so called professionals so it's not like I'm trying to re-invent the wheel.

    Anyway back to the issue. I went through my code and didn't see any duplicated virtuals and then loaded the newest NI3100 firimware. I was running v3.50.430 and now I'm running v3.50.439. Of course now I can't reproduce the error but I don't think it was fixed by the firmware change. I think it's just a flaky issue.

    I was thinking that it could be caused by my base DPS used in the SET_VIRT_PORT_COUNT since that was 33048:0:0. I was thinking maybe the "0" for the port might screw up the function. Of course here I think the function would only care about the dev.number but who knows. I wanted to use this as my dummy DPS which is why I set the port to zero but I now set it to 1 and created a legitimate dummy outside the DPS range of the used keypads. Still changing it back and forth from 0 to 1 I still couldn't replicate the error. I do feel that using a DPS with a legitimate port number is probably better and it's also what Joe used. Otherwise the both code blocks are pretty much the same and his worked while mine didn't. Maybe his gremlins were sleeping while mine were up and active. Now today they're back to sleep. :)

    rfletcher wrote:
    I think I know why the there's a delay for port's higher than 1 coming online. The online event doesn't trigger until you call set_virtual_port_count(), so those devices don't come online until define_start runs.
    That's what I was thinking too and probably the case.
  • rfletcherrfletcher Posts: 217
    a_riot42 wrote: »
    I don't follow. A controller device has a device ID of 5001 and you access the different ports using the D:P:S nomenclature but since its the same device the device number doesn't change. If you open the online tree you will see each virtual device has a different device number, not the same device number with a different port. I don't think the distinction between a device, port and system is artificial at least not in my experience. A dev is a structure with these three fields I believe, and using them for purposes other than what they were designed could lead to a lot of hair pulling. Netlinx gives you enough rope to hang yourself, but that doesn't mean you should.
    Paul

    I'm sorry, reading that again I don't think I did a good job of explaining what I meant clearly. Let me try again.
    If they are different devices then they should have different device numbers. If its the same device with multiple ports then that is how is should be coded.

    If I understood you correctly, you are saying here that you should not use multiple ports on a virtual device unless the device you are controlling has multiple ports. However, at the base level nearly every non-virtual device we can control has multiple ports, be it an NI controller, a control card, the master and it's IP ports, or a touch panel, or some other native netlinx device. It's only when you abstract out a layer and ignore the serial port (or whatever interface you're using) itself that it appears you have devices with only a single port.

    So either multiple ports on a virtual device should never be used to control anything, or it's ok to use multiple ports on a virtual device to control anything, but saying that multiple virtual ports should only be used with devices that have multiple ports doesn't seem to make any sense to me, as there are (almost) no non-virtual devices that have only a single port.

    Hopefully that makes a bit more sense...
  • a_riot42a_riot42 Posts: 1,624
    Joe Hebert wrote: »
    GET_LAST() with an array of virtual devices even with the same device number and different ports works fine for me. DUET modules use this method all the time.

    Duet modules use different ports for outputs on a single switch for instance but they still use the same device number for the same device. In Duet if you had two switchers and wanted to do what Vining is doing it would look like this:
    // First Switch
    41001:1:0
    41001:2:0
    41001:3:0
    41001:4:0
    41001:5:0
    41001:6:0
    41001:7:0
    41001:8:0
    
    // Second Switch
    41001:9:0
    41001:10:0
    41001:11:0
    41001:12:0
    41001:13:0
    41001:14:0
    41001:15:0
    41001:16:0
    
    

    get_last will work with any device array, it just returns the index of the device that last triggered the event. That's why I don't think the issue has anything to do with get_last and may be one of those hard to trace bugs that only crop up in certain circumstances caused by doing unorthodox programming. Like you mention get_last has no issues differentiating between devices with different port numbers but same device number so obviously get_last isn't the problem here.
    Paul
  • HedbergHedberg Posts: 671
    This thread reminds me of a joke that I first heard when I was about five:

    Guy goes to a doctor and say, "it hurts when I move my arm like this." Doctor responds, "then don't move your arm like that." I thought that joke was funny when I was about five, these days I find it about as funny as a rubber crutch.
  • viningvining Posts: 4,368
    a_riot42 wrote: »
    Duet modules use different ports for outputs on a single switch for instance but they still use the same device number for the same device. In Duet if you had two switchers and wanted to do what Vining is doing it would look like this:
    // First Switch
    41001:1:0
    41001:2:0
    41001:3:0
    41001:4:0
    41001:5:0
    41001:6:0
    41001:7:0
    41001:8:0
    
    // Second Switch
    41001:9:0
    41001:10:0
    41001:11:0
    41001:12:0
    41001:13:0
    41001:14:0
    41001:15:0
    41001:16:0
    
    
    So what's the harm in having the same dev.number for multiple devices as long as each has a unique DPS by port or system. In my code each virtual is mated to a real KP that's used in controlling an output zone on an auto patch switcher. So is it a virtual for the keypad or a virtual for the switcher. Neither it's a virtual and in this case a bridge between the two so why in one case it perfectly ok and in the other it isn't? it makes no difference!

    get_last will work with any device array, it just returns the index of the device that last triggered the event. That's why I don't think the issue has anything to do with get_last and may be one of those hard to trace bugs that only crop up in certain circumstances caused by doing unorthodox programming. Like you mention get_last has no issues differentiating between devices with different port numbers but same device number so obviously get_last isn't the problem here.
    Paul

    Granted it may be considered unorthodox by a real programmer and when I find one I'll ask him or her what they think but for now I'd just like to know why something that by the books should work, doesn't. It may be very well be a bug and maybe I can't do this the way I want and that's ok. It very well may be my code and that's fine too. But for now there's an issue and it would be nice to know why and just because it's not something you do doesn't make it wrong or unorthodox it's just not something you do and the rest of this imaginary issue you have with it is just in your head and that's ok too. :)
Sign In or Register to comment.