LAST PAGE  BACK TO INDEX  NEXT PAGE

[11.0] VEE Test Sequencer

[11.0] VEE Test Sequencer

v5.0 / 11 of 23 / 01 sep 99 / gvg

* This chapter covers the operation of the VEE test sequencer in detail.


[11.1] SEQUENCER -- SEQUENCER TRANSACTIONS
[11.2] SEQUENCER -- SEQUENCER LOGGING
[11.3] SEQUENCER -- THE FINE PRINT
[11.4] USING THE SEQUENCER TO PROMPT FOR USER INPUTS

 BACK TO INDEX

[11.1] SEQUENCER -- SEQUENCER TRANSACTIONS

* The simplest way to introduce the Sequencer is to build a small "mock" test system with it (you can get the source for it from the file xsequenc.vee). This test consists of the simple sequencing of 3 test routines ("DevTest1" through "DevTest3"), preceded by a routine to set up the test system ("InitTest").

In reality, these are dummy routines that merely return a random number in the range -1 through 11. The three test routines all have the form:


   +------------------------------------------------+
   |            UserFunction: DevTest1              |
   +----+---------------------------------------+---+
   |    |                                       |   |
   |    |                                       |   |
   |    |      +--------------------------+     |   |
   |    |      |         Formula          |     |   |
   |    |      +-----------------+--------+     |   |
   |    |      |[ random(-1,11) ]| Result +---->| X |
   |    |      +-----------------+--------+     |   |
   |    |                                       |   |
   |    |                                       |   |
   +----+---------------------------------------+---+
   |                     [ Done ]                   |
   +------------------------------------------------+
 
Note the routine has an output to return the result of the "test". Such routines could also have one or more inputs.

The "InitTest" routine simulates setting up the test procedure. In this case, all it does is seed the random-number generator:


   +----------------------------------------------------------------------+
   |                        UserFunction: InitTest                        |
   +----+-------------------------------------------------------------+---+
   |    |                                                             |   |
   |    |                                                             |   |
   |    |    +---------------------------------------------------+    |   |
   |    |    |                     Formula                       |    |   |
   |    |    +------------------------------------------+--------+    |   |
   |    |    | [randomseed((10^9)*fracPart(now()/100))] | Result +--->| X |
   |    |    +------------------------------------------+--------+    |   |
   |    |                                                             |   |
   |    |                                                             |   |
   +----+-------------------------------------------------------------+---+
   |                              [ Done ]                                |
   +----------------------------------------------------------------------+
 
The final result is the following list of routines:

   +-------------------------------------------+
   |             Edit UserFunction             |
   +------------+-----------------+------------+
   |            | InitTest        |            |
   |            | DevTest1        |            |
   |            | DevTest2        |            |
   |            | DevTest3        |            |
   |            |                 |            |
   +------------+-----------------+------------+
   |[InitTest                                 ]|
   +-------------------------------------------+
   |        [  OK  ]           [Cancel]        |
   +-------------------------------------------+
 
The Sequencer object is then used to link these test routines together. The Sequencer appears to be an ordinary I/O transaction object:

   +----------------------------------------------------+
   |                      Sequencer                     |
   +---+---------------------------------------+--------+
   |   |[                                     ]|        |
   |   |                                       | Return |
   |   |                                       |        |
   |   |                                       |        |
   |   |                                       |        |
   |   |                                       |   Log  |
   |   |                                       |        |
   +---+---------------------------------------+--------+
 
Note the "Log" pin, which outputs the results of the tests. The "Return" pin can be ignored for now.

However, the Sequencer transactions consist of the sequence of tests to be performed, not I/O transactions. In this case, four transactions need to be set up.

The first is the "InitTest" routine. Click on the transaction field and you get a complicated dialog:


 +-------------------------------------------------------------------------+
 |                          Sequence Transaction                           |
 +-------------------------------------------------------------------------+
 | [ TEST: ] [ test1  ] [   ENABLED   ]                                    |
 | SPEC NOMINAL:        [.5    ] [ RANGE ] [0   ] [ <= ] ... [ <= ] [1   ] |
 | FUNCTION:      [testFunc(s) ] [ LOGGING ENABLED ]                       |
 | [  IF PASS  ] [  THEN CONTINUE  ]                                       |
 | [  IF FAIL  ] [  THEN CONTINUE  ]                                       |
 | DESCRIPTION:  [                                ]                        |
 |                                                                         |
 |                [  OK  ]                         [Cancel]                |
 +-------------------------------------------------------------------------+
 
Note how the first field in the upper left corner says "TEST:". In this case, the routine to be executed is not a test -- all it does is initialize the system for true tests -- so clicking on that field gives the alternative, an "EXEC:" transaction (which simply executes a function):

 +-------------------------------------------------------------------------+
 |                          Sequence Transaction                           |
 +-------------------------------------------------------------------------+
 | [ EXEC: ] [ test1  ] [   ENABLED   ]                                    |
 | FUNCTION:      [testFunc(s) ] [  LOGGING DISABLED ]                     |
 | [  THEN CONTINUE  ]                                                     |
 | DESCRIPTION:  [                                   ]                     |
 |                                                                         |
 |                                                                         |
 |                                                                         |
 |                [  OK  ]                         [Cancel]                |
 +-------------------------------------------------------------------------+
 
The only things that need to be done on this dialog are to change the test name and function name appropriate, and add a description (if desired):

 +-------------------------------------------------------------------------+
 | [ EXEC: ] [ Init   ] [   ENABLED   ]                                    |
 | FUNCTION:      [InitTest()  ] [  LOGGING DISABLED ]                     |
 | [  THEN CONTINUE  ]                                                     |
 | DESCRIPTION:  [ Initialize test setup.            ]                     |
 |                                                                         |
 |                                                                         |
 |                                                                         |
 |                [  OK  ]                         [Cancel]                |
 +-------------------------------------------------------------------------+
 
The test name -- "Init" -- is purely arbitrary and doesn't need to have anything to do with the name of the function being called. The Sequencer simply uses the test name as a handle to the transaction. (Each transaction must have a unique name.)

The other fields in the dialog can be left at their defaults (it is, in fact, impossible to set "LOGGING DISABLED"). While the "ENABLED" field can be used to turn off this transaction, that obviously isn't desired here. Since the idea here is to run "InitTest" and then directly run the test routines, the "THEN CONTINUE" field can be left at its default, instead of being used to specify one of the many alternatives available for sequencer actions on completion of a transaction.

This done, the test transactions for the three test routines can be set up. Clicking on the second field and modifying the transaction dialog appropriately gives:


 +-------------------------------------------------------------------------+
 | [ TEST: ] [ test1  ] [   ENABLED   ]                                    |
 | SPEC NOMINAL:        [5     ] [ RANGE ] [0   ] [ <= ] ... [ <= ] [10  ] |
 | FUNCTION:      [DevTest1()  ] [ LOGGING ENABLED ]                       |
 | [  IF PASS  ] [  THEN CONTINUE  ]                                       |
 | [  IF FAIL  ] [  THEN CONTINUE  ]                                       |
 | DESCRIPTION:  [Device test 1.                  ]                        |
 |                                                                         |
 |                [  OK  ]                         [Cancel]                |
 +-------------------------------------------------------------------------+
 
Here the transaction is given an (also arbitrary) name. The test limits are defined as being from 0 through 10, with the nominal value being 5. The nominal value is simply for documentation in this case, it doesn't do anything. The UserFunction name is given as "DevTest1()"; logging is enabled and the Sequencer is instructed to proceed with the next test, whether the current test passes or fails; and a short description is provided.

The other two tests are defined similarly, resulting in:


   +---------------------------------------------------+
   |                     Sequencer                     |
   +---+--------------------------------------+--------+
   |   | Init                                 |        |
   |   | test1 0 <= (5) <= 10  Test trial 1!  | Return |
   |   | test2 0 <= (5) <= 10  Test trial 2!  |        |
   |   | test3 0 <= (5) <= 10  Test trial 3!  |        |
   |   |                                      |        |
   |   |                                      |  Log   |
   |   |                                      |        |
   +---+--------------------------------------+--------+
 
Test limits can also be derived from a variable. The same program could be implemented as follows:

            +----------+     +--------------+
            |   Real   |     | Set Variable |
            +----------+     +--------------+
            | 0000: 0  +---->|   TestLim    |
            | 0001: 10 |     +-------+------+
            +----------+             |
                                     |
   +---------------------------------+----------------------------------+
   |                             Sequencer                              |
   +---+-------------------------------------------------------+--------+
   |   | Init                                                  |        |
   |   | test1 0 <= (5) <= 10  Test trial 1!                   | Return |
   |   | test2 0 <= (5) <= 10  Test trial 2!                   |        |
   |   | test3 TestLim[0] <= (5) <= TestLim[1]  Test trial 3!  |        |
   |   |                                                       |        |
   |   |                                                       |  Log   |
   |   |                                                       |        |
   +---+-------------------------------------------------------+--------+ 
 
Now the test can be run. However, that begs the question: how does anyone know what the results of the tests are?

 TOP OF PAGE

[11.2] SEQUENCER -- SEQUENCER LOGGING

* Of course, the answer to that is simple: the test results are output through the Sequencer's Log output pin. Unfortunately this simple answer begs a nastier question: what is the format of this output, and what can be done with it?

The answer to this question has two parts. First, the data provided for logging output from the tests can be specified by selecting "Edit Default Preferences" from the Sequencer's object menu. You get the usual tabfolder, but with a tab for "Logging":


   +-----------------------------------------------------------+
   |                    Sequencer Properties                   |
   +-----------+-----------+-----------+-----------+-----------+
   |  General  |  Colors   |   Fonts   |  Logging  | Printing  |
   +-----------+-----------+-----------+           +-----------+
   | +- Record Fields To Log ---------------------+            |
   | | [x] Name           [ ] Low Limit           |            |
   | | [x] Result         [x] Pass                |            |
   | | [ ] Nominal        [x] Time Stamp          |            |
   | | [ ] High Limit     [x] Description         |            |
   | +--------------------------------------------+            |
   | +- Logging Mode -----------------------------+            |
   | | [Log to Output Pin Only]                   |            |
   | +--------------------------------------------+            |
   |                                                           |
   |                                                           |
   |                                                           |
   +-----------------------------------------------------------+
   |              [   OK   ] [ Cancel ] [  Help  ]             |
   +-----------------------------------------------------------+
 
Now for the actual output format. Given the settings shown above, running the Sequencer sets the contents of the Log output pin to:

   +----------------------------------------------------------------+
   |                    Output Terminal Information                 |
   +----------------------------------------------------------------+
   | Name:  [  Log  ]  Mode:  [  Data ]                             |
   |                                                                |
   |                     Container Information                      |
   |                                                                |
   | +---------------------+        +-----------------------------+ |
   | | Type:    [ Record ] |  Data: | Field Name     Value        | |
   | | Shape:   [ Scalar ] |        | [ test1 ] [ Record Scalar ] | |
   | +---------------------+        | [ test2 ] [ Record Scalar ] | |
   |                                | [ test3 ] [ Record Scalar ] | |
   |                                +-----------------------------+ |
   |                                                                |
   |            [   OK   ]                     [ Cancel ]           |
   +----------------------------------------------------------------+
 
The logging output is a single record, with individual subrecords for the result of each test. Each of these subrecords has the format:

   +-------------------------------+
   |       Record Field Data       |
   +-------------------------------+
   |  Field name        Value      |
   | [   Name    ] [test1        ] |
   | [   Result  ] [6.35         ] |
   | [   Pass    ] [1            ] |
   | [ TimeStamp ] [62.9G        ] |
   | [Description] [Test trial 1!] |
   +-------------------------------+
   |    [  OK  ]       [Cancel]    |
   +-------------------------------+
 
This record follows the format defined in the "Logging Config" dialog box. Similarly, if a To File object is connected to the Log pin of the sequencer, the output file will contain the following data:

   {{"test1", 2.498779296875, 1, 62907213809.66, "Test trial 1!"}, 
    {"test2", -0.1331787109375, 0, 62907213809.66, "Test trial 2!"}, 
    {"test3", 2.3255615234375, 1, 62907213809.72, "Test trial 3!"}}
 
This is an extremely concise format that defines exactly the sequence of the test results. Unfortunately, if all that is needed is a simple printout of the test results, it's a pain to deal with because each subrecord has to be sorted out individually.

If there are not too many tests in the sequencer, a simple To String object will suffice to do the job. For example, in this case:


   +---------------------------------------------------------------------+
   |                              To String                              |
   +---+--------------------------------------------------------+--------+
   |   | WRITE TEXT A.test1.Name STR FW:6 LJ                    |        |
   |   | WRITE TEXT A.test1.Result REAL FIX:2 FW:6 RJ           |        |
   |   | WRITE TEXT A.test1.Pass INT:1 FW:3 RJ                  |        |
   |   | WRITE TEXT A.test1.TimeStamp TIME:HMS:H24 FW:12 RJ EOL |        |
   |   | WRITE TEXT A.test2.Name STR FW:6 LJ                    |        |
   |   | WRITE TEXT A.test2.Result REAL FIX:2 FW:6 RJ           |        |
   | A | WRITE TEXT A.test2.Pass INT:1 FW:3 RJ                  | result |
   |   | WRITE TEXT A.test2.TimeStamp TIME:HMS:H24 FW:12 RJ EOL |        |
   |   | WRITE TEXT A.test3.Name STR FW:6 LJ                    |        |
   |   | WRITE TEXT A.test3.Result REAL FIX:2 FW:6 RJ           |        |
   |   | WRITE TEXT A.test3.Pass INT:1 FW:3 RJ                  |        |
   |   | WRITE TEXT A.test3.TimeStamp TIME:HMS:H24 FW:12 RJ EOL |        |
   |   |                                                        |        |
   +---+--------------------------------------------------------+--------+
 
This yields the output:

   test1   4.39  1    13:01:20
   test2   2.12  1    13:01:20
   test3   2.90  1    13:01:20
 
* This sort of field-by-field dissection might prove painful in a test system with a greater number of tests, but there are a number of roundabouts that can avoid it.

The least painful involves a feature of the Logging Config tab. This tab has a field:


   [Log to Output Pin Only ]
 
Click on this and it changes to something of the form:

   [Log Each Transaction To: ] [logTest(thisTest)]
 
This specifies that, after each individual test, the test result is sent to a UserFunction named "logTest" (or some other arbitrary name) using a parameter named "thisTest", whose name is not arbitrary, as the Sequencer uses it to specify the test result.

So, if the UserFunction named logTest, say, writes the formatted I/O transactions to a file:


   +-----------------------------------------------------------------------+
   |                       UserFunction: logTest                           |
   +---+---------------------------------------------------------------+---+
   |   |                                                               |   |
   |   |    +------------------------------------------------------+   |   |
   |   |    |                         To File                      |   |   |
   |   |    +---+--------------------------------------------------+   |   |
   |   |    |   |To File: [               tmp.txt                 ]|   |   |
   |   |    |   |      [X] Clear File At PreRun & Open             |   |   |
   |   |    |   |--------------------------------------------------|   |   |
   |   |    |   | WRITE TEXT A.Name STR FW:6 LJ                    |   |   |
   | A +--->| A | WRITE TEXT A.Result REAL FIX:2 FW:5 RJ           |   |   |
   |   |    |   | WRITE TEXT A.Pass INT FW:2 RJ                    |   |   |
   |   |    |   | WRITE TEXT A.TimeStamp TIME:HMS:H24 FW:12 RJ EOL |   |   |
   |   |    |   |                                                  |   |   |
   |   |    +---+--------------------------------------------------+   |   |
   |   |                                                               |   |
   +---+---------------------------------------------------------------+---+
   |                               [ Done ]                                |
   +-----------------------------------------------------------------------+
 
-- then the formatted file can be retrieved and displayed after the Sequencer has completed its operations:

     from Sequencer Sequence-Out pin
                    |
   +----------------+----------------+     +----------------------------+
   |            From File            |     |    Logging AlphaNumeric    |
   +-----------------------------+---+     +----------------------------+
   | From File:  [   tmp.txt   ] |   |     | test1  1.78 1    12:30:07  |
   |-----------------------------|   |     | test2 10.41 1    12:30:07  |
   |[READ TEXT x STR ARRAY:*    ]| X +---->| test3 10.92 1    12:30:08  |
   |                             |   |     |                            |
   |                             |   |     |                            |
   |                             |   |     |                            |
   +-----------------------------+---+     +----------------------------+ 
 
* One obnoxious feature of Sequencer logging is that you don't get any display of information until the Sequencer has completed its operations. To correct this (as noted in earlier chapters), a set of display functions were added in VEE 3.1:

   showPanel( UFname )
   showPanel( UFname, x, y )
   showPanel( UFname, x, y, width, height )
   hidePanel( UFname )
 
These functions allow display of information while the Sequencer is conducting its tests. You create a UserFunction with a panel view to provide display output -- named, say, "Disp()", which displays a single item on an Alphanumeric display -- and then your test functions can display this panel by invoking:

   showPanel("Disp")
 
Once it is visible, test functions can display data in the panel by invoking that UserFunction:

   Disp("This is a test!")
 
The panel will go away at the end of the program or when a test function uses "hidePanel()" to conceal it.

 TOP OF PAGE

[11.3] SEQUENCER -- THE FINE PRINT

* This fast tour of the Sequencer provides a basic understanding of the object. This section builds on that understanding by a full description of the Sequencer's features.

* The Sequencer object, as should be obvious by now, is used to control the order of calling a series of UserFunctions, Compiled Functions, or Remote Functions.


   +---------------------------------------------------+
   |                     Sequencer                     |
   +---+--------------------------------------+--------+
   |   |[                                    ]|        |
   |   |                                      | Return |
   |   |                                      |        |
   |   |                                      |        |
   |   |                                      |        |
   |   |                                      |  Log   |
   |   |                                      |        |
   +---+--------------------------------------+--------+
 
These functions are called through sequence transactions. After evaluating the expression, a transaction compares the value returned against a test spec. The sequencer then evaluates the next transaction. This transaction may be conditionally selected by the Sequencer according to the results of the previous test.

The Sequencer logs test results to the Log output pin, as well as (optionally) to a UserFunction, Compiled Function, or Remote Function.

The Sequencer's Return output pin is used for returning a result when a sequence has completed. By default, the Return output pin is zero, but a transaction with a "Next Operation" of "Then Return" will set the Return output pin to the specified value.

* The transactions are defined by clicking on the appropriate transaction field. The transactions may be EXEC or TEST transactions (as set by toggling the field in the upper left corner), and their parameters are set by dialogs.

EXEC transactions simply unconditionally execute a routine. They will not test the routine against limits, nor perform logging. The EXEC transaction dialog has the form:


 +-------------------------------------------------------------------------+
 |                         Sequence Transaction                            |
 +-------------------------------------------------------------------------+
 | [ EXEC: ] [ test1  ] [   ENABLED   ]                                    |
 | FUNCTION:      [testFunc(s) ] [  LOGGING DISABLED ]                     |
 | [  THEN CONTINUE  ]                                                     |
 | DESCRIPTION:  [                                   ]                     |
 |                                                                         |
 |                                                                         |
 |                                                                         |
 |                [  OK  ]                         [Cancel]                |
 +-------------------------------------------------------------------------+
 
The "EXEC:" field is followed by the transaction name field. This field contains a unique name that identifies the transaction. The ENABLED field that follows it can have four values:

If either of the last two cases are selected, a new field appears that allows the user to enter the name of an input pin or a function name:


   [ DISABLED IF:] [A    ]
   [  ENABLED IF:] [A    ]
 
The "FUNCTION:" field is followed by the name of the function. The LOGGING DISABLED field is fixed in this transaction and cannot be changed.

The "THEN CONTINUE" field is a little trickier to describe. It defines what operation the Sequencer will perform after performing the specified EXEC transaction:

Finally, the description field is simply a comment field. Any useful text can be placed there.

* The TEST dialog is similar, but contains more detail:


 +-------------------------------------------------------------------------+
 |                          Sequence Transaction                           |
 +-------------------------------------------------------------------------+
 | [ TEST: ] [ test1  ] [   ENABLED  ]                                     |
 | SPEC NOMINAL:        [.5    ] [ RANGE ] [0   ] [ <= ] ... [ <= ] [1   ] |
 | FUNCTION:      [testFunc(s) ] [ LOGGING ENABLED ]                       |
 | [  IF PASS  ] [  THEN CONTINUE  ]                                       |
 | [  IF FAIL  ] [  THEN CONTINUE  ]                                       |
 | DESCRIPTION:  [                                ]                        |
 |                                                                         |
 |                [  OK  ]                         [Cancel]                |
 +-------------------------------------------------------------------------+
 
The name and ENABLED fields operate the same as they do for EXEC transactions. The second line of the dialog is different, however. It specifies the test limits or bounds for the function to be called. It has a variety of forms:

   SPEC NOMINAL:    [.5    ] [   RANGE   ] [0   ] [ <= ] ... [ <= ] [1   ] 
   SPEC NOMINAL:    [.5    ] [   RANGE   ] [0   ] [ <  ] ... [ <= ] [1   ] 
   SPEC NOMINAL:    [.5    ] [   RANGE   ] [0   ] [ <= ] ... [ <  ] [1   ] 
   SPEC NOMINAL:    [.5    ] [   RANGE   ] [0   ] [ <  ] ... [ <  ] [1   ] 
   SPEC NOMINAL:    [.5    ] [   LIMIT:  ] ... [ >  ] [1    ]
   SPEC NOMINAL:    [.5    ] [   LIMIT:  ] ... [ >= ] [1    ]
   SPEC NOMINAL:    [.5    ] [   LIMIT:  ] ... [ <  ] [1    ]
   SPEC NOMINAL:    [.5    ] [   LIMIT:  ] ... [ <= ] [1    ]
   SPEC NOMINAL:    [.5    ] [   LIMIT:  ] ... [ == ] [1    ]
   SPEC NOMINAL:    [.5    ] [   LIMIT:  ] ... [ != ] [1    ]
   SPEC NOMINAL:    [.5    ] [ TOLERANCE:] + [1   ] - [1   ]
   SPEC NOMINAL:    [.5    ] [%TOLERANCE:] + [1   ]% - [1   ]% 
 
This should be largely self-explanatory. Note that the "SPEC NOMINAL:" field is merely for documentation, except in the case of the "TOLERANCE" and "%TOLERANCE" settings. Note that all such tests are done in the same way as the Comparator object. The test result may be a waveform, and the test limit(s) may be another waveform, or an array of type Coord. The test is performed to 6 significant digits. A tolerance of 0 may be specified.

The "FUNCTION:" field allows specification of the test function, as in the EXEC dialog. The LOGGING ENABLED field allows the transaction's results to be logged, or be set to LOGGING DISABLED if that is not wanted.

The next two lines specify what the Sequencer does after executing the test function. One line specifies what happens if the test passes, another what happens if it fails. There are a wide number of options:


   [  IF PASS  ] [  THEN CONTINUE  ]
   [  IF PASS  ] [   THEN RETURN:  ] [1     ]
   [  IF PASS  ] [    THEN GOTO:   ] [test1 ]
   [  IF PASS  ] [   THEN REPEAT   ] MAX TIMES: [3   ]
   [  IF PASS  ] [   THEN ERROR:   ] [10    ]
   [  IF PASS  ] [  THEN EVALUATE: ] ["RETURN 1"]

   [  IF PASS CALL: ] [passProc(1)  ] [  THEN CONTINUE  ]
   [  IF PASS CALL: ] [passProc(1)  ] [   THEN RETURN:  ] [1     ]
   [  IF PASS CALL: ] [passProc(1)  ] [    THEN GOTO:   ] [test1 ]
   [  IF PASS CALL: ] [passProc(1)  ] [   THEN REPEAT   ] MAX TIMES: [3   ]
   [  IF PASS CALL: ] [passProc(1)  ] [   THEN ERROR:   ] [10    ]
   [  IF PASS CALL: ] [passProc(1)  ] [  THEN EVALUATE: ] ["RETURN 1"]

   [  IF FAIL  ] [  THEN CONTINUE  ]
   [  IF FAIL  ] [   THEN RETURN:  ] [1     ]
   [  IF FAIL  ] [    THEN GOTO:   ] [test1 ]
   [  IF FAIL  ] [   THEN REPEAT   ] MAX TIMES: [3   ]
   [  IF FAIL  ] [   THEN ERROR:   ] [10    ]
   [  IF FAIL  ] [  THEN EVALUATE: ] ["RETURN 1"]

   [  IF FAIL CALL: ] [FailProc(1)  ] [  THEN CONTINUE  ]
   [  IF FAIL CALL: ] [FailProc(1)  ] [   THEN RETURN:  ] [1     ]
   [  IF FAIL CALL: ] [FailProc(1)  ] [    THEN GOTO:   ] [test1 ]
   [  IF FAIL CALL: ] [FailProc(1)  ] [   THEN REPEAT   ] MAX TIMES: [3   ]
   [  IF FAIL CALL: ] [FailProc(1)  ] [   THEN ERROR:   ] [10    ]
   [  IF FAIL CALL: ] [FailProc(1)  ] [  THEN EVALUATE: ] ["RETURN 1"]
 
These transactions should be self-explanatory, as long as the THEN CONTINUE action explanations for EXEC are remembered. Note that a THEN REPEAT action will repeat the specified number of times, and then the Sequencer will move on to the following action in the list. Note also that the return values of functions called by IF PASS CALL or IF FAIL CALL are ignored.

Finally, the "DESCRIPTION" field allows for a comment.

* The Sequencer's "Edit Default Preferences" allows specification of logging options as:


   +-----------------------------------------------------------+
   |                    Sequencer Properties                   |
   +-----------+-----------+-----------+-----------+-----------+
   |  General  |  Colors   |   Fonts   |  Logging  | Printing  |
   +-----------+-----------+-----------+           +-----------+
   | +- Record Fields To Log ---------------------+            |
   | | [x] Name           [ ] Low Limit           |            |
   | | [x] Result         [x] Pass                |            |
   | | [ ] Nominal        [x] Time Stamp          |            |
   | | [ ] High Limit     [x] Description         |            |
   | +--------------------------------------------+            |
   | +- Logging Mode -----------------------------+            |
   | | [Log to Output Pin Only]                   |            |
   | +--------------------------------------------+            |
   |                                                           |
 
The logging fields are defined as:

Logging configuration can be set as follows:

* An "Exec Trans" control input pin may also be added. The Exec Trans control input executes only the transactions with the specified names in the order given by a Text scalar or 1D array input.

All field expressions (such as ENABLED IF, FUNCTION, or spec limits) may contain input pin names, UserFunction calls, Compiled Function calls, Remote Function calls, HP VEE math functions, or record names of previously run transactions.

 TOP OF PAGE

[11.4] USING THE SEQUENCER TO PROMPT FOR USER INPUTS

* A VEE user had an interesting question. His program was supposed to query the user for several different items of information, and he wanted to figure out a way by which the user could cancel on a query and go back to make changes in earlier queries.

The VEE Sequencer turned out to be an excellent tool for the job. Querying a user and performing different actions on the replies is conceptually no different from testing a machine and performing different actions on the results, and can be easily used to perform the queries.

* Let's design a little query scheme using the Sequencer that asks the user for name, rank, and serial number. The user can back out of the sequence at any step of the query, and the existing values will not be changed.

In this example, these values are stored in three global variables, with the simple names:


   name
   rank
   serno
 
Since the user can input new values but back out before completing input, the input values he or she provides have to be buffered in a separate set of variables, which can be conveniently named:

   newname
   newrank
   newserno
 
* The first thing our sample program has to do is initialize these variables. It's not really necessary to initialize the buffer variables, since they won't be used until somebody actually loads them, but it is a good programming practice to always initialize variables to prevent unpleasant surprises.

These variables can be initialized by a UserFunction named "Init". This UserFunction is very simple, consisting of six Set Global (or Set Variable) objects, one for each variable, plus a text constant containing the arbitrary string "NULL" that is wired into all six.

* Performing the queries is also simple. They all basically request strings, so we can use the same UserFunction for all three queries, and just provide the UserFunction with parameters giving the prompt string and the name of the variable to be updated.

We'll call this UserFunction "GetReply()". All it does is pop up a Text Input dialogue box to query the user and get his or her reply. To allow the Sequencer to test the results of the query, the UserFunction returns "1" if the user makes an entry, and "0" if the user cancels the dialogue box.

The resulting UserFunction is very simple:


   +------------------------------------------------------------------+
   |                            GetReply                              |
   +---------+--------------------------------------------------------+
   |         |                                                        |
   |         |                +---+                                   |
   | prompt  +---+            | 1 +--------------------+              |
   |         |   |            +-+-+                    |              |
   |         |   |              |                      |              |
   |         |   |   +----------+----------+           |              |
   |         |   |   |      Text Input     |           |              |
   |         |   |   +--------+---+--------+           +-->+-----+    |
   |         |   +-->| Prompt |   | Value  +--------+      | JCT +--->|
   |         |       |        |   | Cancel +---+    |  +-->+-----+    |
   |         |       +--------+---+--------+   |    |  |              |
   |         |                               +-+-+  |  |              |
   |         |                               | 0 +--|--+              |
   |         |                               +---+  |                 |
   |         |                                      |   +----------+  |
   |         |                                      +-->|   Set    |  |
   | varname +----------------------------------------->|  Global  |  |
   |         |                                     name +----------+  |
   |         |                                                        |
   +---------+--------------------------------------------------------+
 
The core of this UserFunction is a Text Input dialogue box, with the "Prompt" pin brought out to allow it to be defined as a parameter. A Set Global (or Set Variable) object allows you to set the value of a variable, whose name is also input as a parameter. The UserFunction puts a value of "1" on the output, which is overwritten by a "0" if the "Cancel" pin is activated.

* This UserFunction is called by the Sequencer to perform the set of queries. The first query asks for the user's name. The transaction is set up as follows:


 +-------------------------------------------------------------------------+
 |                          Sequence Transaction                           |
 +-------------------------------------------------------------------------+
 | [ TEST: ] [ Query1 ] [   ENABLED   ]                                    |
 | SPEC NOMINAL:        [ 1    ] [ LIMIT ]                   [ == ] [1   ] |
 | FUNCTION: [GetReply("Your name?","newname")]       [ LOGGING DISABLED ] |
 | [  IF PASS  ] [  THEN CONTINUE  ]                                       |
 | [  IF FAIL  ] [  THEN RETURN:   ] [ 1 ]                                 |
 | DESCRIPTION:  [                                ]                        |
 |                                                                         |
 |                [  OK  ]                         [Cancel]                |
 +-------------------------------------------------------------------------+
 
This defines a test transaction named "Query1", which tests for a limit equal to "1". That is, the test succeeds if the value is "1", and fails if it is anything else. The "SPEC NOMINAL" is set to "1", since that's the expected proper return value, but it's largely irrelevant in this example.

This transaction calls "GetReply()", providing a prompt of "Your name?", and specifying that the return value be stored in the variable "newname". If "GetReply()" returns a "1", the test passes and the Sequencer moves on to the next transaction. If "GetReply()" returns a "0", the test fails and the Sequencer exits on the THEN RETURN, and also putting an arbitrary value of 1 on the output pin.

In reality, you would probably want to use a somewhat more tactful prompt, such as: "Please input your name." -- but I used a short prompt here just to save space in the illustration.

* The second transaction asks for rank:


 +-------------------------------------------------------------------------+
 |                          Sequence Transaction                           |
 +-------------------------------------------------------------------------+
 | [ TEST: ] [ Query2 ] [   ENABLED   ]                                    |
 | SPEC NOMINAL:        [ 1    ] [ LIMIT ]                   [ == ] [1   ] |
 | FUNCTION: [GetReply("Your rank?","newrank")]       [ LOGGING DISABLED ] |
 | [  IF PASS  ] [  THEN CONTINUE  ]                                       |
 | [  IF FAIL  ] [  THEN GOTO:     ] [ Query1 ]                            |
 | DESCRIPTION:  [                                ]                        |
 |                                                                         |
 |                [  OK  ]                         [Cancel]                |
 +-------------------------------------------------------------------------+
 
This is very similar to the first test transaction, except for a different name ("Query2"), different parameters for "GetReply()" ("Your rank?" and "newrank"), and a different action on test failure. Instead of performing a THEN RETURN, it does a THEN GOTO and jumps back to the first transaction, "Query1".

* The third transaction asks for serial number:


 +-------------------------------------------------------------------------+
 |                          Sequence Transaction                           |
 +-------------------------------------------------------------------------+
 | [ TEST: ] [ Query3 ] [   ENABLED   ]                                    |
 | SPEC NOMINAL:        [ 1    ] [ LIMIT ]                   [ == ] [1   ] |
 | FUNCTION: [GetReply("Serial number?","newserno")]  [ LOGGING DISABLED ] |
 | [  IF PASS  ] [  THEN CONTINUE  ]                                       |
 | [  IF FAIL  ] [  THEN GOTO:     ] [ Query2 ]                            |
 | DESCRIPTION:  [                                ]                        |
 |                                                                         |
 |                [  OK  ]                         [Cancel]                |
 +-------------------------------------------------------------------------+
 
This is almost the same as the second transaction, except for the transaction name ("Query3"), different parameters for "GetReply()" ("Serial number?" and "newserno"), and a THEN GOTO on failure back to the second transaction, "Query2".

* These transactions define a sequence that steps forward through all three queries, moving backwards through the sequence if the user cancels on the inputs.

Remember, however, that the queries have put values into buffer variables, so an "execute" Sequencer transaction has to be built to load the actual variables from the buffer variables, calling a UserFunction named "UpdateData()".

All "UpdateData()" does is use three Get Global (or Get Variable) objects to get the values of "newname", "newrank", and "newserno", and three Set Global (or Set Variable) objects to load the respective values into the variables "name", "rank", and "serial number".

The fourth transaction looks like this:


 +-------------------------------------------------------------------------+
 |                          Sequence Transaction                           |
 +-------------------------------------------------------------------------+
 | [ EXEC: ] [ Update ] [   ENABLED   ]                                    |
 | FUNCTION:      [ UpdateData()]  [  LOGGING DISABLED ]                   |
 | [  THEN CONTINUE  ]                                                     |
 | DESCRIPTION:  [                                   ]                     |
 |                                                                         |
 |                                                                         |
 |                                                                         |
 |                [  OK  ]                         [Cancel]                |
 +-------------------------------------------------------------------------+
 
This transaction is named "Update", and all it does is call "UpdateData()". Nothing to it.

Since the Sequencer executes transactions step by step, and this is the last of the four, this transaction will not be executed unless the previous three were. This allows the user to cancel all inputs up to the very last one without changing the original values of the "name", "rank", and "serno" variables.

* The final sample program is about as simple as you might want:


		       +-----------+
		       | Call Init |
		       +-----+-----+
			     | 
   +-------------------------+--------------------------+
   |                      Sequencer                     |
   +---+---------------------------------------+--------+
   |   | Query1(1) == 1                        |        |
   |   | Query2(1) == 1                        | Return |
   |   | Query3(1) == 1                        |        |
   |   | Update                                |        |
   |   |                                       |   Log  |
   |   |                                       |        |
   +---+---------------------+-----------------+--------+ 
			     |
                  +----------+---------+    +-------------+
		  | Get Global: name   +--->| Wyle Coyote |
                  +----------+---------+    +-------------+
			     |
                  +----------+---------+    +-------------+
		  | Get Global: rank   +--->| Predator    |
                  +----------+---------+    +-------------+
			     |
                  +----------+---------+    +-------------+
		  | Get Global: serno  +--->| 1           |
                  +----------+---------+    +-------------+
 
This displays some representative user inputs.

* This example was written using as conservative features of VEE as were possible to allow it to work on older versions. It can be mildly streamlined by using current VEE features.

Of course, this is an example, and has to be simple. A few comments for changes and improvements can be provided:

More complicated decision making could be performed by using additional transactions to test the value of a variable and branch appropriately.

For example, to generate a FOR loop, you could have a transaction before the beginning of a loop that calls a function to initialize a variable to the loop count. At the end of the loop, you could have a transaction that decrements the value, tests it for equality to zero, and loops back to the transaction just after the initialization transaction.

An UNTIL-BREAK loop could be devised by initializing a variable to 0, testing to see if it has been set to 1 by one or more of the transactions in the loop, and branching back if it is still 0.

However, one of the problems with the Sequencer is that it has enough features to create really complicated solutions when a simpler one might be much easier to write and maintain. In this case, the Sequencer proved to be clearly the very best tool for the job.

 TOP OF PAGE


 LAST PAGE  BACK TO INDEX  NEXT PAGE