v5.0 / 12 of 23 / 01 sep 99 / gvg
* VEE control flow is a tricky subject. The propagation rules are a little devious, though when analyzed are usually perfectly logical, and the only way to get a realistic grasp of them is through walking through examples.
It is difficult for someone who is experienced in working with VEE to remember how tricky the learning curve is. Concepts that seem difficult to the newcomer suddenly seem perfectly familiar, are taken for granted, and the difficulty is forgotten.
This chapter provides an overview of VEE control
* VEE control flow can be understood by analogy with a number of different
concepts. It has some similarities to digital logic, is in some ways like
the relay-ladder logic used in industrial controllers, is even to a degree
like laying out track for a model train set.
Most of all, however, it is still has much in common with a normal sequential
programming language, in particular in that (despite any initial impression
that VEE might give) you can't do two different things at the same time, and
that operations will follow each other in a sequence, though one which is not
necessarily as deterministic as in a conventional language.
To get started, here are some simple rules of VEE style:
One of the common remarks made by users having ghostly problems with VEE is:
"I deleted the object, replaced it with a new one of the same type, and then
it worked."
This is an strong indication of an error in control-flow design. The reason
such a substitution can make the program work is because VEE's execution
order depends on the order in which objects were added if you haven't
explicitly forced the execution to operate in a specific sequence.
Similarly, some programs that worked perfectly well in VEE 3.X don't work at
all in VEE 4.x because the execution order has changed (while remaining legal
under VEE propagation rules. There are also some peculiarities to VEE
execution under VEE 4.x compiled mode that are different from VEE 3.x).
Sometimes the sequence of execution between different objects doesn't matter,
sometimes it matters a great deal. This chapter will provide you with
information on how to make that judgement and otherwise deal with
control-flow problems.
* Writing VEE programs requires a grasp of how the core control constructs
work in controlling programs. The simplest control is in generating a simple
loop. If you want to generate a simple count, you can use a For Count
object:
* If you want to generate an endless loop, you can use an Until Break object:
You can set the time format though the "Edit Properties" entry in the
AlphaNumeric's object menu. The number formats include a set of time formats
for date and time, date, or time.
But what if you want to bail out of this loop at some time? This is a little
tricky. One possible approach is to just add an OK button to drive a Break
object to break out of the loop:
This ambiguity would occasionally lead to troubles with VEE 3.X, but when VEE
4.0 came out, the troubles became blatant and obvious. In reality, the same
effect can be obtained just by adding an OK Button and a Stop object as a
separate thread:
The Toggle should be configured to initialize to a 0 value to ensure that the
program starts up correctly. This program's actions are much more
predictable than those of the previous program.
* That said, now let's mix things up a little. Suppose you want to generate
a count over and over again. One user tried this trick:
What would work was of course the Until Break again:
Note the implication of this architecture: the path that is executing has to
complete before another path can be executed. If the path that is being
executed takes a long time to complete, you'll wait until it gets finished
before you do something else.
* It is not a great leap forward from the techniques outlined in the section
above to define how to handle more complicated VEE control tasks.
For an example, consider a VEE program that allows the user to select one of
several tasks, or have a task initiated by a service request (SRQ) from an
instrument. The following program (see xsrqtest.vee for the source) does
this task:
The two paths defined by the "Task 1" and "Task 2" buttons simply display
appropriate text ("TASK 1!" and "TASK 2!") in the Message Box to the right,
while the path defined by the "Quit" button simply stops the program.
The interesting part of the program is the SRQ handling component, of course.
Note that a 3478 State Driver is used at the beginning of the program to set
a 3478 DMM's SRQ mask to 63; this allows you to assert an SRQ by pressing a
button on the front panel of the DMM.
This done, the path that handles the SRQ simply uses the Interface Event
object to wait for the SRQ. It is set as follows:
Operating the example is simple; you run it and click the "Task 1" or "Task
2" buttons to get the appropriate pop-up display -- then if you press the SRQ
button on the front of the 3478 , you get the "SRQ!" display output -- and
go back to perform another task. The program stops when you click on the
"Quit" button.
* For another example along this line, consider a user who wanted to be able
to execute one of three different UserFunctions -- FunctionA, FunctionB, or
FunctionC -- and then have his program continue to execute the last selected
action. This could be done with the following program (see xpgmfunc.vee
for the source):
* For another applied problem in control flow, one of our HP support people
had a related problem where he wanted to generate a strip chart that would be
cleared every certain number of points, or whenever the user clicked a
button. Interesting idea, but doing it isn't intuitive.
The following program (see xclstrip.vee for the source) shows how it's
done:
The Strip Graph is reset every 100 times by a Counter driving an If/Then/Else
object. The output of the If/Then/Else is fed back to clear the Counter so
the count can begin over again. It is also reset by the Toggle button.
* One difficult concept to deal with in VEE is error handling -- that is, how
to perform an action and either repeat the action or continue after an error
occurs.
A simple way to show how to handle this is with a dialog box to represent an
action that can have different outcomes, as the following program (check
xerrdemo.vee for the source) shows:
Note how the element(s) to be executed sequentially after the "I/O" loop are
connected to the sequence-out pin of the Until Break. They are not
connected to any of the loop elements. Note also that you could connect the
Break object to the "Ignore" pin instead of the sequence-out pin and it would
work all the same.
* VEE 3.2 added a useful improvement to error handling. In previous versions
of VEE, users who wanted to trap errors on objects through error pins simply
got an error number, not an error message.
To get around this limitation, we added a new function named "errorInfo()"
that allows a program to retrieve full details of the error. For example:
* Beware of using error-handling as a standard practice, particularly with,
say, a Transaction Object whose transactions contain complicated math
formulas. VEE will allocate memory to parse these formulas, and if an error
occurs during that parse activity, will lose that memory -- causing an
incremental memory leak. This flaw is deeply rooted in the VEE architecture
and will not likely disappear any time soon.
* VEE propagation has certain idiosyncracies that can be difficult to puzzle
out. A few examples can help illuminate such problems.
* A VEE user was tryin to build a record of three waveforms as follows:
Since the Build Record object only received one input on each iteration of
the loop, only one input was valid at a time, and the object never fired.
After some more tinkering I came up with a solution using a "Shift Register"
instead of a "Demultiplexer", where all three inputs would be valid
simultaneously:
This could also be more simply done with a "Collector" if an array output is
desired instead.
Another approach would be to use the original scheme, but embed the circuit
that feeds the Build Record into a UserObject. The outputs of the UserObject
will all be valid when it completes operation:
This sounded like the same sort of problem: the Formula box was inside a
loop and getting two more more values in different cycles. As the inputs
were never valid in the same iteration of the loop, the Formula never fired.
The user's program showed this to be the case. The program, stripped down to
essentials relevant to the problem at hand, looked like this:
This particular example makes it more clear why VEE invalidates inputs on
each iteration of the loop.
This Formula box has two pins, A and B, and it simply adds the two values
together, though the particular operation it performs is generally irrelevant
for the purposes of this discussion.
Before the first execution of the loop, the two input pins have no values
and are invalid:
* In response to this circumstance, we are often asked to give the user more
control over the behavior of the input pins, but in reality the issue is easy
to deal with: just add memory elements to the program. This is as
convenient a solution as any other that could be conceived.
Memory can be provided in some cases by a Shift Register, but variables are
in general a straightforward way to accomplish most tasks. The example above
can be modified to do the job as follows:
Second, in this program Nval is always given a value before it is used in the
Formula box. A VEE programmer needs to be sure that this is the case, or he
or she will end up with a similar problem as that described for the
not-invalidated input pins above.
* Miswired Timer objects can lead to very odd timing results, and are an
excellent illustration of VEE propagation issues. For example, consider this
simple program:
This program may actually work, but it isn't likely to. There are two
problems. First, when the sequence-out pin of Block 1 fires, it will "ping"
both Block 2 and the first input of the Timer. As the program does not
deliberately specify which is pinged first, it can choose either. If it
chooses Block 2 to ping first, it will not ping the Timer input until Block 2
is done, giving very misleading timing.
What can be confusing about this is that if you show VEE data flow, the
data flow indicators may not indicate the actual sequence of operation. The
sequence of operation may change depending on how the objects were wired
together. Sometimes it will work, sometimes not.
The answer to this problem is to use a Do object to guarantee the proper
sequence of operations:
It is best to connect the second input of the Timer to the sequence-out pin
of Block 2. This way it will not be activated until Block 2 and all the
blocks it is driving have completed. If there are other blocks connected to
that sequence-out pin, a Do object should be used to ensure proper
sequencing.
Here's the right way to build this program:
[12.1] CONTROL FLOW OVERVIEW
[12.2] ELEMENTARY CONTROL FLOW
[12.3] ADVANCED CONTROL FLOW
[12.4] ERROR HANDLING
[12.5] PROPAGATION PROBLEMS
[12.1] CONTROL FLOW OVERVIEW
[12.2] ELEMENTARY CONTROL FLOW
+----------------------+
| Logging Alphanumeric |
+----------------------+
+---------------+ | 6 |
| For Count: 10 +--->| 7 |
+---------------+ | 8 |
| 9 |
+----------------------+
Note that the For Count counts from 0 to 9, not 1 to 10. Counter objects can
be nested, of course:
+---------------+
| For Count: 10 +---+
+-------+-------+ |
| |
| +-------+-------+ +---------+
| | For Count: 10 +-->| Counter +-->
| +---------------+ +---------+
|
+---+---+
| Beep |
+-------+
Note how the outer-loop counter generates a "beep" through its sequence-out
pin when it's done. This illustrates an important feature of how such
looping objects work: they don't generate a sequence-out pulse until
after everything they are driving has executed.
+-------+
| Until |
| Break +----+
+-------+ | +-----------------------------+
| | AlphaNumeric |
+---+---+ +-----------------------------+
| now() +--->| Sun 19/Feb/1995 13:19:16 |
+-------+ +--------------+--------------+
|
+-----+-----+
| Delay: 1 |
+-----------+
I add a delay here so the AlphaNumeric won't update more often than once a
second. Actually, an On Cycle would allow you to generate a container to
drive the "now()" object with any delay that you like and so would be
preferred in this case, but I will be developing the use of Until Break in
following examples, and might as well be consistent.
+-------+
| Until |
| Break +----+----------+
+-------+ | | +-----------------------------+
| | | AlphaNumeric |
| +---+---+ +-----------------------------+
| | now() +--->| Sun 19/Feb/1995 13:19:16 |
| +-------+ +--------------+--------------+
| |
+--+--+ +-----+-----+
| OK +---+ | Delay: 1 |
+-----+ | +-----------+
|
+---+---+
| Break |
+-------+
However, the approach of using two parallel paths has a major drawback:
there is no guarantee that one path will be executed. One path might be
executed all the time, or the other path might be executed all the time, or
execution might be balanced between the two. There is nothing in the program
to determine what will happen.
+-------+ +------+
| Until | | Quit +------+
| Break +--+ +------+ |
+-------+ | +---+---+
| | Stop |
| +-------+
|
| +-----------------------------+
| | AlphaNumeric |
+---+---+ +-----------------------------+
| now() +-->| Sun 19/Feb/1995 13:19:16 |
+-------+ +--------------+--------------+
|
+-----+-----+
| Delay: 1 |
+-----------+
But what if the program has other threads that can't be halted? Then a
Toggle button can be wired into the program to cause it to break out:
+-------+
| Until |
| Break +--+
+-------+ | +-------------------+
| | If/Then/Else |
+----+----+ +---+--------+------+
+->| Toggle +--+-->| A | A == 1 | Then +---------------+
| +----+----+ | +---+--------+------+ |
+-------|-------+ +---+---+
| | Break |
| +-----------------------------+ +-------+
| | AlphaNumeric |
+---+---+ +-----------------------------+
| now() +--->| Sun 19/Feb/1995 13:19:16 |
+-------+ +--------------+--------------+
|
+-----+-----+
| Delay: 1 |
+-----------+
The Toggle is wired back to its Reset pin. If a user clicks on the Toggle
with a mouse, it is set to 1, and so the Break object is fired. Otherwise,
control is passed on to the rest of the program. (This particular trick is
used in examples in earlier chapters.)
+-------+
| Start |
+---+---+
|
+--->+-----+
| JCT +----+ +----------------------+
+--->+-----+ | | Logging Alphanumeric |
| | +----------------------+
| +-------+-------+ | 6 |
| | For Count: 10 +--->| 7 |
| +-------+-------+ | 8 |
| | | 9 |
+---------------+ +----------------------+
However, this only counted from 0 to 9 once. I was appalled at this since I
could take one look at it and know it wasn't likely to work. Then I realized
I couldn't actually say why it wouldn't work. It was just that after
having played with VEE for a long time I had a feel for what would work and
what wouldn't. (This construct is actually illegal in VEE 4.X.)
+-------+
| Until |
| Break +---+ +----------------------+
+-------+ | | Logging Alphanumeric |
| +----------------------+
+-------+-------+ | 6 |
| For Count: 10 +--->| 7 |
+---------------+ | 8 |
| 9 |
+----------------------+
* Anyway, given these techniques I can now demonstrate how to create a
general architecture for interactive VEE programs. Let's consider a simple
program where the user can select one of two actions or exit the program by
clicking on the appropriate Toggle buttons; this program has the form:
+-------+
| Until |
| Break +---+
+-------+ | +--------------+
| | If/Then/Else |
+---+---+ +-------+------+
+->| Key 1 +--+-->| A==1 | Then +--+
| +---+---+ | +-------+------+ |
+------|------+ |
| +---------+----------+
| | Text |
| +--------------------+
| | You pressed key 1! +--+
| +--------------------+ |
| |
| +--------------+ |
| | If/Then/Else | |
+---+---+ +-------+------+ |
+->| Key 2 +--+-->| A==1 | Then +--+ |
| +---+---+ | +-------+------+ | |
+------|------+ | |
| +---------+----------+ |
| | Text | +-->+-----+
| +--------------------+ | JCT +--+
| | You pressed key 2! +----->+-----+ |
| +--------------------+ |
| |
| +--------------+ |
| | If/Then/Else | +--------------+
+---+---+ +-------+------+ |
+->| Quit +--+-->| A==1 | Then +--+ | +--------------+
| +-------+ | +-------+------+ | | | AlphaNumeric |
+-------------+ | | +--------------+
+---+---+ +->| |
| Break | +--------------+
+-------+
The concept is simple: each separate action is positioned in a ladder of
selections, and each action ends in a Next object to drive the next iteration
of the Until Break object. You can add more actions as you need to.
[12.3] ADVANCED CONTROL FLOW
+-------+ +-----------+ +--------------+
| ID | | Toggle | | If/Then/Else |
| 3478 | | +---+---+ +--------------+
+---+---+ | +->| Quit +-+--->| A==1 +--------+
| | | +---+---+ | +--------------+ |
+---+---+ | +------|-----+ +---+---+
| Until | | | +--------------+ | Break |
| Break +---+ Toggle | | If/Then/Else | +-------+
+-------+ +----+----+ +--------------+
+->| Task 1! +-+-->| A==1 +--------+
| +----+----+ | +--------------+ |
+-------|------+ +----+----+
| +--------------+ | TASK 1! +--> T1
Toggle | | If/Then/Else | +---------+
+----+----+ +--------------+
+->| Task 2! +-+-->| A==1 +--------+
| +----+----+ | +--------------+ |
+-------|------+ +----+----+
| +--------------+ | TASK 2! +--> T2
| | If/Then/Else | +---------+
+------+------+ +--------------+
Interface Event | SRQ: hpib7 +-->| A!=0 +-------+
+-------------+ +--------------+ |
+------+------+
Device Event | Spoll: 3478 |
+------+------+
|
T1 --->+-----+ +---------+ +----+----+
T2 --->| JCT +-->| Message | Text | SRQ! +--> SRQ
SRQ --->+-----+ | Box | +---------+
+---------+
The program is driven by an Until Break object. The Until Break object
drives the four parallel paths within the program -- controlled by three OK
buttons ("Task 1", "Task 2", "Quit") and the Interface Event object ("SRQ:
hpib7") -- and an outer loop that guarantees repeated execution of the paths.
|
+---------------+---------------+
| SRQ: hpib7 |
+-----------------------+-------+
| Interface: [ hpib7 ] | |
| Action: [NO WAIT] | event +-->
| Event: [ SRQ ] | |
+-----------------------+-------+
When an SRQ occurs, the Interface Event object then fires off the Device
Event object to do a serial poll, which clears the SRQ on the 3478:
|
+-----------------+------------------+
| SRQ: hpib7 |
+---------------------------+--------+
| Device: [ 3478 (hp3478) ] | |
| Event: [ Spoll ] | status +-->
| Action: [ NO WAIT ] | |
| Mask: [ #H0 ] | |
+---------------------------+--------+
Since NO WAIT is set, this object does the serial poll and then proceeds
(firing off a text object to display that an SRQ has occurred); the mask
value is irrelevant. (You can also do a WAIT SPOLL through a Direct I/O
object if desired.)
+-------+
| Until | +------+
| Break +--+ | Quit +--+
+-------+ | +------+ |
| |
+---------+--------+ +---+---+
| Execute Function +--+ | Stop |
+------------------+ | +-------+
OK Button |
+-------+-------+
| Radio Buttons | +-----------+ +-----------+
+---------------+ | Formula | | |
| <*> FunctionB | +-----------+ | +-------+-------+
| < > FunctionB +-->| asText(A) +--|-->| Call Function |
| < > FunctionC | +-----+-----+ | +---------------+
+---------------+ | |
+--------+
This program calls three different functions -- FunctionA, FunctionB, and
FunctionC (all they do is pop up a message box indicating "Task A!", "Task
B!", or "Task C!"). The scheme is quite simple: An OK button fires off a
Radio Buttons box that contains a list of the functions; the selected entry
is converted into text and then sent into a Call Function box.
+-------+
| Until +--+
| Break | |
+-------+ |
|
+-------+-------+ |
| Random Number +--------------------------------------------->|Trace 1]
+-------+-------+ |
| +------+ |
| | Quit +--+ |
| +------+ | |
| | |
| +---+---+ |
| | Stop | |
| +-------+ |
+-------|-------+ |
| +----+----+ | +-----------------------------------+ |
+->| Toggle +--+ | If/Then/Else | |
+----+----+ | +-------+--------------------+------+ |
Reset | +-->|[A Any]|[(A==1) OR (B==100)]|[Then]+--+-->|[Clear]
| | | | | | |
+--------+ +-->|[B Any]| |[Else]| |
| | +-------+--------------------+------+ |
+-->+---------+ | |
| Counter +--+ |
+-->+---------+ |
| Clear |
| |
+-----------------------------------------------------------+
An Until Break object clocks the program. It triggers a sequence of random
numbers that are driven into the Strip Graph object (which is only shown as a
set of input pins at the right of the diagram).
[12.4] ERROR HANDLING
+-------+
| Until |
| Break +------------------------+
+---+---+ |
| +---+---+
| | Beep |
| +---+---+
| |
| +--------------------+--------------------+
| | Message Box |
| +--------------------------------+--------+
| | Message [ I/O Error ] | Abort +-------------+
| | Symbol [(!)] [ Exclamation ] | Retry +--+ |
| | Buttons [ Abort Retry Ignore ] | Ignore | | |
| | Default [ Abort ] | | | |
| +--------------------+-----------+--------+ | |
| | | |
| +---+---+ +---+---+ +---+---+
+-------------+ | Break | | Next | | Stop |
| +-------+ +-------+ +-------+
|
+------------------+-----------------+
| Message Box |
+-------------------------------+----+
| Message [ ALL DONE! ] | |
| Symbol [(i)] [ Information ] | OK |
| Buttons [ OK ] | |
| Default [ OK ] | |
+-------------------------------+----+
This program pops up a Message Box to ask if you want to "Abort Retry
Ignore". If you "Abort", a Stop object is executed to stop the program; if
you "Retry", you get a "Next" loop to try again. If you "Ignore", you get a
"Break" that turns off the Until Break that then sequences down to the
Message box that tells you the program is over.
+------------------+
| Formula |
+---+ +---+-----+--------+
| 1 +----->| A | A/B | Result +--->
+---+ +-->| B | | error +--+
| +---+-----+--------+ | +--------------------------+
+---+ | | | AlphaNumeric |
| 0 +--+ +------+------+ +--------------------------+
+---+ | errorInfo() +-->| {511, "Divide/Mod by 0"} |
+-------------+ +--------------------------+
This example is also part of the file xerrdemo.vee. Of course, this file
will not load in earlier versions of VEE.
[12.5] PROPAGATION PROBLEMS
+---------------+ +--------------------+
+-----------+ | Demultiplexer | | Build Record |
| Function | +------+--------+ +---+------------+---+
| Generator +-------------->| Data | Addr 0 +--->| A | Output | |
+-----------+ | | Addr 1 +--->| B | Shape: | R +-->
+-->| Addr | Addr 2 +--->| C | [Array 1D] | |
+-----------+ | +------+--------+ +---+------------+---+
| For Range +--+
| (0:2) |
+-----------+
The "Build Record" object would never fire. This was a consequence of the
way VEE loops work. Within a loop, input values are invalidated at the
beginning of each loop, otherwise an object might generate an output based on
previous rather than current values on its input pins.
+-----------+
| For Range |
| (0:2) +------------+
+-----+-----+ |
| |
+------------------|-----------------------+
| |
+-------+--------+ +---------+----------+
+-----------+ | Shift Register | | Build Record |
| Function | +------+---------+ +---+------------+---+
| Generator +--->| Data | Current +--->| A | Output | |
+-----------+ | | 1 Prev +--->| B | Shape: | R +-->
| | 2 Prev +--->| C | [Array 1D] | |
+------+---------+ +---+------------+---+
This program clocks three waveforms into the "Shift Register" and then
strobes the "Build Record" to generate a record of them.
+-------------------------------------------+
| UserObject |
+---------------------------------------+---+
| +---------------+ | | +--------------------+
| +-----------+ | Demultiplexer | | | | Build Record |
| | Function | +------+--------+ | | +---+------------+---+
| | Generator +--->| Data | Addr 0 +-->| X +--->| A | Output | |
| +-----------+ | | Addr 1 +-->| Y +--->| B | Shape: | R +-->
| +->| Addr | Addr 2 +-->| Z +--->| C | [Array 1D] | |
| +-----------+ | +------+--------+ | | +---+------------+---+
| | For Range +-+ | |
| | (0:2) | | |
| +-----------+ | |
+---------------------------------------+---+
* Use of a Demultiplexer object inside a loop is a somewhat common source of
confusion on VEE operation. We received a bug report on VEE that indicated a
Formula box wasn't generating an output, even though its two input pins were
receiving inputs.
+--------------+
| For Count: 4 +--+
+--------------+ |
|
+----------+
| +-----+
| +------------------+ +-->| 0 |
| | Demultiplexer | | +-----+
| +------+---+-------+ |
+-->| Data | | Addr0 +-----+ +-----+
| | | | Addr1 +-------->| 1 |
| | | | Addr2 +-----+ +-----+
+-->| Addr | | Addr3 +--+ |
+------+---+-------+ | | +-----+
| +-->| 2 |
| | +-----+
| |
| | +-----+
+--|-->| 3 |
| | +-----+
| |
| | +-------------------+
| | | Formula |
| | +---+---------------+ +-----+
| +-->| A | A + B +-->| |
+----->| B | | +-----+
+---+---------------+
The Demultiplexer is driven by the For Count object to output a single value
(0 through 3) for each cycle of the count. Since only one value is output
per cycle, and the inputs to the Formula box are made invalid after each
cycle, the two inputs A and B of the Formula box are never valid at the same
time, and nothing happens.
A: *
B: *
Result: *
Suppose on the first iteration of the loop, the two pins are given the values
13 and 23. This gives:
A: 13
B: 23
Result: 36
No problem so far. Now suppose on the next iteration of the loop, the two
pins are given the values 17 and 50. This would presumably give:
A: 17
B: 50
Result: 67
However, suppose that 17 is the first value sent to the Formula box and that
the input pins haven't been rendered invalid after the first iteration. That
means that until the second value, 50, is sent to the Formula box, the actual
situation is:
A: 17
B: 23
Result: 40
Since in this case, the Formula box fires the instant it gets the 17, the
Formula box has generated a bogus output (40) and sent it out over its output
line. This won't happen if the inputs were made invalid before beginning the
loop, which is what VEE does in practice.
+---+ +-----------------+
| 0 +-->| Set Value: Nval |
+---+ +--------+--------+
|
+-------+-------+
| For Count: 4 +--+
+---------------+ |
|
+--------------------+
| +-----+
| +------------------+ +-->| 0 |
| | Demultiplexer | | +-----+
| +------+---+-------+ |
+-->| Data | | Addr0 +-----+ +-----+
| | | | Addr1 +-------->| 1 |
| | | | Addr2 +-----+ +-----+
+-->| Addr | | Addr3 +--+ |
+------+---+-------+ | | +-----+
| +-->| 2 |
| | +-----+
| |
| | +-----+
+--|-->| 3 |
| | +-----+
| |
| | +------------------+
| +-->| Set Value: Nval |
| +------------------+
|
| +-------------------+
| | Formula |
| +---+---------------+ +-----+
+----->| A | A + Nval +-->| 5 |
+---+---------------+ +-----+
There's a few tricks to watch out for here. First, the global variable Nval
is initialized as the first thing in the program. There's no necessary
reason to do it in this particular program, as it will otherwise initialized
before it is used, but it's a good programming practice.
+-----------+
| |
| Block 1 |
| |
+-----+-----+
|
+--------+ +--------------+
| | | AlphaNumeric |
+-----+-----+ +-->+---------+ +--------------+
| | | Timer +-->| |
| Block 2 +--+-->+---------+ +--------------+
| | |
+-----------+ |
+-------------------+
| |
| +-----------+ | +-----------+
| | | | | |
+-->| Block 3 | +->| Block 4 |
| | | |
+-----------+ +-----------+
The "Blocks" in this program are arbitrary VEE objects, UserFunctions,
UserObjects, or whatever. The objective of the program is to time how long
Block 2 takes to execute.
|
+--+--+
| Do +--> do this first
+--+--+
|
then this
The other problem is that the second input of the Timer is connected to the
output pin of Block 2, and shares that wire with Block 3 and Block 4. There
is no guarantee which of these will be executed first, and so the timing
is very misleading.
+-----------+
| |
| Block 1 |
| |
+-----+-----+
|
+--+--+
| Do +---------+
+--+--+ | +--------------+
| | | AlphaNumeric |
+-----+-----+ +-->+---------+ +--------------+
| | | Timer +-->| |
| Block 2 +--+ +-->+---------+ +--------------+
| | | |
+-----+-----+ | |
| | |
+--------|---+
|
+-------------------+
| |
| +-----------+ | +-----------+
| | | | | |
+-->| Block 3 | +->| Block 4 |
| | | |
+-----------+ +-----------+