Sample Scripts
This section includes some sample scripts that illustrate some of the
things that can be done with the Control Manager and CMS and the
techniques that are used. It's by no means exhaustive, such a thing
is probably not even possible, but it will hopefully give you some
ideas for creating your own CMS functions.
Some of samples in this section have rather long lines due to the
comments. For maximum readability, you will probably need to maximize
the Help Window to prevent the lines from "wrapping" to the next line
and making things appear a bit confusing.
Example #1 - Combining Axes
This sample shows how you can use the CMS to combine axes to form
new axes that are more useful in a particular situation. In this case
the problem will be to combine the toe brakes into a single Y-Axis.
This function is sometimes useful in racing simulators that don't
provide for independent gas and brake pedals, but rely on the pedals
going one way for gas, the other for brake, with the center position
essentially being "no gas or brake".
Before starting the script, we need to look at the problem, consider
the facts, and then figure out how we're going to approach it.
The first useful fact here is that all axes return 0 to 255 for full
travel. This is true of the axes that the real controllers are
generating as well as the values that need to be sent to Windows via
the CMS Controls.
The second thing we need to know is that the Left Toe Brake is the
A1 axis for the ProPedals and the Right Toe Brake is the A2 axis. It's
easy to see the values and in which direction they go using the Test/Calibrate
screen, and the GUI will give you the name of the device if something other
than a simple button.
Finally we need to know what the simulation wants. Normally is a 0
for full throttle, a 255 for full brakes, and a 128 for neither, but this
can vary.
When it's all boiled down to the bare essentials, we have two axes,
the toe brakes, both of which are sending values of 0 when they're released
and 255 when they're completely pressed. The idea is to generate one axis
that goes from 128 (the effective center value) to 0 as one pedal is pressed,
and goes from 128 to 255 when the second pedal is pressed. It's just simple
arithmetic really, so simple arithmetic is what we'll do.
The Script
For this sample, we'll assume that there is a Yoke that we'll use as
a steering wheel on the first Device Tab in the GUI, it would be JS1.
We'll also assume that the ProPedals are on the second Device Tab in
the GUI and are thus JS2. If your devices are on a different Device
Tabs, then these references would need to be changed. The script would
look like this:
SCRIPT
CMS.A1 = 128 + (JS2.A2/2) - (JS2.A1/2);
ENDSCRIPT
It's really just one line of script. It starts with the center value of
128, then it adds one half the Y-Axis value and subtracts one half the
X-axis value. Thus, pressing the Right Toe Brake (A2) increases the initial
128 value towards 255, while pressing the Left Pedal (A1) reduces the
initial value towards 0, which is just what we wanted.
To complete the map, the axis assignments have to be made out in the
GUI. The Yoke X-Axis would be assigned to the X-Axis on CM Device 1.
The Yoke Y-Axis would be assigned to "None" since it won't be used.
Axis 1 on the CMS Controls would be assigned to the Y-Axis on
CM Device 1.
Example #2 - Map Mode Control
As was mentioned in the section on setting the Mode, it is possible
to use the CMS to control which Mode the map is using via the
CURRENTMODE variable. In this example, we'll implement a cycling
mode switch that operates much like the hardware mode switches on
the FighterStick and ProThrottle.
The only real fact of interest here is that, to cycle through the
three Modes, we need to set the CURRENTMODE variable to 0, then 1,
then 2, then back to 0. In this example, we use one of the internal
Analog Variables, A1, to keep track of which mode we're going
to set. That variable will be incremented by one every time a
button is pressed, and when it goes above 2, we'll reset it to
zero. A sequence is probably the simplest way to implement this
since we can use the WAIT() function to stall the sequence until
our button is clicked.
The Script
For this map, we'll assume that there's just a CombatStick in
the map and we want the Mode button to be Button 2 on that device.
The script would look something like this:
SCRIPT
SEQUENCE
WAIT( JS1.B2 ); // Wait here until JS1.B2 clicks
A1 = A1 + 1; // Add 1 to our Mode counter
IF( [ A1 > 3 ] ) THEN // If the value is over 3 then
A1 = 0; // set it back to 0
ENDIF
CURRENTMODE = A1; // Now copy it to CURRENTMODE
ENDSEQUENCE
ENDSCRIPT
JS1.B2 will increment A1 by 1 every time it's clicked. When A1 goes
above three, it gets reset to zero. The values will cycle 0, 1, 2, 3, 0,
1, 2, 3, etc. thus cycling through the four modes, just as we intended.
Since this doesn't control any CM Device axes or buttons, there is
really nothing that needs to be assigned in the GUI except that
Button 2 on the CombatStick should be assigned to "None" so it
doesn't interfere with whatever else the map is doing.
Another method would be to control the four modes with a single 4-way
hat. This has the advantage that all four modes are available instantly
without having to cycle through the other 3 modes, and we can tell which
Mode we are in by the direction that we push the hat.
For this example we'll assume we have a FighterStick and wish to use
Hat 3 to control the four Modes. Hat 3 generates Buttons 13 through
16. The script itself is straightforward, just a series of nested
IF/THEN/ELSE blocks that set the CURRENTMODE variable based on which
of the current position of Hat 3. It's useful to remember when using
hats that they can only have one position active at a time so we don't
need to make any allowance for a situation where B13 and B14 were
closed at the same time.
The script might look like this:
SCRIPT
IF( JS1.B13 ) THEN
CURRENTMODE = MODE1;
ELSE
IF( JS1.B14 ) THEN
CURRENTMODE = MODE2;
ELSE
IF( JS1.B15 ) THEN
CURRENTMODE = MODE3;
ELSE
IF( JS1.B16 ) THEN
CURRENTMODE = MODE4;
ENDIF
ENDIF
ENDIF
ENDIF
ENDSCRIPT
Out in the GUI, the Hat 3 positions should, of course, be programmed
to "NONE" so as not to send any commands directly.
Example #3 - Automatic Trim
This example implements an automatic trim function. In practice,
you move the joystick until you're flying straight and level. At
that point, you press a button which locks the axes in that position.
With the button held down, you return the joystick to its center
position and release the button. The trim setting will hold the
controls in the trimmed position when the joystick is centered,
and the stick will move the controls from that position as the joystick is
moved from center. To reset the trim, you leave the joystick centered
and click the button again.
Again, this is just arithmetic. We need to find out what the values
are at the trim position, then use those to calculate the difference
between the values there and the values for joystick center. Once
we have these offsets, we just add them to the actual joystick values
to produce the "trimmed" values.
The Script
For this example, we'll assume that there's a single CombatStick in
the map, it will be JS1, and that we want to use Button 2 on the
CombatStick as our "Trim" button. The script is really a two-step
process. First, calculating the Trim values when the Button 2 is
pressed, then applying the trim values when Button 2 is released.
We'll use the internal variables A1 and A2 to keep track of our
trim offset for the X and Y axes respectively.
The offsetting of the joystick values must go on continuously while
the button is released so that every reading gets updated. On the other
hand, the calculation of the offset values only needs to be done when
the button is first clicked. An IF/ENDIF block is most appropriate
for the former, a SEQUENCE for the latter.
The script would look like this:
SCRIPT
IF( NOT JS1.B2 ) THEN // If Button 2 is released then
CMS.A1 = JS1.A1 + A1; // add the X offset to the stick X value
CMS.A2 = JS1.A2 + A2; // and add the Y offset to the stick Y value
ENDIF
SEQUENCE
WAIT( JS1.B2 ); // Wait until Button 2 clicks
A1 = JS1.A1 - 128; // Calculate the X offset and save it in A1
A2 = JS1.A2 - 128; // Calculate the Y offset and save it in A2
ENDSEQUENCE
ENDSCRIPT
In the GUI, some assignments need to be made. First, the X and Y axes
for the CombatStick need to be assigned to "None" so they don't interfere.
Second, the CMS.A1 and CMS.A2 values need to be assigned to the X and Y
axes on CM Device 1 so the trimmed values are available to Windows.
Example #4 - Using the %DEFINE Statement
This example illustrates some of the things that can be done using the
%DEFINE directive. This provides essentially the same function as the
AutoTrim example above, but makes rather heavy use of the %DEFINE
directives.
The Script
The script actually generates exactly the same thing as the AutoTrim
Example. The main difference is in the %DEFINE statements at the top
of the script and then the way that things are referenced later by
name. It looks like this:
// Named variables to hold the trim offsets
//
%DEFINE xTrimValue A1
%DEFINE yTrimValue A2
// Names for the actual joystick axes
//
%DEFINE xStickValue JS1.A1
%DEFINE yStickValue JS1.A2
// Friendly names for GUI axes that go to Windows
//
%DEFINE xCmsValue CMS.A1
%DEFINE yCmsValue CMS.A2
// Name a constant for the axis center value
//
%DEFINE centerStickValue 128
// Name a couple of bit functions.
//
%DEFINE trimButtonPressed JS1.B2
%DEFINE trimButtonReleased NOT JS1.B2
// The definitions are complete. Now we write the script.
//
SCRIPT
// This trims the joystick values and puts them in the CM Device
// Axes while the trim button is released. This is the normal run
// situation.
//
IF( trimButtonReleased ) THEN
xCmsValue = xStickValue + xTrimValue
yCmsValue = yStickValue + yTrimValue
ENDIF
// This waits for the trim button to be pressed, then records
// how far from stick center that the X and Y values are. Those values
// are used as the trim correction that will be in effect when the
// trim button is released.
//
SEQUENCE
WAIT( trimButtonPressed );
xTrimValue = xStickValue - stickCenterValue;
yTrimValue = yStickValue - stickCenterValue;
ENDSEQUENCE
ENDSCRIPT
The above example is perhaps "overkill", but shows many of the things
that can be done. When you use the %DEFINE statements, keep in mind that
the idea is to help make clearer what the functions and variables actually
do. It's entirely possible to "go too far", collapsing things to the point
that the script becomes difficult to follow. For example, you could easily
define something like this:
%DEFINE setXTrim A1 = JS1.A1 - 128
%DEFINE setYTrim A2 = JS1.A2 - 128
SEQUENCE
WAIT( trimButtonPressed ); // Wait for the button press
setXTrim; // Set the X trim value
setYTrim; // Set the Y trim value
ENDSEQUENCE
The script that would be generated is exactly the same, but how the program
sets the X and Y trim values gets hidden and rather difficult to understand.
Example #5 - Sending Multiple Character Strings
There are times when it is desirable to send several strings based
on the press of only a single button. Each press sends the next string
in the series. This can't be accomplished in the GUI, but it's certainly
possible using the CMS. The approach taken in this example is to simply
use several of the buttons from the CMS Controls and program each to
send a different string using the GUI. The script takes care of cycling
through the strings when the correct button is pressed.
The Script
There are a couple of ways to do this. The simplest is probably just to
use a seqeuence. We'll again assume there's a single CombatStick in the
map as the JS1 device and we want to use Button 2 on that to cycle through
the messages. We'll also assume that there are four messages and that we
want to trigger them with CMS.B1 through CMS.B4. It would look something
like this:
SCRIPT
SEQUENCE
WAIT(JS1.B2); // Wait the first click
CMS.B1 = TRUE; // Press CMS Controls Button 1
WAIT(JS1.B2); // Wait for the next click
CMS.B1 = FALSE; // Release CMS Control Button 1
CMS.B2 = TRUE; // Press CMS Controls Button 2
WAIT(JS1.B2); // Wait for the next click
CMS.B2 = FALSE; // Release CMS Control Button 2
CMS.B3 = TRUE; // Press CMS Controls Button 3
WAIT(JS1.B2); // Wait for the next click
CMS.B3 = FALSE; // Release CMS Control Button 3
CMS.B4 = TRUE; // Press CMS Controls Button 4
WAIT(JS1.B2); // Wait for the next click
CMS.B4 = FALSE; // Release CMS Control Button 4
ENDSEQUENCE
ENDSCRIPT
Another method could make use of the SELECT function to accomplish
the same thing. The SELECT will be controlled by one of the internal
Analog Variables, its value will be controlled by JS1.B2. First we
wait for the click, then set the new value. That gets processed through
the SELECT statement, each case turning on one of the CMS.Bx outputs
and triggering the string. It might look like this:
SCRIPT
SEQUENCE
WAIT( JS1.B2 ); // Wait until Button 2 is pressed
A1 = A1 + 1; // Increment our message counter
IF( [ A1 > 3 ] ) THEN // If A1 is over three then
A1 = 0; // Reset A1 to 0
ENDIF
ENDSEQUENCE
SELECT( A1, POSITION ) OF
CASE 0:
CMS.B1 = TRUE; // Set the bit for our first message
EXITCASE:
CMS.B1 = FALSE; // Clear the bit for our first message
BREAK;
CASE 1:
CMS.B2 = TRUE; // Set the bit for our second message
EXITCASE:
CMS.B2 = FALSE; // Clear the bit for our first message
BREAK;
CASE 2:
CMS.B3 = TRUE; // Set the bit for our third message
EXITCASE:
CMS.B3 = FALSE; // Clear the bit for our third message
BREAK;
CASE 3:
CMS.B4 = TRUE; // Set the bit for our fourth message
EXITCASE:
CMS.B4 = FALSE; // Clear the bit for our fourth message
BREAK;
ENDSELECT
ENDSCRIPT
In the GUI we would need to make the necessary Programmed Mode assignments
to CMS.B1 through CMS.B4 to define each of the four strings that we want
to send.
Example #7 - Analog Trim Function
An analog trim function for axes as the elevator can be a useful thing
to have as it provides better control then the common Trim+ and Trim-
key commands. Some flight simulations provide such a function and they
can simply be assigned to an axis and used directly. If not, then the
CMS can be used to generate one. Again, it's just arithmetic. We need
to take an unused axis and use its value as an offset to the main
control value for the axis we want to trim.
The Script
For this example, we'll assume that we have a FighterStick on the first
Device Tab (JS1) and a ProThrottle on the second device tab (JS2). Since
we're using the ProThrottle to control the throttle itself, we can use
the throttle wheel (A3) on the FighterStick as our trim axis. We want
to trim the elevator (A2). We'll limit the available trim to 10% of full
travel. We'll use CMS.A1 as the value that provides the final elevator
setting.
Again, it's just arithmetic. We need to offset the trim value so it goes
positive and negative, providing a 0 at the center position. Since
it's a real axis (JS1.A3) we simply subtract 128 from its actual value
to get a value between -128 and +127. We then divide that by 10 to give
us the 10% trim limit, and add it to the value for JS1.A2. Finally, we
check to see if the result has gone out of range and set limits on it
if it has.
The script would look something like this:
SCRIPT
CMS.A1 = JS1.A2 + (( JS1.A3 - 128) / 10); // Calculate the trimmed value
IF( [ CMS.A1 > 255 ] ) THEN // If the result is too high then
CMS.A1 = 255; // set it to the maximum allowable
ENDIF
IF( [ CMS.A1 < 0 ] ) THEN // If the result is too low then
CMS.A1 = 0; // set it to the minimum allowable
ENDIF
ENDSCRIPT
In the GUI, we would need to assign Axis 1 of the CMS Controls to control
the Y-Axis on CM Device 1. The Y-Axis on the FighterStick would be assigned
to "None" to disable it.
Example #8 - Switching Between Trackball and Ministick
If you're using a Trackball in your map, you probably use it to control
the mouse most of the time. There are times, though, when it might be
desirable to switch to the ministick on the ProThrottle or some other
set of joystick axes to control the mouse. This isn't possible using just
the GUI since you cannot make two assignments to the mouse DX device.
It can be accomplished using the CMS scripting, though. This example
shows one possible method. It also shows how the XRELATIVE, YRELATIVE,
and ZRELATIVE flags are uses.
The Script
For this example, we'll assume that we've got a FighterStick as Joystick 1,
and ProThrottle as Joystick 2, and a TrackballPro as Joystick 3. We need a
way to determine whether to take the data from the Trackball or from the
Ministick. We can do this by just checking if the Ministick is off-center.
If it is, we'll assume that we should take the Ministick data for the
mouse move. If the Ministick is centered, we'll just take the TrackballPro
data directly. That way, the switch should be more or less invisible. We'll
also need to switch the ?RELATIVE flags at the same time. We handle the two
axes more or less independently to allow the axes to respond independently.
The script might look like this:
SCRIPT
// Set the ?RELATIVE flags if the Ministick axes are centered.
// This then disables the trackball when the Ministick is off center.
//
XRELATIVE = (JS2.A1 > 120) AND (JS2.A1 < 136));
YRELATIVE = (JS2.B1 > 120) AND (JS2.B1 < 136));
// If X is centered, it's relative and we use the Trackball.
// Otherwise, we use the Ministick.
//
IF( XRELATIVE ) THEN
CMS.A1 = JS3.A3;
ELSE
CMS.A1 = JS2.A1;
ENDIF
// Now we do the same with Y.
//
IF( YRELATIVE ) THEN
CMS.A2 = JS3.A4;
ELSE
CMS.A2 = JS2.A2;
ENDIF
ENDSCRIPT
In the GUI, we would program CMS.A1 and CMS.A2 to control the Mouse X
and Mouse Y axes respectively and we'd be finished.
Example #9 - Switching Between Controllers
There are occasions when it might be desirable to use multiple sets
of controllers and be able to select which set is active. Such a
situation might arise, for example, if we wanted to set up a
dual-control cockpit for an instructor/student or pilot/copilot
operation. We need some way to set which set of controls is going
to be the active set. This is easily done using CMS.
The Script
For this script we'll assume that we have two Yoke LEs and two
sets of Pro Pedals. In the map, they're set up with the pilots
Yoke LE on the first tab (JS1), the pilots ProPedals on the second
tab (JS2), the copilots Yoke LE on the third tab (JS3), and the
copilots ProPedals on the fourth tab (JS4). We'll also assume that
Button 1 on the pilots Yoke LE is to be used to toggle between
one set of controls and the other.
The script is fairly straightforward. We set things up so that the
internal bit variable B1 is in control of which set of controls is
in use, then we set up a short sequence to toggle B1 every time
that JS1.B1 is clicked. Then, using an IF/THEN/ELSE block based on
the state of B1, we assign one set of controls or the other to a
set of CMS axes. We'll only do the X, Y, Throttle, and Rudder axes
for now to show the method. A practical example would probably need
to do a similar thing with the buttons and toe brakes. Also, since
B1 is initially false, the pilots position is indicated by B1 being
FALSE and the copilots position is indicated by B1 being TRUE.
The script would look like this:
SCRIPT
SEQUENCE
WAIT( JS1.B1 ); // Wait until the pilots Button 1 is pressed.
B1 = NOT B1; // Flip the state of the B1 variable
ENDSEQUENCE
IF( B1 ) THEN // If b1 is TRUE then we're doing the copilot so
CMS.A1 = JS3.A1; // copy the copilots X axis,
CMS.A2 = JS3.A2; // Y axis,
CMS.A3 = JS3.A3; // Throttle,
CMS.A4 = JS4.A3; // and Rudder Axis to the CMS axes.
ELSE // Otherwise we're doing the pilot so
CMS.A1 = JS1.A1; // copy the pilots X axis,
CMS.A2 = JS1.A2; // Y axis,
CMS.A3 = JS1.A3; // Throttle,
CMS.A4 = JS2.A3; // and Rudder Axis to the CMS axes.
ENDIF
ENDSCRIPT
In the GUI, we'd assign the CMS Controls to CM Device 1. CMS Axis 1 would go
to the X-Axis, CMS Axis 2 would go to the Y-Axis, CMS Axis 3 would go to
the Z-Axis, and CMS Axis 4 would go to the R-Axis. We'd also need to
unassign the X, Y, Z, and R axes from the yokes and pedals to prevent
conflicts.
Example #10 - Changing Joystick Response
In some circumstances, it can be desirable to change the response
of your Controllers while you're using them in a game or simulation.
For example, in a combat flight simulation, you might like to use
a relatively insensitive response for normal flying, then switch
to a more sensitive response during a dogfight to be able to respond
more quickly. This example shows how you might set this sort of thing
up using the SCALE function.
The Script
The script for doing this is relatively simple. We need a "toggle"
that indicates which of the two sets of response parameter are active.
The state of this toggle is then used in an IF/THEN/ELSE block to
activate the proper response. In this example, we're doing it inside
a sequence. This is more efficient since the last SCALE function
remains in effect until another SCALE function is executed. Using the
sequence means that the scaling code will only execute when the button
is clicked.
For the script, we assume that there's a FighterStick on the first
Device Tab, so it's JS1 that we're dealing with. We're going to
use Button 4 as the response selection button. If our toggle is
TRUE, we select a less sensitive set of parameters, if the toggle
is FALSE, we select the more sensitive set. Note that before the
button is clicked for the first time, whatever parameters were set
in the GUI will be in effect, and that the first click will set the
less sensitive parameters since B1 is initial FALSE. The script would
look like this:
SCRIPT
SEQUENCE
WAIT( JS1.B4 ); // Wait for Button 4
B1 = NOT B1; // Flip the toggle
IF( B1 ) THEN // If the toggle is TRUE then set
SCALE( JS1.A1, 50, 5, GAIN3 ); // Sensitivity=50%, Deadzone=5%
SCALE( JS1.A2, 50, 5, GAIN3 ); // and Gain Curve=3
ELSE // Otherwise set
SCALE( JS1.A1, 100, 5, GAIN6 ); // Sensitivity=100%, Deadzone=5%
SCALE( JS1.A2, 100, 5, GAIN6 ); // and Gain Curve=6
ENDIF
ENDSEQUENCE
ENDSCRIPT
In the GUI, we really wouldn't need to do anything at all. The normal
JS1 assignments to X and Y are the ones that the SCALE function controls,
so we're just acting on the real controls and there are no CMS assignments
necessary.
Example #11 - Generating Repeating Sequences
Sometimes you might need to generate a repeating sequence of events for
some period of time. An example might be the release of Chaff and Flares
in a jet fighter simulations. The SEQUENCE statement is the obvious choice
for this, and it ends up being very much like Example #5, the main difference
being that a button would be used more to enable the function while DELAY
instructions would be used to step through the sequence.
The Script
The script is straight forward. We'll assume that we have a FighterStick
and that we want to use Button 2 to enable the sequence. Further, we need
two commands, one for Chaff and one for Flares. We want them to alternate
so long as Button 2 is held down with a 5 second spacing between the
commands.
Since we want the action to continue so long as Button 2 is held, we'll
use the WHILE() function to control that. We'll also use CMS.B1 to send
the "Chaff" command and CMS.B2 to send the "Flares" command.
The script would look like this:
SCRIPT
SEQUENCE
WHILE(JS1.B2); // Keep it going so long as the button is down
CMS.B1 = TRUE; // Press CMS Controls Button 1 for Chaff
DELAY( 100 ); // Wait for about 5 seconds
CMS.B1 = FALSE; // Release CMS Controls Button 1
CMS.B2 = TRUE; // Press CMS Controls Button 2 for Flares
DELAY( 100 ); // Wait for another 5 seconds
CMS.B2 = FALSE; // Release CMS Controls Button 2
ENDSEQUENCE
ENDSCRIPT
Out in the GUI, we'd need to make the assignment on CMS Controls Button 1
and Button 2 for Chaff and Flares. These should be programmed with the
leading NULL character to keep them from sending repeating characters for
the full 5 seconds each time.
Example #12 - Using SELECT Blocks
This is not really a practical example, more a demonstration of some of
the advanced functions that can be implemented using the SELECT function.
This function can be used for a number of fairly complex operation beyond
just responding to the current state of an axis. This example will
illustrate a few of the possibilities.
One of the more interesting is the ability to send or control one group
of operations when an analog axis is moving one way, another group if
the axis is moving in the opposite direction. While the Up/Down programming
available using the GUI can do this
for single characters, controlling more elaborate functions can be tricky.
The key to doing this is to track the current position using an analog
variable. Then, when we enter a new "case", we can check that variable
to tell you what the last case was and thus know whether the axis is
increasing or decreasing. The basic structure would need to look like
this:
SCRIPT
SELECT( JS1.A3, RANGE ) OF
CASE 0:
A2 = 0; // Set State 0, we must have come from Case 64
BREAK;
CASE 64:
IF( [ A2 EQ 0 ] ) THEN
// We came from Case 0
ELSE
// We must have come from Case 128
ENDIF
A2 = 1; // Set State 1
BREAK;
CASE 128:
IF( [ A2 EQ 1 ] ) THEN
// We came from Case 64
ELSE
// We must have come from Case 192
ENDIF
A2 = 2; // Set State 2
BREAK;
CASE 192:
A2 = 3; // Set State 3, we must have come from Case 128
BREAK;
ENDSELECT
ENDSCRIPT
Each case in the above checks the analog variable A2 to determine where
it came from. It then registers its own state id in analog variable A2 so
that the next state can tell where it came from.
Another possibility is that we might want to control some group of
sequences based on the SELECT block. We would need to control them
externally using internal flags that were set in the "CASE" part of
each case, and then cleared in the "EXITCASE" part.
That introduces a potential problem in that we probably
don't want one case to start until the sequence initiated by the
previous case has been completed. This, too, is not too difficult
to guarantee. We define a flag that controls when the SELECT block
actually sees the value change. Each sequence can then set this
flag to lock out any further changes in the SELECT value until its
corresponding sequence has completed.
A script to implement this logic might look something like this:
SCRIPT
// This is the lockout logic. If B1 is FALSE, then we copy JS1.A3
// to the SELECT value. If B1 is TRUE, then we skip this, the
// SELECT value does not change, and so the SELECT block stays in
// whatever state it's currently in.
//
IF( NOT B1 ) THEN // If B1 is FALSE, then we copy the
A1 = JS1.A3; // real value to our SELECT value.
ENDIF;
SELECT( A1, RANGE ) OF
CASE 0:
B1 = TRUE; // Lock out the value change in the SELECT block
B2 = TRUE; // B2 will control our first sequence
EXITCASE:
B2 = FALSE; // Clear our first sequence flag
BREAK;
CASE 64:
B1 = TRUE; // Lock out the value change in the SELECT block
B3 = TRUE; // B3 will control our second sequence
EXITCASE:
B3 = FALSE; // Clear our second sequence flag
BREAK;
CASE 128:
B1 = TRUE; // Lock out the value change in the SELECT block
B4 = TRUE; // B4 will control our third sequence
EXITCASE:
B4 = FALSE; // Clear our third sequence flag
BREAK;
CASE 192:
B1 = TRUE; // Lock out the value change in the SELECT block
B5 = TRUE; // B5 will control our fourth sequence
EXITCASE:
B5 = FALSE; // Clear our fourth sequence flag
BREAK;
ENDSELECT
// Now we can do the four sequences. Each one will clear the
// the B1 flag when it's done so that the SELECT block can
// update the SELECT value.
//
// These sequence just have a wait for their corresponding bit.
// Then they execute a DELAY and clear the lock on the SELECT
// block data transfer. They don't really do anything practical.
//
SEQUENCE
WAIT( B2 ); // Wait for our first sequence flag
DELAY( 10 ); // Some useful function would go here
B1 = FALSE; // Disable the lockout so the SELECT can move
ENDSEQUENCE
SEQUENCE
WAIT( B3 ); // Wait for our second sequence flag
DELAY( 10 ); // Some useful function would go here
B1 = FALSE; // Disable the lockout so the SELECT can move
ENDSEQUENCE
SEQUENCE
WAIT( B4 ); // Wait for our third sequence flag
DELAY( 10 ); // Some useful function would go here
B1 = FALSE; // Disable the lockout so the SELECT can move
ENDSEQUENCE
SEQUENCE
WAIT( B5 ); // Wait for our fourth sequence flag
DELAY( 10 ); // Some useful function would go here
B1 = FALSE; // Disable the lockout so the SELECT can move
ENDSEQUENCE
ENDSCRIPT
There is another interesting possibility with the above sort of
logic. Rather than basing the input to the SELECT block on one
of the real axes, we could simply base it on a value that was
set in each of the SEQUENCEs. In that case, the ordering of the
various states could depend completely on, for example, what the
value of CURRENTMODE is, causing the SELECT to transition from
one state to any of three other states depending on the current
Map Mode. Any set of conditions, really, can be used to determine
what the next state might be. All you need to do is to generate
the correct controlling value for the SELECT statement.