This is a follow-on from my colleague Sorin Serban's recent post, in which he looked at invoking methods from the NAG Library for .NET from within the LabVIEW programming environment. The motivation for this work was the realization that many LabVIEW users want to enhance their applications by making use of some of NAG's numerical routines. Recently, I've been using our Fortran and C libraries to do the same thing, and some of this work is discussed here.
The mechanism used within LabVIEW to call a routine from either of these libraries is different from that used in the case of the .NET Library. This is because that library is a so-called .NET assembly, whilst the C and Fortran libraries are (on the Windows platform) dynamic link libraries (DLLs). The .NET assembly uses the common language runtime (CLR) and the .NET framework to manage assembly functions, and to export information about classes, methods, properties and events. The practical implication of this is that when a method from the NAG Library for .NET is loaded into LabVIEW, information about its function arguments is automatically loaded as well. This is not the case with a DLL, as we shall see.
As previously, we begin building a LabVIEW application by creating a blank virtual instrument (VI) using, for example, the Blank VI option on LabVIEW’s Getting Started splash screen, and go to the block diagram, which is where our routine will be loaded. Once again, we build our example around the NAG routine g01aa, which performs simple statistical calculations on a set of ungrouped data; besides being implemented as a method in the .NET Library, it is also available in both the Fortran and the C libraries.
To load the routine, we first bring up the Functions Palette, using the option on the View menu on the block diagram window. Next, we open the Connectivity collection from the palette, and then the Libraries & Executables item. This palette contains a variety of functions for calling code from libraries and executing system commands, and we select the Call Library Function item – which calls a DLL directly – and drag it onto the block diagram in order to initialize it. To specify the library we want to call, we right-click on the node and select Configure.... This brings up the Call Library Function dialog box, and we enter the name of the DLL in the Library name or path box. In general, either the name or the path is acceptable, although the choice between them has consequences if the VI is to be distributed or relocated. In our example, we're running LabVIEW under Windows, and are using the implementation of the NAG Fortran Library contained in FLDLL254M_nag.dll and the NAG C Library implementation in CLW3225DA_nag.dll .
Having specified the library, we next select the name of the routine we wish to call by picking it from the drop-down list in the Function name dialog box; in our example, this is G01AAF if we're using the NAG Fortran Library, or g01aac if it's the NAG C Library that we're calling. In either case, we specify the calling convention as stdcall (WINAPI).
The next step is to tell LabVIEW about the argument list for our routine via the Parameters tab on the Call Library Function dialog box. For each parameter, we specify its name, type and how it is to be passed (as a value or a pointer to a value) to LabVIEW when it calls the routine. As these details are entered, LabVIEW updates the Function prototype display at the bottom of the dialog box with the C prototype of the routine (LabVIEW is written in C, and it is from this language that the external routine is called). This table shows the parameters for G01AAF, including their Fortran type, their LabVIEW specification and the C type that LabVIEW generates for the function prototype:
Fortran variable name | NAG Fortran type | LabVIEW Type | LabVIEW Constant? | LabVIEW Data type | LabVIEW Pass | LabVIEW Array Format | LabVIEW C type |
N | INTEGER | Numeric | Y | Signed 32-bit Integer | Pointer to Value | const int32_t* | |
X | double precision array | Array | Y | 8-byte Double | Array Data Pointer | const double* | |
IWT | INTEGER | Numeric | N | Signed 32-bit Integer | Pointer to Value | int32_t* | |
WT | double precision array | Array | N | 8-byte Double | Array Data Pointer | double* | |
XMEAN | double precision | Numeric | N | 8-byte Double | Pointer to Value | double* | |
S2 | double precision | Numeric | N | 8-byte Double | Pointer to Value | double* | |
S3 | double precision | Numeric | N | 8-byte Double | Pointer to Value | double* | |
S4 | double precision | Numeric | N | 8-byte Double | Pointer to Value | double* | |
XMIN | double precision | Numeric | N | 8-byte Double | Pointer to Value | double* | |
XMAX | double precision | Numeric | N | 8-byte Double | Pointer to Value | double* | |
WTSUM | double precision | Numeric | N | 8-byte Double | Pointer to Value | double* | |
IFAIL | INTEGER | Numeric | N | Signed 32-bit Integer | Pointer to Value | int32_t* |
Although there are several parameters, many of them have the same characteristics. Firstly we recall that, since Fortran passes arguments by reference (not by value), each parameter must be passed as a pointer when calling the Fortran routine from LabVIEW (i.e. from C). Next, the documentation for this implementation of the NAG Fortran Library tells us that double precision means DOUBLE PRECISION - that is, an 8-byte floating point number, whilst the Fortran INTEGER type is a 32-bit integer. Hence, these two types can be respectively mapped to doubleand int32_t, via LabVIEW's 8-byte Double and Signed 32-bit Integer types. Finally, it can be seen how designating a variable as a constant (because it's only used on input to the routine) results in LabVIEW applying the const qualifier to the C type.
Similar remarks apply, for the most part, to the use of the g01aac routine (note that the parameter list is slightly different for this version of the routine):
Fortran variable name | NAG Fortran type | LabVIEW Type | LabVIEW Constant? | LabVIEW Data type | LabVIEW Pass | LabVIEW Array Format | LabVIEW C type |
n | Integer | Numeric | Y | Signed 32-bit Integer | Value | const int32_t* | |
x | const double array | Array | Y | 8-byte Double | Array Data Pointer | const double* | |
wt | const double array | Array | Y | 8-byte Double | Array Data Pointer | const double* | |
nvalid | Integer* | Numeric | N | Signed 32-bit Integer | Pointer to Value | int32_t* | |
xmean | double* | Numeric | N | 8-byte Double | Pointer to Value | double* | |
xsd | double* | Numeric | N | 8-byte Double | Pointer to Value | double* | |
xskew | double* | Numeric | N | 8-byte Double | Pointer to Value | double* | |
xkurt | double* | Numeric | N | 8-byte Double | Pointer to Value | double* | |
xmin | double* | Numeric | N | 8-byte Double | Pointer to Value | double* | |
xmax | double* | Numeric | N | 8-byte Double | Pointer to Value | double* | |
wsum | double* | Numeric | N | 8-byte Double | Pointer to Value | double* | |
fail | NagError* | Numeric | N | Signed 32-bit Integer | Value | int32_t* |
Since we're now calling a C routine from C, there is a closer correspondence between the NAG type and the LabVIEW C type, although a couple of additional explanatory remarks might be necessary. Firstly, the NAG C Library uses a data type called Integer; for this implementation, this is defined as long, which is a 32-bit integer on Windows systems. Secondly, the fail variable is a pointer to a C structure named NagError; we access this using a 32-bit integer (not a pointer to an integer), which is the same size as the address of the structure.
More information about how to create an interface between LabVIEW and external libraries (including advice for more complicated cases than those used here) is available here. Having created the interface to the NAG routine, the final step is to add LabVIEW controls and indicators to handle the input and output of our application. This is exactly the same procedure as was described previously, and we conclude this post with Figure 1, which shows the block diagram of our application, centered around the Call Library Function, which is where the NAG routine (in this case, g01aac) is being called.
Figure 1. The g01aac routine in the LabVIEW block diagram