Chapter 6 – The Wait Keywords
Controlling Time Within the AMX System
Within the AMX system there are a variety of timing keywords available for your use. Functions can be delayed for a few seconds, or they can be delayed until certain conditions are right. This chapter will help you learn each of these keywords and show you how to apply them to your programs.
You will build on your previous program, but this time you will not be changing the layout of your Touch Panel, you’ll just add more functions to the existing buttons.
The WAIT List
The most common keyword relating to time is the WAIT keyword, which is used to activate functions after a specified delay time has elapsed. The program flow does not stop when a WAIT is encountered. Instead, the Master places the statement associated with the WAIT keyword into a list in memory and continues on with the rest of the program. The Master scans this list, and if any WAITs have come due, the Master executes the statement or compound statement associated with the expired WAIT keyword. Up to 50 WAIT references are allowed in the list at a time.
Time in the WAIT list is measured in tenths of a second. A WAIT of 10 is one second, a WAIT of 15 is one and a half seconds, a WAIT of 20 is two seconds, and so on.
Suppose in your system you have two relays controlling system power. One is for the audio amplifier, and the other is for the rest of the equipment (we’ll call that “rack power”). In many cases, a time delay is desired between powering up the source equipment and powering up the audio amplifier. The reason is that if both are powered up at the same time, there is sometimes a loud “pop” over the audio system from the source equipment; but if there is a delay, the source equipment is already on and there will be no “pop” when the amp is turned on. In most cases a one half-second delay is enough.
In your program, you will first add a constant definition for your new relay and change the name of the existing system power relay to RACK_POWER. If you use relay 7 (the next available relay on the card), the line to add to the DEFINE_CONSTANT section is: AMP_POWER = 7
The companion code to this section is STEP3a.
As for the System Power button, you want the rack power to come on first (relay 3) when the power is turned on; one half-second later, the amp power should turn on. When you turn the power off, you want the reverse to happen. First, look at the organization of this set of statements. Here is a simplified outline of the structure of this code:
BUTTON_EVENT[TP,31]
{
PUSH:
{
IF (device-channel) {
compound statement }
ELSE {
compound statement }
}
From this outline you can more easily examine what is happening here. Following the PUSH statement is a single IF...ELSE statement which has only a device-channel reference for its condition. In this case the Master checks that channel’s status. If it is on, the Master evaluates the condition as true and executes the first compound statement. Otherwise the compound statement following the ELSE is executed.
Here’s the new code for the System Power button:
BUTTON_EVENT[TP,31]
{ PUSH:
{
IF([RELAY,RACK_POWER]) {
The first compound statement, which is executed if the rack power is on, uses a WAIT to accomplish a time-delayed powering-down of the system. The first statement inside the compound statement turns off the amplifier relay. The next statement is a WAIT statement for five-tenths of a second
Chapter 6 – The Wait Keywords
(same as one half-second), followed by an OFF statement to the rack power relay. The Master places this WAIT into the WAIT list and continues with the program. Since these statements are part of an IF compound statement, the Master does not execute the compound statement following the ELSE.
As the system continues to run, the WAIT is still in the WAIT list. After one half-second has elapsed, the Master will execute the statement immediately following the WAIT, which in this case turns off the rack power. The corresponding WAIT is then taken out of the list.
The compound statement following the ELSE is nearly the same as the one just described; its functions are just slightly different. The Master first turns on the rack power, waits a half-second, and then turns on the amp power.
Multiple Wait Statements
Now that you know how to delay an action for a specific amount of time, you can add a little pizzazz to your system. Suppose you want a series of several events to happen when you press just one button. At this point, the System Power button completes just two events with a delay in between.
Now you will make the System Power button accomplish several more things when the power is being turned on and off. Suppose that when the power is being turned on, you want a series of timed events to take place. First, the rack power comes on as before. At the same time, the screen starts to come down and the drapes start to close. One half-second later, the amp power comes on, just like before. Twenty seconds after the button is pressed, the Medium lighting scene is selected.
When the power is turned off a different sequence happens. First, the amp power is turned off, lights go to the Full setting, the screen is raised, and the drapes are opened. One half-second later the rack power turns off. Two minutes later the lights go to the Off setting. Here is the code for the System Power Push for the described scenario:
BUTTON_EVENT[TP,31]
{ PUSH:
{
IF ([RELAY,RACK_POWER]) (* power being turned off *) {
OFF[RELAY,AMP_POWER]
TO[LIGHTS,LIGHT_FULL]
TO[RELAY,SCREEN_UP]
TO[RELAY,DRAPE_OPEN]
WAIT 5 {
OFF[RELAY,RACK_POWER]
}
WAIT 1200 {
PULSE[LIGHTS,LIGHT_OFF]
} }
ELSE (* power being turned on *) {
ON[RELAY,RACK_POWER]
TO[RELAY,SCREEN_DOWN]
TO[RELAY,DRAPE_CLOSE]
WAIT 5 {
ON[RELAY,AMP_POWER]
} WAIT 200 {
PULSE[LIGHTS,LIGHT_MED]
} } } }
The companion code to this section is STEP3b.
Chapter 6 – The Wait Keywords
Notice the use of the PULSE keyword. This is done, as you may recall, because TO cannot be used inside a WAIT. Since the lighting buttons are momentary, you use a PULSE to actuate the relay for just a moment. WAIT statements can appear inside other WAIT statements. This is called “nesting”
WAIT statements. You don’t need to nest WAIT in your program here, but here is how it is done:
WAIT 200 {
ON[RELAY,SCREEN_UP]
WAIT 200 {
OFF[RELAY,SCREEN_UP]
} }
In this example, the system would wait twenty seconds, turn on the Screen Up relay, wait twenty more seconds, then turn off the Screen Up relay. Any timed sequence of events can be
accomplished with or without nested WAIT statements. Non-nested WAIT statements use less code.
However, using nested WAIT statements is in many cases more readable than non-nested WAIT statements. In addition, using nested waits can result in more efficient code with less burden on the processor. Here is the same example without nesting:
WAIT 200 ON[RELAY,SCREEN_UP]
WAIT 400 OFF[RELAY,SCREEN_UP]
Special Uses of Wait
Any one WAIT can only be placed in the WAIT list once. If a particular WAIT is already in the WAIT list, it cannot be placed into the list a second time until the first instance is either cancelled or expired. For instance, suppose the following line appears in mainline where it will be executed every pass through mainline:
WAIT 5 FLASH = NOT FLASH
The first time this is executed, the WAIT is placed into the WAIT list. But what if this line is executed again before the WAIT expires? Since the WAIT is already in the WAIT list, the line is simply ignored. One half-second after the first execution of this statement, the value in variable FLASH is inverted; if it was zero it will be changed to 1, and if it was non-zero it will be changed to zero. On the next pass through mainline, the WAIT will again be placed into the WAIT list and the cycle will repeat for the duration of the program. This in effect creates a variable whose state inverts every half-second.
Naming Wait Statements
As you saw in the examples above, it is not necessary to name waits but when a WAIT is given a unique name, it can be cancelled, paused, or restarted. To name a WAIT, simply place a name in single quotes after the WAIT statement. For example:
WAIT 30 ’DELAY’
Once DELAY is entered into the list, it cannot be re-entered until the first has been removed (cancelled or expired).
There are certain considerations in naming WAIT statements:
! They should not be previously defined constants or variables.
! They cannot be names that have already been assigned to buffers or subroutines.
! They can contain spaces, unlike other identifiers.
Canceling, Pausing, and Restarting Wait Statements
Once a WAIT is named, it can be manipulated within the program with several keywords.
PAUSE_WAIT places a WAIT on “hold.” The WAIT does not continue counting down until it is resumed with RESTART_WAIT. The WAIT then continues from where it was paused. CANCEL_WAIT completely nullifies a WAIT, removing it from the WAIT list. If you do not name your waits you won't be able to reference them individually. You would only be able to refer to all of them using the next several keywords discussed. CANCEL_ALL_WAIT nullifies every WAIT currently in the list.
The keywords PAUSE_ALL_WAIT and RESTART_ALL_WAIT act the same as PAUSE_WAIT and RESTART_WAIT, except they affect every WAIT in the WAIT list, named and unnamed.
You could use a named WAIT in your System Power PUSH routine. Suppose the user just turned off the power. The program now has a two-minute WAIT in the WAIT list for the lights to go off. If the user turns the power back on before this WAIT executes, the power-on sequence will start, but the events of the LIGHT_OFFWAIT will still happen! The user could end up in a very dark room, which is definitely not what he or she wanted. In this case it would be advantageous to name that WAIT and cancel it in the power-on section of the PUSH. To do this, simply add the WAIT name to the WAIT in the power-off section like this:
WAIT 1200 'LIGHTS OFF' {
PULSE [LIGHTS,LIGHT_OFF]
}
To cancel the WAIT in the power-on section, simply add this line:
CANCEL_WAIT ‘LIGHTS OFF’
The WAIT_UNTIL Keyword
The WAIT_UNTIL keyword is not a true “timing” keyword; the system does not wait for a certain amount of time to elapse. Instead, it checks to see if a condition is true. When the condition becomes true, the Master executes the statements listed directly below the WAIT_UNTIL statement.
All WAIT_UNTIL statements go into another list very similar to the WAIT list, called the WAIT_UNTIL list. Just as it does with WAIT statements, each pass through mainline the Master checks to see if any WAIT_UNTIL conditions have become true. For each one that has, the system immediately executes the sequence below the WAIT_UNTIL statement. If not, the system keeps the WAIT_UNTIL in the WAIT_UNTIL list until its condition becomes true.
Chapter 6 – The Wait Keywords
Misusing WAIT_UNTIL
Since the system only checks the status of pending WAIT_UNTIL statements after completely running mainline, make sure that the condition has a chance to become true, or you will defeat the purpose of the WAIT_UNTIL statement. You don’t need WAIT_UNTIL in your program yet, but this program segment illustrates the misuse of WAIT_UNTIL:
WAIT_UNTIL (Y=4) {
(* statements *) }
Y=4 Y=3
As you can see, Y will never equal four at the end of the program. The WAIT_UNTIL in this case is completely useless.
It would be hard to make this mistake in a small program such as the one you are working on, but this problem could make its way into a fairly large program. The compiler cannot detect this sort of error, so make sure each WAIT_UNTIL statement can become true one way or another.
Naming and Removing WAIT_UNTIL Statements
In the same manner as a WAIT, a WAIT_UNTIL can be named. To do this, a name in single quotes is placed at the end of the statement. Once a WAIT_UNTIL has a name, it can be cancelled with the CANCEL_WAIT_UNTIL keyword, which removes it from the WAIT_UNTIL list. The
CANCEL_ALL_WAIT_UNTIL keyword removes all WAIT_UNTIL statements from the WAIT_UNTIL list.