Chapter 1. Building a Module

This chapter takes you step by step through the process of creating a simple module in the Module Builder. The proposition is to build a module that acts as a channel selector. This module will take in an array containing a vector of values per sample, and output an array with a scalar at each sample. The user manipulates a widget to select which scalar, or channel, is output.

The example shows how to build a module with a user function written either in C or in Fortran. The process is identical for both, but some menus differ, depending on the language used. These differences are called out clearly in the text.

The information on building a module spans two chapters:

If you want more information than is given in Chapter 1 about a particular step, refer to the parallel section in Chapter 2.

Introduction to Module building

The IRIS Explorer product uses modules to carry out operations on data in order to visualize the data in a way that has meaning to the observer. The modules supplied with Explorer offer a range of functions, but many users will want to construct their own modules, providing a more specific function or greater power than already exists.

The Module Builder is a tool that lets you build your own Explorer modules, either by modifying and renaming existing Explorer modules or by creating new ones. The great virtue of the Module Builder is its graphical user interface, which lets you build a basic module with no programming beyond that needed to write the computational function.

The module-building process has three main stages:

  • Defining the internal structure, or “engine”:

    • Creating a user function or subroutine

    • Defining the input and output ports

    • Defining the function arguments and their calling sequence

    • Defining the relationships between the inputs and outputs and the function arguments

  • Defining the external structure, or user interface:

    • Laying out a module control panel

    • Associating input parameters with widgets, or control mechanisms

    • Creating menu bar items

  • Building and installing the module in Explorer:

    • Setting environment variables to define the build directory

    • Selecting build options

    • Writing hook functions (if required)

    • Running the build command

Once the module is built, you can wire it into a map in the Map Editor and use it like any Explorer module.

Creating the User Function

The user function is the computational function or subroutine that acts on the data fed into the module through its input ports and produces new or changed data on the output ports. It provides the core functionality of the module.

Designing the Example Module

You are designing a module that will select one data variable, or channel, from an array that has a number of data variables. Arrays in Explorer are called lattices. The module is a simple selector with no limits on the range, and uses only float data. Here is a brief description of the structure you need in the new module, which you will call ChannelSelector.

It should take in all the data variables from the input lattice and put out values for only one data variable. It should have a widget for selecting a particular data variable. It needs an input port, an output port, some defined variables for the lattice data and coordinate values, and a computational function to select out the data values for a specific channel.

Writing the User Function

The example shows how you can create a channel selector module with a C function or a Fortran subroutine. The module accepts input data in the form of an Explorer lattice. You need not be concerned with lattices now, except to note that as a matter of convention, the variable names in this function correspond closely with the variable names in the cxLattice data type structure.

To find out more about lattices, read Chapter 3, “Using the Lattice Data Type.”

Example Code and Modules

Working example code for this module can be found in /usr/explorer/src/MWGcode/Tutorial/C/channel.c and /usr/explorer/src/MWGcode/Tutorial/Fortran/channelselect.f. There are also complete module resources built from this code, called ChannelC (for C) and ChannelF (for Fortran) in the same directories. You can open the respective .mres files to see how yours should look.

The C User Function

The function called channel in the file channel.c serves as the user function in this module. It is written in C. The code shows the source of the function arguments used later in this chapter.

   long sizes(long,long *);

   channel(long nDim, long *dims, long *nDataVar, long which,
   float *dataIn, float *dataOut)
{
   int i,numElems,sel;
      numElems = sizes(nDim,dims);

/* use a legal range for the data */

    sel = which;
    if(sel < 0){
    sel = 0;
    else if(sel >= (*nDataVar)){
    sel = (*nDataVar)-1;
 }

/* copy the data */

  for(i=0;i<numElems;i++){
  dataOut[i] = dataIn[i*(*nDataVar)+sel];
 }
 *nDataVar = 1;
}

   long sizes(long nDim,long *dims)
{
  long foo,i;
  foo= 1;
  for(i=0;i<nDims;i++)
  foo *= dims[i];
    return(foo);
}

The Fortran User Function

The Fortran subroutine called channel in the file channelSelect.f is the user function in this module. This code fragment shows the source of the function arguments used later in this chapter.

c
  subroutine channel(ndim,dims,nvars,which,datain,dataout)
  implicit none
  integer nvars,ndims
  real    datain(nvars,1),dataout(1)
  integer dims(ndims),which
  integer numElems,i,j,sizes
c
   numElems = sizes(ndims,dims)
c
   j = min(max(1,which),nvars)
c
   do i=1,numElems
      dataout(i) = datain(j,i)
   end do
c
   nvars = 1
   return
   end
c
c
    integer function sizes(ndims,dims)
    implicit none
    integer ndims,dims(ndims),i
    sizes = 1
    do i=1,ndims
       sizes = sizes * dims(i)
    end do
    return
    end

Since cxLattice is a C data structure, the Fortran code contains pointers to the cxLattice data type (see Chapter 3, “Using the Lattice Data Type.”)

Setting up your Build Environment

Before you start to build this module, you need to set up a test directory and copy the example code into it.

  1. Make a subdirectory in your home directory as follows:

    cd ~
    mkdir testMod
    

  2. Change to the new subdirectory and copy the example code from /usr/explorer/src/MWGcode/Tutorial/* into testMod:

    cd testMod
    cp /usr/explorer/src/MWGcode/Tutorial/C/channel.c .(C)
    cp /usr/explorer/src/MWGcode/Tutorial/Fortran/channelSelect.f . (Fortran) 
    

You can now build your module using the Module Builder and run it in the Map Editor. Just follow the steps in the next sections.

C or Fortran?

The method for building modules is the same for both languages, although some of the information you enter varies according to whether the user function is written in C or Fortran. Because the similarities are so great, this tutorial presents the process for users of both languages together. Where the activities for the two language diverge, the differences are clearly marked and parallel instructions are given for each language.

Invoking the Module Builder

Make sure you are in the testMod directory, then start up the Module Builder:

mbuilder

The Module Builder main window appears (see Figure 1-1 and Figure 1-2). Click in the Module Name slot to select it and delete the default, then type in the module name ChannelSelector. In the next slot, enter the name of the file that contains the user function:

C Users:  

channel.c

Fortran Users:  

channelSelect.f

You can also specify include files, additional libraries or your own Makefiles, but none are required for building the ChannelSelector module. To learn more about these options, read through“The Include Files” and “User Makefiles” in Chapter 2.

Figure 1-1. The Module Builder Window for C Users


Figure 1-2. The Module Builder Window for Fortran Users


Saving the Module Resources

The Module Builder creates a number of files as you go through the construction process. The Module Builder automatically saves all the module characteristics, or resources, that you have just specified in the module resource (.mres) file before it builds the module. This is a binary file and cannot be accessed directly.

You can use “Save Resources” on the File menu to save your work at any time during the module construction phase. It is a good idea to save your work periodically if you expect to be interrupted or if you are building a very complex module.

Quitting the Module Builder

To quit the Module Builder at any time once it has been invoked, click on the File menu on the main window and select “Quit” (see Figure 1-3).

Figure 1-3. Module Builder File Menu


Defining the Internal Structure

The inputs, outputs, function arguments, and connections among different variables constitute the internal structure of the module, and you define this structure in the Module Builder windows. You can have only one window open at a time, other than the main window.

Creating an Input Port

Click on the first button, Input Ports, to bring up the Input Ports window (see Figure 1-4).


Note: The window is initially displayed with only one text slot. Click on the slot to highlight it and press <Enter> to produce a new text slot.

The module requires an input port that will accept data in the form of the lattice data type.

  1. Click in the first text slot to highlight it, then type the name “Input” in the first text slot to create an input port.

  2. Click on the button on the option menu in the middle column to choose the data type menu and select cxLattice.

    Figure 1-4. Input Ports Window


Defining the Data Type

When you select cxLattice from the data type option menu, the Lattice Constraints window appears (see Figure 1-5). To define the characteristics of the lattices the input port will accept:

  1. Click off the yellow buttons to get the results shown in Figure 1-5

    The input port called “Input” will now accept a lattice of any dimension, containing any number of data variables and with any number of coordinates per node. The primitive data type must be a float.

  2. Data values are required, but coordinate values need not be given, so Data Structure is marked “Requiredand Coord Structure is “Optional.”

  3. Click OK to save the setting and close the window.

    Figure 1-5. The Lattice Constraints Window


Defining the Parameters

The next step is to define a parameter for the input port in the Input Ports window (see Figure 1-4).

  1. Click the second slot in the Input Ports window and enter the parameter name “Which.”

  2. Select cxParameter from the data type option menu.

  3. Select Required from the option buttons in the third column for both ports.

  4. Click OK to save the setting and close the window.

The module must receive data on both its input ports before it will fire, so each port is made a “Required” port.

Creating an Output Port

Click the Output Ports button to bring up the Output Ports window (see Figure 1-6). The module needs an output port so that it can pass the data from the selected channel downstream to the next module.

Figure 1-6. The Output Ports Window


To create the output port:

  1. Enter the name of the output port, “Output,” in the text slot.

  2. Select cxLattice from the data type option menu.

    When the Lattice Constraints window appears (see Figure 1-7), set the constraints. All the constraints, except the Num Data Variables, are the same as those for the input lattice, however, the Num Data Variables is set to 1 in the output lattice because only one data channel is being output.

    Figure 1-7. Lattice Constraints for the Output Port


  3. Click OK to save the constraints setting and close the window.

  4. Click OK to save the output port setting and close the Output Ports window.

For more information about input and output ports, read “Creating Ports” in Chapter 2.

Defining the Function Arguments

The Function Arguments window defines each function argument in the user function. Each function argument must be connected to an input or output item. You list the function arguments in their calling sequence, as shown in Figure 1-8 and Figure 1-9.

To define the function arguments:

  1. Click the Function Args button to display the Function Arguments window. When the window first appears, all of its fields are blank.

  2. Enter the name of the function, channel, in the Func Name slot. This is the name of the function contained in the channel.c or channelSelect.f file.

  3. Set the language option.

    If you are using the C function, select “C” from the language option menu.

    If you are using the Fortran subroutine, select “Fortran” from the language option menu.

  4. Enter the first function argument for the user function in the text slot. Press <Enter> after typing each function argument name to generate a new text slot.

    The function arguments for the C function are shown in Table 1-1 and Figure 1-8.

    The function arguments for the Fortran subroutine are shown in Table 1-2 and Figure 1-9.

  5. Set the corresponding values for the argument type and reference by clicking each option menu button in turn. The C values differ from the Fortran values, as you will see from the function argument tables.

  6. When you have entered all of the arguments, click Apply to have them accepted but keep the window open.

  7. At the bottom of the window, you will see the function name displayed with all its arguments listed. Click OK to close the window and return to the Module Builder main window.

For more information about the data types and reference mechanisms, read “Defining Function Arguments” in Chapter 2.

These are the function arguments for the C function

Table 1-1. Function Arguments for the C Function

Argument Name

Type

References

nDim

int

Scalar

dims

int

Array

nDataVar

int

&Scalar

which

int

Scalar

dataIn

float

Array

dataOut

float

Array

These are the function arguments for the Fortran subroutine.

Table 1-2. Function Arguments for the Fortran Subroutine

Argument Name

Type

References

nDim

integer

Scalar

dims

integer

Array

nDataVar

integer

Scalar

which

integer

Scalar

dataIn

real

Array

dataOut

real

Array

Figure 1-8 shows the argument names, data types, and references you enter if you are building your module around the C user function.

Figure 1-8. The Function Args Window for C Users


Figure 1-9 shows the argument names, data types, and references you enter if you are building your module around the Fortran user function.

Figure 1-9. The Function Args Window for Fortran Users


Connecting Ports and Function Arguments

Each port must be connected with one or more function arguments, so that the incoming data can be routed to the correct function argument and the outgoing data can be directed to the correct output port.

The connections are identical for C and Fortran user functions.

  1. Click on the Connections button to invoke the Connections window (see Figure 1-10). When it is first displayed, no connecting wires exist. These appear as you wire ports and function arguments together.

  2. To connect an input port to a function argument or output port, click with the right mouse button on the input port name bar. A popup menu appears, listing the data structures associated with the port.

    For example, the input port “Input” accepts lattice data, and its popup menu (see Figure 1-11) lists the lattice substructures. They contain data arriving on the module's input port from upstream. The connections you make from the data structures on the input ports to the function arguments determine which data are recognized and processed, and which data are ignored.

  3. Select the structure member you want, for example, Dimensions Array. The function arguments and ports that can accept a connection are highlighted in green (output ports are darker green).

    Figure 1-10. The Connections Window


  4. Then click on a function argument, for example, dims, with the right mouse button. A small Select button appears (see Figure 1-10).

    When you click Select, a blue wire links the input port with the function argument.

  5. Follow the same procedure to link input ports or function arguments to the output port. The output port menu items are printed in blue if they require a connection for the integrity of the data structure (see “Connecting Arguments to Ports” in Chapter2).

To break a connection, simply click on both ends of the connection, as in the Map Editor.

The popup menu in Figure 1-11 shows one connection from the input port (top-level Lattice Structure) to the output port.

Figure 1-11. Input Port Popup Menu


It also shows four connections from lattice components, such as the number of dimensions (Num Dimensions), to function arguments. The Output port has three connections passing into it, although the port menu is not shown. The details of the data type structures are explained in Chapters 3 through 7.

Making the Right Connections

The correct connections are critical to the proper operation of the module. In this example, the values affected by the function arguments are passed separately to the lattice structure on the output port. The values for all members of the lattice data type not explicitly linked to function arguments are passed directly from the input port to the output port as default values.

For more about connecting ports and function arguments, read “Connecting Arguments to Ports” in Chapter 2

To build the Channel module correctly, you must make these connections. They are the same for the C and the Fortran modules.

Input 

Lattice Structure to port Output: Lattice Structure

Num Dimensions to argument nDim

Dimensions Array to argument dims (switch off Copy by pressing the yellow button)

Num Data Variables to argument nDataVar

Data Array to argument dataIn

Which 

Value to argument which (see option menu on right)

<<Storage>> 

“Select” to argument dataOut, then set the storage value (see Figure 1-12 or Figure 1-13).

nDataVar 

“Select” to port Output: Num Data Variables

dataOut 

“Select” to port Output: Data Array

Using the Pseudoports

<<Storage>>, <<Constant Value>> and <<Extern>> are “pseudo-ports.” When you connect one to a function argument, you use the second item on the function argument popup menu (shown on the left) to define the value for the pseudo-port. In this example, you need to define the storage value. The value is different for C and Fortran modules.

To set the storage size for dataOut, click the menu item “Set storage size” and enter the text shown in Figure 1-12 and Figure 1-13 for the C and Fortran modules respectively. There is a default setting which you should delete first.

This text is expected by the code that the Module Builder generates when the module is built. In this example, a routine is called to compute the number of floating (C) or real (Fortran) values that must be allocated for the dataOut function argument.

Figure 1-12. Setting Storage Size for C Users


Figure 1-13. Setting Storage Size for Fortran Users


You can enter comments after the values if you wish, to remind you and explain to others why you chose this value.

When you have filled in all the popup windows and made all the connections you need, click OK to save the configuration and close the window.

Defining the User Interface

The module has an interface that allows it to be controlled by the user. This interface is the module control panel. See Chapter 4, “Editing Control Panels and Functions” in the IRIS Explorer User's Guide for more details on control panel design and widget use.

The module control panel holds widgets, such as dials and sliders, that let you control the values of input port parameters interactively.

Click the Control Panels button to invoke the Control Panel Editor. The Control Panel Editor and a prototype control panel appear (see Figure 1-14).

Figure 1-14. Control Panel Editor and Control Panel


All ports of type cxParameter appear in the “Parameters” list in the Control Panel Editor. In this example, “Which” is a parameter port (see Figure 1-14). It is characterized in the control panel as a slider by default because its values are floating or real.

To change the widget for “Which” to another kind, click the left mouse button on it to select it, then click the Type option menu under Widget Attributes. You can select any widget type that is not grayed out.

To change the widget's position in the control panel, click it to highlight it, then hold down the left mouse button and drag it into a new position.

To change the widget's size, use the type-in slots to enter a new set of dimensions, or drag on any of the eight black handles around the widget.

To set default values for the slider, double-click on the bottom text slot and type 1, then hit <Enter>. Likewise, type 5 in the top slot (see Figure 1-14). Because the module input port can accept up to five data variables, the total number of channels is 5. You can enter a current value in the text field above the slider.

Creating a Menu Item

You can create your own menu bar options for the module control panel. For this module, you will create a File menu with a “Quit” option.

  1. Select “Menu Bar” from the Edit menu on the Control Panel Editor. The Menu Bar Editor appears (see Figure 1-15).

  2. Type File in the first slot to name the menu.

    Figure 1-15. Menu Bar Editor


  3. Click the Menu button underneath the slot. The Menu Editor appears (see Figure 1-16).

  4. Type Quit in the first menu item slot to name the action.

  5. Click the option menu button next to the first slot and select the action “Quit” from the option menu.

  6. Click OK to apply the selection and close the window.

    You can close each open window individually, or you can select OK on the Control Panel Editor, which closes all the subsidiary windows and applies the changes you have made.

    Figure 1-16. Menu Editor


Testing the Menu Bar

You can click on the Control Panels button to bring up the prototype control panel again. You will see that it now has a File menu on its menu bar. Click File to see the “Quit” option.

This completes the construction of the module. The next stage is to build the module and install it in Explorer so that you can use it in a map.

Building the Module

You have a blueprint for a module that must now be turned into an executable program. The code is linked and compiled during the build process.

Selecting Build Options

The Module Builder Build menu provides lets you set defaults and build the module. To check the build options for the Channel module, select “Options” from the Build menu. The Build Options window appears (see Figure 1-17).

Figure 1-17. Build Options Menu


You want to accept all the current defaults. You want the Module Builder to:

  • write wrapper code, called the Module Data Wrapper, which interfaces between the user function and the rest of the module

  • create a default Module Control Wrapper (MCW) for managing other module functions

  • link the module to C because the user function is not written in C++

  • use the stipulated executable program file

  • install no related files when it installs the module

  • use the Map Editor as the text output window for the module

Click OK to accept the default options and close the window.

For more information about build options, read “Using the Build Menu,” in Chapter 2.

Running the Build Command

The Build command is on the Build menu (see Figure 1-18).

  1. Click on the Build menu and select “Build.” This command builds the module in the current directory, which is ~/testMod in this case.

    If you receive this message:

    compile, link or install MAY have failed, 
    

    check to see that you are running the Module Builder from your current directory.

    Figure 1-18. Build Menu Options


  2. Select “Quit” from the File menu to exit the Module Builder.

You can now run the Map Editor by typing explorer at the shell prompt, and launch the ChannelSelector module from the Module Librarian.

To see how it works, connect these modules: ReadImg to Channel to DisplayImg. Read in the image file /usr/explorer/data/image/flowers2.rgb and select each of the color channels, 1 to 3, in turn.