TOC PREV NEXT INDEX

Getting Started with AVS/Express Developer Edition




9 Integrating User Code


In this chapter, you create a component that performs its work by calling a C-routine.

In this chapter you:

9.1 Introduction

You can integrate C, Fortran, or C++ code into an AVS/Express application in essentially two ways:

In this chapter, you create an AVS/Express module that calls a C routine. The module, part of the Echo Sounding application, reads echo-sounding data from a file.

AVS/Express offers several techniques for integrating C code into a module. In this chapter, you use the Add_Module tool to add the method and parameters of the module.

9.2 Start AVS/Express

If you are no longer in AVS/Express, start it now with the myproj project.

1. Open a command shell or an xterm window.
2. Navigate to the myproj directory.
Depending on where you saved myproj, you may need to specify another pathname
3. Start AVS/Express by typing the platform-specific command listed below.
Visualization Edition users type:
Table I-1
UNIX
Windows
vxp
bin\pc\vxp

Developer Edition users type:
Table I-2
UNIX
Windows
express
bin\pc\express

AVS/Express begins and loads your project.

If you are still in AVS/Express from the last chapter, delete the current application workspace and load a new one.

1. Select File->Delete Application to delete the current application workspace.
2. Select File->New Application to load a new application workspace.
3. Select Application from the New Application dialog.
4. Click OK.
Note: Do You Have A C++ Compiler?  
If you do not have a C++ compiler, you can still compile the sample module in this chapter. (You will be executing it in an external process called user, which you can build using a C compiler.) But you must start AVS/Express with the -nocxx command-line option. On UNIX systems: express -project myproj -nocxx

If you need to do this and you are still in AVS/Express from the last chapter, exit and restart AVS/Express, this time specifying the -nocxx option.
9.3 Create EchoReader

Integrating user code

Create EchoReader
Save EchoReader as a template
Generate C-code
Compile EchoReader's process
Test EchoReader

You define EchoReader as a module, similar to Echo. In this case, though, EchoReader will perform its processing by calling a C routine- in this case a C-routine that we provide for you, echo.c. However, EchoReader will be able to read any C-routine you instruct it to.

EchoReader also requires one input parameter, the name of the data file. In this tutorial we also provide that file. It is called echo.dat. Both echo.dat and echo.c are found in <install_dir>\getstart\.

The echo.dat data file looks like this:

# header lines, each beginning with '#'
#

60
3828
0.0133 -71.1667 42.8333
0.0133 -71.1467 42.8333
0.0133 -71.1267 42.8333
...

Accordingly, EchoReader requires four output parameters: DistanceSR, the number of data points, time, and location.

You begin by creating EchoReader in the Network Editor.

Starting the Add_Module Tool
1. Create a ScratchPad to work in.
Select the File->New Application command, select ScratchPad from the New Application dialog, and click OK.
2. Select Object->Add_Module. The first page of the Add_Module Wizard appears.


3. Click on the "Object name" field and replace NewModule with EchoReader.
You now have a module named EchoReader. The next step is to add the method.
Adding the Method
1. Click on the "Type field" and select C function (o method) to replace the default C++ member function (cxx method) because in this case we will be adding C-code. The o-method refers to C-code.
2. Click on the Add button.
This puts you into the screen where you can add a method object, identified by the lightning-bolt icon. You use a method object to associate EchoReader with a C routine.
3. Once on the add_method page (pictured below) click on the Object name field and type "update". This will name the method you are creating.
Figure I-1


Notice that the method object appears and alters in the network editor as the Add_Module tool does its work.
4. Specify properties for the update method. Under Method Type select Method runs when object is instanced.
5. The Weight - which determines the priority assigned to this method - should be set to one.
6. In the C-function name field enter echo_reader. This will be the name of the function update calls.
7. Hit Done. You will then return to the first page of the Add_Module Tool.
8. Hit Next.
Creating and Setting the First Parameter



1. Select the parameter - Select the string menu item in the Type field
2. Click the Add button.
The Add_Module Tool then displays an editor page for the parameter.


3. Rename the parameter - In the Object name field, change New Parameter to filename.
4. Expose an input port - Set the toggles Object is an exported parameter and Display input port on.
Note: These toggles are independent of one another. The toggle Object is an exported parameter determines whether the parameter is visible or not when the module is opened in Display Params mode, and the Display input port makes the parameter's port available at the module interface. You can set either one without setting the other.
5. Click on Next - this moves you to the second part of the adding parameter process.



You use this page to assign attributes that define the interaction between the selected parameter and the method you select in the upper panel. In the current example, update is the only method and so is automatically selected.

6. Set the parameter's method attributes - For filename, click the toggles for notify, read, and req to set these attributes for filename.
Attributes are boolean characteristics for an object. The notify attribute says that the update method should be notified when the parameter's value changes. Typically, a notification results in the method's execution. The read attribute indicates that this is an input parameter. The req attribute indicates that this parameter is required, meaning that the update method should execute only when the parameter has a value. More specifically, it means that the Object Manager will generate an OMget.xxx call rather than an OMset.xxx call.
7. Click on Done. This will return you to the add_parameter page and allow you to either move on by clicking Next> or add more parameters. In this case we will add more parameters
Creating and Setting the Other Parameters
1. Specify the output parameter for distance
2. Specify the output parameter for the number of data points
3. Specify the output parameter for time values
Figure I-2


Notice that the title bar for the time subobject in both the Add_Module Tool and the Network Editor displays a pair of brackets after the object name, indicating that the object is an array.
4. Specify the output parameter for location values
Figure I-3


Press Enter to assign the dimensions. Notice that the title bar of the location subobject displays two sets of brackets, indicating a two-dimensional array.
Like the time parameter, the location parameter needs to be an array large enough to hold num_values elements. But the location array also requires an additional dimension to store the longitude and latitude for each data point.
5. You have now added all the parameters and the method to your new module EchoReader.
Click on the Next> button to move to step three of the Add Module Process.
Set Properties Related to Integrating a C Routine

For a module that calls a C routine, you need to supply certain code-related information. For EchoReader you set properties and attributes on the module and its parameters to indicate:

You provide this information to AVS/Express through a set of code management properties. You can set these properties using step three of the Add Module Process.

Note: You could have set the code management properties, the attributes, and the parameter and method properties in the Object Editor. In the Add_Module Tool we set them all at once so that you know you haven't missed anything.

For EchoReader, we are going to set the process property to "user", the build_dir property to "echo", and the src_file property to "echo.c". This is all done in Step 3 of the Add_Module Process.

Figure I-4


1. Set the process to user - From the option menu Process in which object resides, select the user option.
The process property specifies the routine's process.
2. Enter the source file name - In the field Source Filename, enter echo.c.
This field specifies the name of the source file. It sets the src_file property for the module.
3. Set the build directory - In the Advanced Properties panel, set the build_dir field to echo.
The build_dir property specifies the source-file directory. You specify the directory relative to the project directory. The current project directory is displayed as the Directory for source above the Source filename field. The build directory you specify in build_dir is appended to this path.
This field sets the build_dir property for the module.
4. Click on Done. This will complete the module and exit you out of the Add-Module Tool.
Note: VERY IMPORTANT: The file called "echo.c" does not yet exist in your source directory. To have this example work you will need to copy echo.c from <install_dir>\getstart\ to <install_dir>\myproj\echo\. The example will NOT work unless you do this. However, before you do that, proceed through the next two sections. The instructions on how to set the echo.c file can be found in the section Modify echo.c on page 9-19
9.4 Save EchoReader as a Template

Integrating user code

Create EchoReader
` Save EchoReader as a template
Generate C-code
Compile EchoReader's process
Test EchoReader

In this section, you save EchoReader as a template and save your project.

1. Close EchoReader.
2. Go to the Library Workspaces page.
3. Drag EchoReader from the ScratchPad workspace into Workspace 1.
Figure I-1


4. Save your project.
Select Project->Save.
5. Delete ScratchPad by selecting File->Delete Application.
Deleting ScratchPad reveals your previous application workspace, though it is currently in the closed state within Applications.



9.5 Generate C-code

Integrating user code

Create EchoReader
Save EchoReader as a template
` Generate C-code
Compile EchoReader's process
Test EchoReader

You can write the C code for EchoReader from scratch. Or you can allow AVS/Express to generate a skeleton version of the code, which you can then modify. In this section, you use the generate-and-modify technique.

Generate the Code

You can edit a module's source file from AVS/Express. If you request to do so but the source file does not yet exist, AVS/Express generates a skeleton version of the source file, which you can then modify.

1. Select EchoReader in the Workspace 1 library. DO NOT instance it- just click on it.
AVS/Express highlights EchoReader in blue.
2. Select Project->Edit Source.
AVS/Express attempts to open an editor for EchoReader's source file. AVS/Express looks in the project directory for a directory called echo and a source file called echo.c. This is what you specified for EchoReader. But echo and echo.c do not exist, so AVS/Express, after prompting you with a pop-up dialog, creates them and a skeleton version of the code. AVS/Express then opens echo.c in an editor. (On UNIX systems, it is the editor specified by the EDITOR environment variable. In Windows it will be your default editor- usually Visual Studio.)
3. In the OM compile dialog box, Click Yes.

Here is the top portion of echo.c:

#include "user.h"


int
echo_reader(OMobj_id EchoReader_id, OMevent_mask event_mask,
int seq_num)
{
/***********************/
/* Declare variables */
/***********************/
char *filename = NULL;
double distance_sr;
int num_values;
int time_size;
float *time;
int location_size;
float *location;

/***********************/
/* Get input values */
/***********************/
/* Get filename's value */
if (OMget_name_str_val(EchoReader_id, OMstr_to_name("filename"),
&filename, 0) != 1)
...

If you scroll through the file, you see that it contains several Object Manager API calls, such as OMget_name_str_val and OMset_name_real_val. Calls such as these get the module's input parameter, set the module's scalar output parameters, and get pointers to the module's array parameters. (You will soon see a version of the routine with more detailed comments.)

Edit EchoReader's Definition

Either now or when you compile the process later, you may find that something is incorrect with the way you defined EchoReader. For example, if you forget to set the update object to the name of the target routine, AVS/Express will not generate the routine for you.

If for any reason you need to edit EchoReader's definition, do the following. You may want to practice these steps now, even if EchoReader's definition appears correct.

1. Exit from the editor.
2. Load a ScratchPad application workspace by selecting File->New Application and selecting ScratchPad from the New Application dialog.
3. Instance EchoReader from Workspace 1 into the ScratchPad workspace.
4. Make any required changes to EchoReader.
You can skip this step if you are just practicing.
5. Drag EchoReader back into Workspace 1.
6. Save your project.
7. Delete ScratchPad by selecting File->Delete Application.
8. Maximize the DefaultApplication workspace by double-clicking on it or selecting the Maximize popup command.
Modify echo.c

You now modify the generated code, primarily by adding code to read a data file and populate the output parameters.

To save you time, a modified version of echo.c is available in the express directory. You can copy that version, rather than making the changes manually.

If you had attemped to run the previous version of EchoReader after compiling, quit express. Delete the file, lib\pc\echo\echo.obj, and restart express.

1. If you are still in the editor that contains the generated version of echo.c, exit from it.
2. Copy echo.c from the express directory to the echo directory in myproj.
cp install_dir/getstart/echo.c ~/myproj/echo/
copy install_dir\getstart\echo.c path\myproj\echo\

install_dir is the directory in which you installed AVS/Express.

Look at echo.c

Here is the modified code for echo.c, including detailed comments on how it works:

#include "user.h"
#include <stdio.h>


/***************************************************************/
/* echo_reader: reads a data file into the echo_reader module. */
/* */
/* Most of the code you see here was generated by AVS/Express. */
/* This includes the function declaration, the OM calls */
/* to access the module's data parameters, and the variable */
/* definitions related to those calls. */
/* */
/* You add routine-specific code, in this case, code to open a */
/* data file, read its values, and populate the time and */
/* location arrays. */
/***************************************************************/

/***************************************************************/
/* AVS/Express generates the function declaration you */
/* see below. The function's first input parameter is the key */
/* one for the purposes of most routines. AVS/Express assigns */
/* a unique object ID to each object in the object hierarchy. */
/* The first input parameter contains the object ID of the */
/* calling module. From this ID, you can access the module's */
/* data parameters. The other two function parameters provide */
/* additional information, which you do not need for this */
/* module. */
/***************************************************************/
int
echo_reader(OMobj_id echo_reader_id,
OMevent_mask event_mask, int seq_num)
{

/***********************/
/* Declare variables */
/***********************/
char *filename = NULL;
double distance_sr;
int num_values;
int time_size;
float *time;
int location_size;
float *location;

int i;
char Buff[512];
FILE *fp;


/************************************************************/
/* A routine typically begins by getting the module's input */
/* data parameters. In this case, it gets the value of */
/* parameter filename. */
/* */
/* Most API calls have the prefix OM, for Object Manager. */
/* The call to OMget_name_str_val, generated for you by */
/* AVS/Express, looks in the module identified by */
/* echo_reader_id for a parameter named "filename", and */
/* places the value of that parameter in the variable */
/* filename. */
/************************************************************/
if (OMget_name_str_val(echo_reader_id,
OMstr_to_name("filename"),&filename,0)
!= 1)
filename = NULL;

/***********************/
/* Function's Body */
/***********************/
/* Code you supply to open the data file. */
fp = fopen(filename, "r");
if (fp == NULL) {
fprintf(stderr, "cannot open file %s\n", filename);
return(0);
}

/* Code you supply to strip off header (lines beginning with #) */
/*(assumes there is a blank line after last header line) */
while (fgets(Buff, sizeof(Buff), fp))
if (Buff[0] != '#') break;

/* Code you supply to read distance SR and the number of */
/* data points. These are the first two values in the */
/* input file. */
fscanf(fp, "%lf", &distance_sr);
fscanf(fp, "%d", &num_values);


/***********************************************************/
/* AVS/Express also generates OM calls to set the value of */
/* the module's scalar output parameters. For example, */
/* the first call to OMset_name_real_val looks in the */
/* module identified by echo_reader_id for a parameter */
/* named "distance_sr" and places into that parameter the */
/* value of distance_sr. */
/***********************************************************/
/* Set distance_sr's value */
OMset_name_real_val(echo_reader_id,
OMstr_to_name("distance_sr"), distance_sr);

/* Set num_values's value */
OMset_name_int_val(echo_reader_id,
OMstr_to_name("num_values"), num_values);


/************************************************************/
/* For array parameters, AVS/Expresss generates OM calls to */
/* return a pointer to the array. For example, the first */
/* call to OMret_name_array_ptr looks in the module */
/* identified by echo-reader_id for a subobject named */
/* "time" and returns a pointer to the array. The argument */
/* OM_GET_ARRAY_WR tells the Object Manager that you intend */
/* to write to the array. After the call, the fourth */
/* argument, time_size, contains the number of elements in */
/* the array. */
/************************************************************/
/* Get a pointer to the time array */
time = (float *)OMret_name_array_ptr(echo_reader_id,
OMstr_to_name("time"), OM_GET_ARRAY_WR, &time_size, NULL);


/* Get a pointer to the location array */
location = (float *)OMret_name_array_ptr(echo_reader_id,
OMstr_to_name("location"), OM_GET_ARRAY_WR, &location_size, NULL);

/* Code you supply to read the data into the arrays */
for (i=0; i<num_values; i++)
fscanf(fp, "%f %f %f", time+i, location+2*i, location+2*i+1);


/************************************************************/
/* The Object Manager keeps track of array references. A */
/* call to OMret_name_array_ptr tells the Object Manager */
/* that the routine needs to reference the array. A */
/* subsequent call to ARRfree, generated by AVS/Express, */
/* tells the Object Manager that the routine is finished */
/* with the array. */
/************************************************************/
if (filename) free(filename);
if (time != NULL) ARRfree((char *)time);
if (location != NULL) ARRfree((char *)location);

return(1);
}
9.6 Compile the Process

Integrating user code

Create EchoReader
Save EchoReader as a template
Generate C-code
` Compile EchoReader's process
Test EchoReader

Workspace 1, the library where you placed EchoReader, is set up so that modules compiled in it belong by default to the express process. But you cannot compile the express process when you are in AVS/Express because the express process is running. So if you want to compile EchoReader within AVS/Express, you need to specify that EchoReader belongs to an external process. AVS/Express provides one, called user.

In a previous step, you set EchoReader's process property to "user", thereby overriding the default for the library. You are now ready to compile the user process.

1. In the Workspace 1 library, select EchoReader.
AVS/Express highlights EchoReader in blue.
Selecting a template object indicates the process you wish to compile. AVS/Express compiles the process specified by the process property for the selected object. You could have selected any object that belongs to the user process.
2. Select Project->Compile.
AVS/Express compiles the user process. This includes generating a makefile, compiling the code and linking the process. A window appears that displays informational messages and any error messages that may be generated.
At the end of a successful compilation, the following message appears:
Hit any key to continue...
3. If the process compiles without error messages, remove the window by pointing to it and pressing Return or any other key.
If you have errors, edit echo.c and compile the process again.
9.7 Test EchoReader

Integrating user code

Create EchoReader
Save EchoReader as a template
Generate C-code
Compile EchoReader's process
` Test EchoReader

In Chapter 11, Creating and Compiling an Application, you will use EchoReader along with Echo in an actual application, one that includes a user interface and a data viewer.

For now, simply test EchoReader on its own. You can use as input a sample data file located in the express installation directory.

1. Instance EchoReader into the DefaultApplication workspace.



2. Open EchoReader, widen it, and open filename.



3. Enter the name of the data file.
The file is called echo.dat and is located in install_dir/getstart.
For example, if the installation directory is /home/express, you would enter the following. Remember to enclose the string in quotes. In Windows you need to either use foward slashes, "c:/Express/getstart/echo.dat" or double back slashes "c:\\Express\\getstart\\echo.dat"



4. Close filename.
Closing the object applies the assignment.
EchoReader's update method is notified that filename has changed, so it executes. The routine reads the data file and places the data into the output parameters.
The title bars of these parameters indicate that they now have values:



5. Open time.
A scrollable text window appears containing time's values.



6. Close time and open location.
A scrollable text window appears containing location's values.



This completes the tutorial.

n Either stay in AVS/Express and continue to the next tutorial, or exit AVS/Express by selecting File->Exit.
9.8 For More Information

TOC PREV NEXT INDEX