View Single Post
  #6   Spotlight this post!  
Unread 15-06-2010, 14:45
Luke Pike's Avatar
Luke Pike Luke Pike is offline
Programmer
FRC #1501 (THRUST)
Team Role: Mentor
 
Join Date: Jan 2008
Rookie Year: 2008
Location: Huntington
Posts: 114
Luke Pike is a name known to allLuke Pike is a name known to allLuke Pike is a name known to allLuke Pike is a name known to allLuke Pike is a name known to allLuke Pike is a name known to all
Re: LabVIEW for C/C++/Java Programmers

Quote:
Originally Posted by Gdeaver View Post
The Key to Successful programming a robot in Labview is to understand the concept of data flow. Many of the text for Labview state that a procedural programming mindset can cause problems, but do not define or give very good examples of exactly what data flow means. There is an opportunity for some one to write a paper on data flow best practices. This would help the FRC community allot. Anyone up to the challenge? The next big thing that could help with the robot programming is a good tutorial on state machines. There are some good college level text on the subject but most students seam to have problems comprehending them. Another opportunity for a white paper. This year our robot code was much more complex than previous years. We had some serious control problems. As the the season progressed and the code evolved, it went from a procedural flavor to data flow and our problems resolved. This happened by experimentation not by design. The question is how to teach these concepts to new Labview programmers.
Here's my crack on data flow:

LabVIEW Data Flow Explained

Most people who come to LabVIEW from a procedural language like C or C++ have trouble understanding the execution order of the code. The trouble comes from trying to making things happen in a sequence, as in do A then B. LabVIEW code does not have a sequentially defined order like text languages, where the execution goes from line to line. Instead, execution is defined by data flow.

Data Flow

In LabVIEW, any piece of code, either a primitive VI or a user-defined VI, can have input and output terminals. In order to give these terminals a value, data must be "wired" into these terminals from another source, either a UI control, constant, or output terminal. Wires that are connected to inputs must have a value, they cannot be null. The way LabVIEW decides which part of the code to execute depends on what wires have values. Suppose you have two constants wired into a multiply block, and the output of that is wired into a negate block. The negate block cannot execute until it's input wire has a value. Therefore, the multiply block executes first, as it's input wires have values (the constants). After the multiply block has run, the output terminal gives the negate block's input wire a value. Now that all of the inputs to the block have values, the negate block can run.

And that's it. As long as a block or VI's input wires have a value, it can run. Inputs that are not wired and not required have a default value, so as long as the others have values, it can still run. That means that if you have two multiply blocks with constants wired into their inputs and no output from one multiply block goes into an input to the other, the order in which those multiply blocks execute is undefined; for all intents and purposes, they happen at the same time, or in parallel. Now, if you wire the output of one of the multiply blocks to the input of the other, then the first block has to execute first, then the next as it is waiting for a value.

Using Data Flow To Control Execution

Now that data flow has been explained a little, how is it used to control the execution of code? For most operations, nothing else needs to be done. A series of calculations on a number simply execute as the previous calculations complete until it is done. However, some things don't have data dependancies (wires) between each other, yet still must happen in a sequence. Many first time LabVIEW users use the sequence structure to make things happen one after the other. While this can be done, it's generally not the best paradigm to use when solving a problem.

Here's a few alternative things to try:

First, use error clusters to enforce data flow. Most VIs have error in and out terminals that are usually used for error checking. Another way to use them is to wire the error out from one VI to the error in on another VI to make sure the first VI executes before the second.

Secondly, if the VIs don't have error terminals, you could put each segment of code into a sub VI, add error in and out terminals, and wire the error out terminals into the error in terminals to make the VIs execute in order.

Lastly, if your code is too complicated for the other two suggestions, consider a state machine. A state machine executes code based on a state. Once one state is deemed complete, the next state is executed, or any other state depending on the need. State machines consist of a while loop with a case structure inside. An enum in a shift register is used to determine the state. The enum is wired into the case structure and each case has code for each state. To move to another state, simply wire a different value for the enum into the right terminal of the shift register and the next loop will execute it. To make the state machine into a VI that can be called from anywhere, wire a boolean true into the stop condition terminal of the while loop and make sure that nothing is wired into the left side of the enum shift register. Put the code in a sub VI, give it inputs and outputs if needed. The sub VI will remember the values of the shift registers and maintain it's state. Here's a post of mine on how to make a state machine.

If you have any questions or improvements, feel free to post them.
__________________
Twitter Profile