A "step by step" IRIS Explorer modules creation guide


The way modules building is explained here is not the one explained in the "standard" IRIS Explorer manuals. But we found easier the method explained here even if there is less "mouse interactions" and more typing.

An Explorer module has two main aspects:

User interface is defined via a independent program called mbuilder. This program allows also to create the Makefiles, man pages and documentation's related to a module.

This document is a "step by step" modules creation guide. It shows all the various tasks you should perform in order to build a module. First we will see how to build a very simple module with no parameters (ports). Then we will increase the complexity by adding input and output ports to the module. This will be done using the modules adapters developed at CERN.


Overview


The mbuilder phase

The mbuilder program is a standalone application which allows to construct the module resources file (the .mres file).

Start the mbuilder:

$ mbuilder modulename.mres

the program will display the following window:

Just click on "OK" . Now one window called Module Builder remains on the screen. Only the field Module name is not empty. It should contain the string modulename.

Module source code file name:

In the field User Func File enter the name of the file which will contain the code executed by the module. For example modulename.cpp. Now the module builder window should looks like:

Language and options:

In the Build pull down menu of the Module Builder window select the choice Options....

A panel, called Build Options, will pop up in which you should define:

The top of the Build Options panel should now looks like:

The other fields of the panel should not be changed. Then press  in this panel.

Now the only window on the screen is the Module Builder.

Function definition:

In the Module Builder window click on the button Function Args ...

The Functions Args panel will pop up.

In this panel we shall define the name of the function which will be executed each time the module will be fired: in the Func Name field you should enter the name of the function, for example moduleName.

We will work in C++ language so the Language should be set on C++.

The top of the Function Args panel should now looks like:

The other fields of the panel should not be changed. You can now press  in this panel.

Special functions:

In the standard module building method these functions are not define. In the method explained here two functions are mandatory: To define the special functions just select the Hook Funcs ... item in the Build menu of the Module Builder window:

This action will display the Hook Functions panel. All the fields in this panel are empty. You should enter the string initHook in the Init Hook Function field, and the string removeHook in the Remove Hook Function field. The Hook Functions panel should now looks like:

Now you can press  in this panel. The only window on the screen is now the Module Builder.

The mbuilder phase is now finished. The next step is the definition of the code executed in the module. Before exiting the Module Builder, you should save your work by selecting the Save resources item of the File menu in the Module Builder window:

When the saving is done, you can exit the Module Builder by selecting the Quit item in the File menu:


The code definition phase

Now you should enter your favorite text editor and type the following piece of C++ code in the file modulename.cpp.

#include <stdio.h>

extern "C" {
   void moduleName ( void );
   void initHook ( void );
   void removeHook ( void );
}

//-----------------------------------------------------------------------------
//
// This function is the kernel of the module.
//
void moduleName (void) {
   printf ("moduleName is called \n");
}

//-----------------------------------------------------------------------------
//
// Hook function for Explorer module actions.
// This function is executed once when the module start.
//
void initHook(void)
{
   printf ("initHook is called \n");
}

//-----------------------------------------------------------------------------
//
// Hook function for Explorer module actions.
// This function is executed when the module stop.
//
void removeHook(void)
{
   printf ("removeHook is called \n");
}

Module making phase

Once the code is entered, the module can be build. First the Makefile must be generated from the file modulename.mres. To do that it is enough to type:

$ cxmkmf

Then the module executable should be created. For that just type:

$ make


Module testing phase

The module is now ready to be tested. Remember that it is just, for the time being, a test module which does nothing because it doesn't have any input or output parameters. To test the module just type:

$ explorer

You should find your module in the Librarian on Localhost window. Just type mod in the Modules scroll list and you should see your module as follow:

To test the module, you should create a instance of it in the Map Editor window by "drag-and-droping" with the left mouse button the box  in the Map Editor window as shown in the following figure:

This module does nothing for the time being. But with the printf(s) we put in the modulename.cpp file we can check that the functions moduleName, initHook and removeHook are called and executed correctly.

We can check immediately that the initHook function has been called. This function is called when the module in put in the Map Editor. We can see that initHook has been called by looking in the Explorer Log window:

To check that the function moduleName is working, you should "fire" the module. This can be done using the pull down menu available when you click on the right mouse button on the module name in the module box. When the menu appears choose the item Fire Now. The Explorer Log window contains now:

The function removeHook is called when the module is destroyed. To destroy the module just select the Destroy item in the same pull down menu we used before to fire the module. You can also "drag and drop" the module box on the Destroy icon:

The Explorer Log window should now looks like (??) :

We have now seen the complete module creation chain: from the module builder phase to the module execution. We will now see how to add parameters to the module.

As we already said we will use the adapters build at CERN. With this method the calling sequence of the module "core function" (moduleName) remains unchanged (i.e. : void), whatever the number of module parameters is.


Module parameters

To add parameters to a module you should modified the resources file with the mbuilder program. In the field User Makefile you should add modulename.mk. This file contains all the extra options needed to build the module. In particular the file contains the necessary compile options needed to use the adapters include files.

The user makefile:

The Module Builder window should now looks like:

The modulename.mk file allows to customize the Makefile generated from the resources file. For that a set of environment variables is provided. These variables are used by the Makefile and by defaults are not defined. As we assumed we are working in C++, the variables we will use are the following:


 

Variable Meaning
DEFINES Definitions for cpp use in C++ compiles, and in make depend
C++DEFINES Passed to the C++ compiler
INCLUDES Include directory specification for cpp use in C++ and in make depend
C++INCLUDES Includes directory specification for cpp use in C++ compiles
LOCAL_LDFLAGS Loader flags to be passed when linking modules
LOCAL_LIBRARIES Library filenames relative to the local directory (or fully qualified). Used in linking modules and in calculating dependencies so that the module will re-link if any of these libraries changes.
SYS_LIBRARIES System libraries (given in the -L<dir> -l<lib> form) to be passed to the loader.
EXTRA_LIBRARIES User libraries (given in the -L<dir> -l<lib> form) to be passed to the loader.
For the time being, the only extra option we need to define, is the place where the adapters include files and library are. So the content of the modulename.mk file should be:
C++INCLUDES     = -I/afs/cern.ch/sw/lhcxx/specific/@sys/HEPEXPLORER/pro/include/ \
                  -I$(EXPLORERHOME)/include -I$(EXPLORERUSERHOME)/include
EXTRA_LIBRARIES = -L/afs/cern.ch/sw/lhcxx/specific/@sys/HEPEXPLORER/pro/lib/ \
                  -lHEAdaptors $(C++INCLUDES)

Adding input parameters:

In the Module Builder window click on the  button. This will display the Input Ports window. In this window you should define two parameters called ParInput and Dial. To do that just enter ParInput in the first blank input text field and press <CR> this will automatically open a new text field in which you should type Dial.

Then defined these parameters as Optional with the menus choices on the right of the Input Ports window.

The parameters type are not changed and are defined as cxParameter. The Input Ports window should now looks like:

Now you can click on the button  to validate the changes.

Defining the parameter's graphical user interface:

To define the module graphical user interface, you should enter the Control Panel Editor. For that you just click on the  button in the Module Builder window. This will display two windows:
On the left of the Control Panel Editor are listed all the parameters currently available in this module. In this example there is only two called ParInput and Dial.

To define the graphical user interface of one parameter just click on the parameter name you want to work on. All the current attributes values of the selected parameter will be displayed in the Control panel Editor and can be edited.

In this example ParInput will stay as a pure input parameter (input port) with no user interface. This parameter will be changed only via input values coming from the output ports of other modules.

On the other hand, Dial will be connected to a Dial widget. To do that it is enough to define the Type of the Widgets Attributes. In the Type menu choice select Dial. The preview of the module graphical user interface is now:

The appearance of the module graphical user interface can now be changed in a WYSIWYG way just using the mouse. You can change the size of the window, adjust the widget position in the window etc. ...

The limits in which varies the parameter and its initial value can be changed by clicking (once) in the corresponding text field and by typing the new value in place.

Some extra widget properties can be also defined by double clicking on the widget itself. The extra properties are displayed in the Control Panel Editor.

Having done all these manipulations you can get something like:

The corresponding Control Panel Editor window being:

Now you can click  in the Control Panel Editor window.

Adding output parameters:

In the Module Builder window click on the button. This will display the Output Ports window. In this window you should define the parameter ParOutput. Proceed like you did previously for the input parameters.

When you have finished the Output Ports window should looks like:

Now you can click on the button  to validate the changes.

To validate all the changes (for input and output parameters), you should save the resources and quit the module builder as we already explained before.

Then you can build a new version of the module.


Access to parameters in the module code

Until now we didn't used any special code, method or libraries to build the module: the methods explained were fairly standard. With this section we will escape from the "standard" approach. To access parameters in the module code we use the "adapters" developed at CERN.

The code in the file modulename.cpp should be changed. The new version is:

#include "HEAdaptors/HEInputParam.h"
#include "HEAdaptors/HEOutputParam.h"

extern "C" {
   void moduleName ( void );
   void initHook ( void );
   void removeHook ( void );
}

static HEInParamLong   *parinput;
static HEInParamLong   *dial;
static HEOutParamLong  *paroutput;

//-----------------------------------------------------------------------------
//
// This function is the kernel of the module.
//
void moduleName (void) {
   if (dial->lookUp() || parinput->lookUp()) {
      long ParInputValue;
      long DialValue;
      ParInputValue = parinput->getValue();
      DialValue     = dial->getValue();
      paroutput->setValue(ParInputValue*DialValue);
      paroutput->sendOut();
   }
}

//-----------------------------------------------------------------------------
//
// Hook function for Explorer module actions.
// This function is executed once when the module start.
//
void initHook(void)
{
   parinput  = new HEInParamLong("ParInput");
   dial      = new HEInParamLong("Dial");
   paroutput = new HEOutParamLong("ParOutput");
}

//-----------------------------------------------------------------------------
//
// Hook function for Explorer module actions.
// This function is executed when the module stop.
//
void removeHook(void)
{
   delete parinput;
   delete dial;
   delete paroutput;
}
The modifications compared to the previous version are paint in green.

Three adapters are declared as static pointers at the beginning of the file.

In initHook the adapters are created. At creation time, each adapter constructor has one parameter: the name of the parameter. This name should be exactly the same as the one given in the module builder.

The adapters are deleted in removeHook.

In the moduleName function:

This module multiply the input parameter value by the dial value and send the result on the output port.


Module included in a map

Like we did it previously we are now able to use the module in IRIS explorer. But now it is possible to connect the module to other modules available in the Librarian i.e.: to build a map. Connect the output of a PrintParam module to the input a modulename module. Then connect the output of modulename to the input of an other PrintParam module. You should get the following map:

With this new version of modulename you can move the dial in the module graphical user interface or change the value in PrintParam and you will see the value changing in PrintParam<2>.


How to use HistOOgrams

In this example we describe how to access histograms within a module. This module will rename an histogram in the data base and passed it to an output port. The module building phases are the same we just explained:

The Module Builder window should be:

Where HistOOgramRename.mk is:

OTH_INC         = -I/afs/cern.ch/sw/lhcxx/specific/@sys/HEPEXPLORER/pro/include/ \
                  -I$(EXPLORERHOME)/include -I$(EXPLORERUSERHOME)/include
HISTOO_INC      =  -I$(HISTOODIR)/code
OBJY_INC        =  -I$(OBJY_DIR)/include
HEP_ODBMS_INC   = -I$(HEP_ODBMS_DIR)/include
C++INCLUDES     = $(OTH_INC) $(HISTOO_INC) $(OBJY_INC) $(HEP_ODBMS_INC)

OTHLIB          = -L/afs/cern.ch/sw/lhcxx/specific/@sys/HEPEXPLORER/pro/lib/ \
                  -lHEAdaptors $(C++INCLUDES)
HISTO_LIB       = -L$(HISTOODIR)/lib -lHistoxx 
OBJY_LIB        = -L$(OBJY_DIR)/lib -loo.4.02 
HEPODBMS_LIB    = -L$(HEP_ODBMS_DIR)/lib -lHepODBMS
EXTRA_LIBRARIES = $(OTHLIB) $(HISTO_LIB) $(OBJY_LIB) $(HEPODBMS_LIB)
This script file is more complex than the previous one because now we need to access the HistOOgram libraries and include files. We will see later in details the HistOOgramRename.cpp file.

The Build options are the same as before.

The Function definition is now:

The definition of the special functions is also the same.

The Input Ports should be defined as follow:

The Output Ports should be defined as follow:

The ouput port HistoOuput should be connected to a Text widget. To do that follow the procedure previously described.

The last operation is to define the file HistOOgramRename.cpp:

#include "HEAdaptors/HEInputParam.h"
#include "HEAdaptors/HEInputHistOO.h"
#include "HEAdaptors/HEOutputHistOO.h"
#include "HEAdaptors/HE2Objy.h"
#include "histograms.h"

extern "C" {
   void HistOOgramRename      ( void );
   void initHook              ( void );
   void removeHook            ( void );
}

static HESessionHelper     *helper;
static HEInHistOO          *HistoInput;
static HEInParamStr        *NewName;
static HEOutHistOO         *HistoOutput;

//-----------------------------------------------------------------------------
//
// This function is the kernel of the module.
//
void HistOOgramRename (void) {

   helper->startUpdate();

   NewName->lookUp();

   if (NewName->notEmpty()) {
      HepRef(BaseHisto1D) histo_in;
      HistoInput->lookUp();
      histo_in = (HepRef(BaseHisto1D)&) HistoInput->getValue();
      histo_in->set_name(NewName->getValue());
      HistoOutput->sendOID(histo_in);
   }

   helper->commit();
}

//-----------------------------------------------------------------------------
//
// Hook function for Explorer module actions.
// This function is executed once when the module start.
//
void initHook(void)
{
   HistoInput  = new HEInHistOO("HistoInput");
   HistoOutput = new HEOutHistOO("HistoOutput");
   NewName     = new HEInParamStr("New Name");

// Open FDDB
   helper = new HESessionHelper();
   if (helper != 0) {
      if (helper->fDbCheck () == 0) { // Check if OO_FD_BOOT is defined
         removeHook();
         exit (1);
      }
   } else {
      cerr << "Can't create session helper" << endl;
      exit(1);
   }
}

//-----------------------------------------------------------------------------
//
// Hook function for Explorer module actions.
// This funtion is executed when the module stop.
//
void removeHook(void)
{
   delete HistoInput;
   delete HistoOutput;
   delete NewName;
   delete helper;
}
The structure of the function is very similar to the modulename.cpp one: Now the module is ready do be build.

The module can then be included in a explorer map: