Home AMX User Forum NetLinx Studio

Waits at the end of functions?

Why can't I have a wait at the end of a function? I Was trying to build functions for source selection buttons, and I'm trying to implement a wait for the tvs turn on delay. What would be a better approach?

Comments

  • jjamesjjames Posts: 2,908
    vegastech wrote: »
    Why can't I have a wait at the end of a function? I Was trying to build functions for source selection buttons, and I'm trying to implement a wait for the tvs turn on delay. What would be a better approach?

    You can have WAITs in functions, you'll just need one for every combination. ;)

    Consider this . . .
    Paramter A = 1
    Function is called with parameter A.
    Function is executed upon parameter A.
    WAIT is encountered, and retains parameter A.
    Function is called again, this time parameter A = 2.
    WAIT is executed - which do you think it's going to use? 1 or 2?

    Answer = 2.

    The best way to use WAITs in a function is to spell them out. Do a SWITCH/CASE or SELECT/ACTIVE on the parameter value, then do the WAIT. Do any CANCEL_WAITs if necessary.

    For your example, this approach would probably work best (and this is what I do as well.)

    Note - this is an example, not verbatim code with what I do.
    DEFINE_FUNCTION fnTV_POWER(nTV_NUM)
    {
    	SWITCH(nTV_NUM)
    	{
    		// TV number 1 in dv_TV array
    		CASE 1:
    		{
    			SEND_COMMAND dv_TV[1],"'SP',nTV_INPUT[1] + 80" 		// Send input
    			SEND_COMMAND dv_TV[1],"'SP',27" 			// Power TV On
    			
    			WAIT 120
    				SEND_COMMAND dv_TV[1],"'SP',nTV_INPUT[1] + 80"	// Send input
    		}
    		
    		// TV number 2 in dv_TV array
    		CASE 2:
    		{
    			SEND_COMMAND dv_TV[2],"'SP',nTV_INPUT[2] + 80" 		// Send input
    			SEND_COMMAND dv_TV[2],"'SP',27" 			// Power TV On
    			
    			WAIT 120
    				SEND_COMMAND dv_TV[2],"'SP',nTV_INPUT[2] + 80"	// Send input
    		}
    		
    		// TV number 3 in dv_TV array
    		CASE 3:
    		{
    			SEND_COMMAND dv_TV[3],"'SP',nTV_INPUT[3] + 80" 		// Send input
    			SEND_COMMAND dv_TV[3],"'SP',27" 			// Power TV On
    			
    			WAIT 120
    				SEND_COMMAND dv_TV[3],"'SP',nTV_INPUT[3] + 80"	// Send input
    		}
    	}
    }
    

    Like I said, this is a loose example as to what I do. I would typically remove the first two statements (the initial input & power statements), and make them work with the array - and because they need to be executed immediately, I can use the parameter value (nTV_NUM) as opposed to "spelling" it out ([1], [2], or [3]) The second input command I must specify them or else I'll be bitten if handling more than one TV at once.

    Hope this makes sense; there's actually a thread on here talking about the specifics - but can't find it off hand.
  • Joe HebertJoe Hebert Posts: 2,159
    a_riot42 wrote:
    Couldn't that all be bummed down to:
    DEFINE_FUNCTION fnTV_POWER(nTV_NUM)
    {
      SEND_COMMAND dv_TV[nTV_NUM],"'SP',nTV_INPUT[nTV_NUM] + 80" 		// Send input
      SEND_COMMAND dv_TV[nTV_NUM],"'SP',27" 			// Power TV On
      WAIT 120
        SEND_COMMAND dv_TV[nTV_NUM],"'SP',nTV_INPUT[nTV_NUM] + 80"	// Send input
    }
    

    I?ve never heard that term before but the answer is no, it will not work as desired.
    nTV_Num is evaluated after the WAIT expires not when the WAIT is thrown into the queue.
  • jjamesjjames Posts: 2,908
    To bum it down, I'd do something like this:
    DEFINE_FUNCTION fnTV_POWER(nTV_NUM)
    {
            SEND_COMMAND dv_TV[nTV_NUM],"'SP',nTV_INPUT[nTV_NUM] + 80" 		// Send input
    	SEND_COMMAND dv_TV[nTV_NUM],"'SP',27" 			// Power TV On
    
    	SWITCH(nTV_NUM)
    	{
    		// TV number 1 in dv_TV array
    		CASE 1:
    		{	
    			WAIT 120
    				SEND_COMMAND dv_TV[1],"'SP',nTV_INPUT[1] + 80"	// Send input
    		}
    		
    		// TV number 2 in dv_TV array
    		CASE 2:
    		{
    			WAIT 120
    				SEND_COMMAND dv_TV[2],"'SP',nTV_INPUT[2] + 80"	// Send input
    		}
    		
    		// TV number 3 in dv_TV array
    		CASE 3:
    		{
    			WAIT 120
    				SEND_COMMAND dv_TV[3],"'SP',nTV_INPUT[3] + 80"	// Send input
    		}
    	}
    }
    
  • HedbergHedberg Posts: 671
    Hey, are we being repressed again?

    I've just been looking at this a little and it appears to me that if a function with a wait is called again before the expiration of the wait from the first call that the second wait doesn't happen.

    For example:
    define_function fnTestWait(integer dummy)
    {
    	send_string 0, "'Dummy = ',itoa(dummy)"
    	wait 300
    		send_string 0, "'Dummy = ',itoa(dummy)"
    }
    
    channel_event[dvio,2]
    {
    	on:
    	{
    		nArgument = 999
    		fnTestWait(nArgument)
    		wait 150
    		{
    				nArgument = 1
    			fnTestWait(nArgument)
    		}		
    	}
    }
    

    seems to cause three strings to be sent to the console:
    1 @ time = 0
    1 @ time = 15 seconds
    1 @ time = 30 seconds

    I would expect the second call to the function to generate two strings to the console, the second at time = 45 seconds, but that doesn't seem to be the case.

    Seems to me that waits in functions don't necessarily work as one might expect.
  • Joe HebertJoe Hebert Posts: 2,159
    Hedberg wrote:
    I've just been looking at this a little and it appears to me that if a function with a wait is called again before the expiration of the wait from the first call that the second wait doesn't happen.
    That?s correct. Once a WAIT is encountered and put into the queue any subsequent encounters with that same WAIT are ignored. It has nothing to do with a function, that?s just the way it works. Think of it the same as a WAIT in DEFINE_PROGRAM. If you have something like:
    DEFINE_PROGRAM
    
    WAIT 5 SEND_STRING 0,??Watchdog??
    
    Even though the WAIT is encountered thousands of times a second, the string is only printed out once every half second.
    Hedberg wrote:
    Seems to me that waits in functions don't necessarily work as one might expect.
    There is a problem (or at least used to be) with WAITs inside a function but it?s in reference to a different subject. I can?t recall offhand exactly what it was, something about integer arrays I think. If I can find the thread I?ll post a link to it.
  • Joe HebertJoe Hebert Posts: 2,159
    Joe Hebert wrote: »
    There is a problem (or at least used to be) with WAITs inside a function but it?s in reference to a different subject. I can?t recall offhand exactly what it was, something about integer arrays I think. If I can find the thread I?ll post a link to it.
    Here is the thread I was talking about from January of 2006 which verifies the bug. I just did a quick test and it appears the bug still exists.

    http://amxforums.com/showpost.php?p=6913&postcount=7
  • HedbergHedberg Posts: 671
    Joe Hebert wrote: »
    That’s correct. Once a WAIT is encountered and put into the queue any subsequent encounters with that same WAIT are ignored. It has nothing to do with a function, that’s just the way it works. [...]

    My expectation would have been that waits generated by two different function calls would not be the same wait. That is, apparently, not the way it works and I'm surprised by that. Oh, well.

    Added:

    Let me explain a bit why I'm surprised by the way things are. In the code samples above that jjames provided, he expects that 12 seconds after the function is called that a send_command will execute. But, if a subsequent function call is made within 12 seconds, the subsequent send_command expected will not occur. Button events appear to work the same way. So, if you try to turn on multiple tvs within a 12 second period, the function may not perform as the programmer intended.

    We deal with the environment as it exists and not as we would like it, so I'll get over this, even though I'm not quite sure how to get around the problem.
  • Joe HebertJoe Hebert Posts: 2,159
    Hedberg wrote:
    My expectation would have been that waits generated by two different function calls would not be the same wait.
    I was told to think of all WAITs as named (I believe the line number is involved.) If you don?t explicitly name the WAIT then you can?t cancel or manipulate the WAIT. The same rules apply though for any WAIT, once the WAIT is in the queue anytime the program runs into the same WAIT it?s ignored.
    Hedberg wrote:
    In the code samples above that jjames provided, he expects that 12 seconds after the function is called that a send_command will execute. But, if a subsequent function call is made within 12 seconds, the subsequent send_command expected will not occur. ... So, if you try to turn on multiple tvs within a 12 second period, the function may not perform as the programmer intended.
    The code that jjames provided should perform as intended because there is a separate CASE for each TV and each CASE has its own WAIT. The contents of the WAITs are hardcoded; there are no variables to evaluate. That function can be called three times back-to-back-to-back once for each TV and all three TVs will turn on without issue.

    If you replace the word jjames with a_riot42 then what you cited above is true. If that code is called three times (What happened to that post? Call the cops, there?s been a robbery.) back-to-back-to-back once for each TV then the WAIT is only executed one time. The second and third encounters with the WAIT will be ignored, however, since nTV_NUM is evaluated after the WAIT expires only the third TV will work as intended.
    Hedberg wrote:
    We deal with the environment as it exists and not as we would like it, so I'll get over this, even though I'm not quite sure how to get around the problem.
    One way is to do something like what jjames demonstrated and add a separate CASE/WAIT for each TV.

    In this kind of situation, I prefer to use modules. I use one DEFINE_MODULE for each TV and then in my main code I use the same two lines of code for any TV.

    For off I do a:
    PULSE[sZones[zone].VirtualTV,nPowerOff]
    

    And to turn a TV on a do a:
    PULSE[sZones[zone].VirtualTV, sZones[zone].TVInputChannel]
    

    The module takes care of the rest and the WAIT issues discussed here don?t come into play.
  • Whew! Ask for an inch, get a mile! Ok then, I think for my purposes, I will likely use something like jjames posted, as it seems to be the easiest for me to implement and (comprehend) at this point. BTW, I thought the term was "dumbed down".
  • jjamesjjames Posts: 2,908
    Vegastech,

    The other thing too about the code I posted, is that the nTV_INPUT (obviously a variable that needs to be set somewhere - I do it in the function - I should have explained that before), is used for which input needs to be shot out. Say nTV_INPUT[1] = 3, and then the function is called again and its value is changed to 1 before the WAIT expires, then 1 will be used and not 3. This way, it's all kept up to date and there's very little manage.

    Of course, there are much more elegant ways to manage TV power (I've gone as far as timing the power up and power down sequence and waiting for when the TV is ready to do what the user last told it to do - a headache), but the posted code should give you a good start. Good luck and let us know how it goes!
  • HedbergHedberg Posts: 671
    Joe Hebert wrote: »
    The code that jjames provided should perform as intended because there is a separate CASE for each TV and each CASE has its own WAIT. The contents of the WAITs are hardcoded; there are no variables to evaluate. That function can be called three times back-to-back-to-back once for each TV and all three TVs will turn on without issue.

    You're right and you caught me just in time.
  • Spire_JeffSpire_Jeff Posts: 1,917
    If you want to take a slightly different approach to the problem, you could treat each TV as it's own entity. The reason I am doing it this way is because I am controlling the TVs from a separate processor, but you could easily create a separate function or module for each TV.
    channel_event[vdvRecreationTv,nTvChans]{
    	on:{
    		local_var integer nRecreationTvPower;
    		local_var integer nRecreationChan;
    		switch(channel.channel){
    			case 27:{
    				if(!nRecreationTvPower){
    					send_command dvRecreationTv,"'SP',channel.channel";
    					wait 5 send_command dvRecreationTv,"'SP',channel.channel";
    					wait 120 nRecreationTvPower = 1
    				}
    			}
    			case 28:{
    				send_command dvRecreationTv,"'SP',channel.channel";
    				wait 5 send_command dvRecreationTv,"'SP',channel.channel";
    				nRecreationTvPower = 0
    			}
    			default:{
    				nRecreationChan = channel.channel
    				wait_until(nRecreationTvPower) send_command dvRecreationTv,"'SP',nRecreationChan";
    			}
    		}
    	}
    }
    

    Now, this code block is written with the understanding that all I am doing with the TV is turning it on/off and switching inputs. Because of this, I am storing anything other than the power commands in the nRecreationChan variable. I decided that I don't need to buffer this as you really only want the TV to switch to the most recent input selected. You will have to adjust the power on delay to match the TV you are controlling.

    You could also add some code to the input channels that will execute the power on command if the power is not currently on, and then you simply need to send the input command when you switch inputs. You could also track the current input in a separate local_var and only send it if the TV is not on that input, but since I am dealing with IR devices, I just let it send the input every time just in case the input command is missed by the TV.

    Jeff
  • DHawthorneDHawthorne Posts: 4,584
    WAITs are placed on the global stack. Only by naming them can you make them run independently, and the name has to be a constant. What I do for turn on delays is create a global variable containing the on/off/warmup/cooldown state. I have a single function for turning on and off the TV (or projector) and put my wait to change the variable state in there. Then, I put a WAIT_UNTIL(test variable here) in my source selections. If the variable is already in the "on" state, the WAIT_UNTIL fires immediately. Otherwise, it fires when it goes "on." What you have to be careful of, however, is making sure the wait gets canceled if it's a long one and someone changes their mind midstream, so things don't get backed up and fire all at once. I generally clear my queues and cancel all such WAITs on an OFF request, or I lock the panel until the warmup is over (which isn't always the best course, depends on the system).
  • ericmedleyericmedley Posts: 4,177
    DHawthorne wrote: »
    WAITs are placed on the global stack. Only by naming them can you make them run independently, and the name has to be a constant. What I do for turn on delays is create a global variable containing the on/off/warmup/cooldown state. I have a single function for turning on and off the TV (or projector) and put my wait to change the variable state in there. Then, I put a WAIT_UNTIL(test variable here) in my source selections. If the variable is already in the "on" state, the WAIT_UNTIL fires immediately. Otherwise, it fires when it goes "on." What you have to be careful of, however, is making sure the wait gets canceled if it's a long one and someone changes their mind midstream, so things don't get backed up and fire all at once. I generally clear my queues and cancel all such WAITs on an OFF request, or I lock the panel until the warmup is over (which isn't always the best course, depends on the system).

    My method might seem a bit convoluted, but I love how it works in the end.

    I have a standard database that I enter in all my values to manage a TV. Command type (IR,RS-232, IP, AMX command, etc.. Actuall command values, delay times for all occasions, how video is handled, how audio is handled, how those things interact with other systems in the install. Power lifts or screens up and down, etc, etc,..) This works for up to 100 TVs.

    I have a rather simple TV command function that is fed from a queue. The queue handles timing and sets up the pass through the function and monitors the state of the function. Nothing happens in the function until it's cleared out or cancelled for some reason.

    So, to 'code' an install, you just lable all the TVs and fill in the data matrix. Afterward, you're done. no coding involved. Which TP controls which and how many TVs is entereed into another database on the TP Navigation section. I'm really getting lazy in my old age.
  • I tend to implement waiting for a display or TV to turn then fire the input, to a module that controls the TV. All commands to the TV are placed in a QUE and are parsed when the TV is not busy. I busy state could be warming up or cooling down or just waiting between input selections. The main code just say's if the TV is not on turn it on and then go to the desired input. The module handles the waiting.
  • I'm in the camp that uses modules too. Each device has it's own module, and then when you say "watch cable box" _main.axs sends the commands to the display device, scaler, cable box, audio system, and any other necessary devices to turn on. The module for each will queue up the commands and deliver them as quickly as possible to the device to get them turned on and switched.

    --John
Sign In or Register to comment.