For this part of the tutorial, we will use the LabRAD Simple Connection VIs to write a Client Module that takes data on the dragon curve, exactly like we just did in the Explorer.
Let's open LabVIEW and create a New Blank VI. In the Block Diagram, place a copy of the Simple Client Connection.vi, either from the User Libraries or the Select a VI... entry in your Functions Palette (depending on how you chose to install the components). Right-click on the manager input (top left) of the new VI and select Create =>
Control from the pop-up menu. This should leave your Block Diagram looking something like this:
It will also place the following control onto your VIs Front Panel:
Now, right-click the Simple Client Connection's connection name input (center left) and select Create =>
Constant. Change the content of the newly created constant string to LabVIEW Dragon Curve. Next, add two indicators for the welcome message and client ID outputs (center right) using Create =>
Indicator. Finally, place a copy of the Simple Disconnect VI onto the Block Diagram and wire its connection ID and error in inputs to the corresponding outputs of the Simple Client Connection. If you like, right-click all your controls and indicators and uncheck View As Icon and your Block Diagram should end up looking something like this:
Your Front Panel might look like this:
If you now execute your VI without having typed the correct password, you will most likely get the following error message:
After typing the correct password, though, an execution of the VI should complete without errors and update the welcome message and client ID indicators. You might also be able to see your LabVIEW client connection flash up in the LabRAD Manager for a brief second every time you execute it.
Aside:
If you leave out Simple Disconnect and run your VI a couple of times, you might start to see connections piling up in the LabRAD Manager's list. I am not sure why LabVIEW sometimes closes the connections automatically and sometimes doesn't. That's why it's safer to always specifically ask for a disconnect. If you completely close LabVIEW, though, all the lingering connections should go away.
Since LabVIEW is the most restrictive of the languages we work with when it comes to data types, the LabRAD Data Types were chosen specifically to support LabVIEW. This makes it extremely straight forward to create LabRAD Packets in LabVIEW. All you need to do is create the data-structure you want to send and pass it directly into Build Packet.
To create a new Dataset with the Data Vault, we need to first change into our working directory and then issue a new command and add parameters. Let's build a packet that contains all three of these requests in one:
For the cd Request's data (__s__), drop an Array onto your Block Diagram and stick a single String constant into it. Resize the array container to show you 4 elements simultaneously. Double-click the first one and type Test. Type Fractals into the second one. Now, right-click the Test element and select Data Operations =>
Insert Element Before. This will add another entry before Test that contains an empty string. Your resulting data structure is equivalent to the ['', 'Test', 'Fractals']* from before and should look like this:
For the new Request (s(ss)(sss)), create a Cluster containing a String, an Array of Clusters of two Strings, and an Array of Clusters of three Strings. Type Dragon Curve into the String of the main Cluster. Type x and y respectively into the first String of each of the first Clusters inside each of the Arrays. Leave the other entries empty. The data structure you get is equivalent to the ('Dragon Curve', [('x', '')], [('y', '', '')]) from before and should look like this:
Aside:
If you run a wire from this Cluster somewhere and hold the mouse over it, LabVIEW will show you its type in the Context Help ([Ctrl]+[H]) as:
Finally, for add parameter (here: sw), we will build the data structure on the fly. For this, create a String constant containing Iterations. Drop a Numeric Control onto your Front Panel and change it's Label to Iterations. Double-click it to locate it in the Block Diagram. Right-click on it and change the Representation to U32. Again, you can uncheck View As Icon if you like. Use Bundle to combine the String constant and the Numeric Control into a Cluster:
Now, we need to add these three data structures as Records into a Request Packet. For this, we use three copies of the Build Packet VI. The VI is polymorphic and can accept either a number for its setting input or a string. This allows you to choose the Setting that the record is meant for either by ID or by Name. Place one of the three VIs next to each of your data structures and wire them into the data input. Add a String constant for each with the content cd, new, and add parameter respectively and wire them into the setting input. Finally, link the VIs' records inputs and appended records outputs in the order in which you would like the Records to be processed by the Server. This part of your Block Diagram should look something like this:
To send the Request out, we use the Simple Request VI. Stick a copy of it between Simple Client Connection and Simple Disconnect and wire the appended records output of the last Build Packet VI to its records in input. Make a String constant and set its content to Data Vault and wire it into the target input (this input is also polymorphic). Your entire Block Diagram should now look like this:
If you run your VI at this point, it will create empty datasets in the Data Vault with the correct axis labels and a parameter named Iterations.
Next, we need to initialize the data run by sending a Request to the Fractal Server's Init Dragon setting. To build this packet, all you need to do is place a Build Packet VI and wire its setting input to a String constant containing Init Dragon and its data input to the Numeric Control from before. To send it, use the Simple Request VI with its target input wired to a String constant containing Fractals. Stick this VI between the previous Simple Request and Simple Disconnect (See later for updated Block Diagram).
This request will return information - the number of points in the curve - that we will want to display to the user. We can extract it using the Parse Packet VI. There are three ways in which you can use this VI, depending on whether you know exactly what data type you are expecting in the Reply, or whether there is a couple of different possibilities, or whether you have absolutely no idea. Let's go over these:
If you know exactly what data type you are expecting as the answer, or if you only care about one of the options, you can use the following arrangement to extract the data:
The left two VIs build and send the Request as before. The records out and the error out outputs of the Simple Request VI are wired into the Parse Request VI. The index output of the Build Packet VI is wired into the index input of Parse Request. The index is used to keep track of the potentially multiple Requests contained inside a Packet. The rest of the connections to this VI are needed to turn the returned data into a regular LabVIEW type.
Aside:
LabVIEW is statically typed, i.e. it needs to know the type of each wire and input/output terminal as soon as you place it. But the Build Packet and Parse Packet VIs need to be able to handle data types that are unknown at that time. To allow for this flexibility, LabVIEW uses Variants (purple wires). A Variant is somehwat like a Cluster, except that LabVIEW doesn't require immediate knowledge of what is going to be inside it. This is made possible by severely restricting the capabilities of Variants. The most useful things you can do is turn them into binary strings and back (we use this to generate the data that actually goes out on the TCP connection) and to try to convert them into a specific regular data type. This latter conversion is achieved using the Variant To Data VI (the yellow one with the cog in the above diagram) from Advanced =>
Data Manipulation =>
Variant in your Functions Palette. This VI works by accepting a Variant input and a prototype for the data that you would like to extract from it. You can think of a prototype as an example piece of data whose sole purpose is to define the type of some other piece of data. The Variant To Data VI adjusts its output to match the wire going into the type input. This output can then be used like any other output of that data type.
The purpose of the Parse Packet VI is to turn the binary data in the received Records into a Variant using one of the types inside a Cluster that you wire into its accepted types input. If you are only expecting a single possible data type, the Cluster that you connect to accepted types only needs to contain one prototype element for that type (here: a U32 number). Using Unbundle, you can use this same prototype element as the type input of the Variant to Data VI. The output of this VI then contains the data from the Record. You can use the error out output of the whole chain to check if everything went ok.
The reason why this is somewhat convoluted is two-fold: For one, the Parse Packet VI needs to accomodate the other two Parse Options explained below. But, more importantly, the version of LabVIEW that we use does not allow us to turn that group of three VIs (Parse Packet, Unbundle, and Variant To Data) into a single SubVI with the ability to accomodate any data input like the Variant To Data VI does.
If there are a few known possibilities for what data type you are expecting as the answer you can use the following arrangement to extract the data and handle it according to its type:
Here, the Cluster wired to accepted types contains two prototype elements: A Number (U32) with the label Number and a String with the label Text. Parse Packet will now check for the FIRST element in your Cluster that matches the type (ignoring units!) of the data contained in the Record. It will return a Variant of that type at the data output and the label of the prototype element at the type label output.
This label is used as the selector of a Case Structure. The Case Structure should handle the different possible cases (by label) independently.
As shown here, you can wire your prototypes Cluster into the Case Structure and use Unbundle By Name to extract the correct prototype for the Variant To Data VI. The call to Variant To Data should ALWAYS succeed, provided the text shown inside Unbundle By Name matches the content of the case selector label and provided your labels inside the prototype Cluster are unique.
If you have no idea what data type you might get returned (like when reading a parameter from a random dataset from the Data Vault), you can use the following arrangement:
If you leave the accepted types input of the Parse Packet VI unwired, the VI will return the data converted into a Variant at its data output and the LabRAD Type Tag of the data at the type label output. If all you want to do is display the result to the user, you can create a Variant Indicator and wire it to data. It is not recommended that you use the type label output as the selector of a Case Structure since LabRAD Type Tags are not unique (they can contain random comments, whitespace, commas, etc.). You would instead need to write a function that actively parses the type tag.
For our purposes here, let's use option 1, since we know that we are expecting a U32 back giving us the number of points in the curve.
Since we only want to run the data-taking part of our code if the setup completed successfully, let's enclose it in a Case Structure. You may have already noticed the boolean errors output of the Simple Request VIs. This output returns (True) if the Request failed entirely or if the Reply Packet contains any Error Records. We can use the two errors outputs of both of our requests and wire them together using an Or function into the selector input of the Case Structure. In the True case (the case where there was errors), we then simply forward the connection ID and errors through the Case Structure without any further processing. For now, this will leave our Block Diagram looking something like this:
Now, we've pretty much covered all LabRAD specific functionality, so the rest of this part of the tutorial will be to use these skills to actually take some data.
Let's make the Case Structure a bit larger and add some more of the same things we already used above to its False case to make it look like this:
The Error case of the Case Structure inside the While Loop looks like this:
Even though it's probably clear what is going on, let's go over it quickly just for completeness:
First, we send a Request to the Fractal Server's Dragon setting to ask it for the next 1000 points of the Dragon Curve. We try to convert the returned data into an Array of Clusters with two DBLs.
If this succeedes, this array contains more data points. The little For Loop converts the data from (vv) (the type returned by the Fractal Server) to 2v (the type required by the Data Vault) by applying the Cluster To Array function to each of the elements in the data array. The new data array is then passed to the Data Vault's add setting and the While Loop is continued.
Once all the data has been returned by the Fractal Server, it will send an empty Reply to our Request. This cannot be converted into the array type we gave the Parse Packet VI and thus raises an error. In the Error case of the Case Structure, we clear this error and tell the While Loop to stop executing.
Now, if you type in your password and select an iteration depth (try 10 for starters), the VI will create a new dataset and store all values in it. If you look at the Grapher, you should be able to watch the data come in and get plotted live.
... to be continued ...
~ Markus Ansmann, Feb 27th, 2008