Tutorial 2: Configuring a new driver

  • NB: Document based on OpenViBE 1.1.0.

Introduction

This part of the tutorial will introduce the use of Gtk and Glade, to design the GUI which will be called when the user will ask for the “properties” of your driver.

Gtk is a LGPL toolkit for creating Graphic User Interface (GUI), written in C. Glade is a Rapid Development Tool (RAD) that enables quick and easy development of such user interfaces. Glade saves the GUI as XML, then the OpenViBE acquisition server uses the Gtk Builder to load the interface dynamically as needed.

The Gtk toolkit is already installed with the OpenViBE dependencies, but you have to install Glade on your own (http://glade.gnome.org/). Please keep in my mind that OpenViBE use version 2 of Gtk.

The .ui XML files have to be placed in the directory:

  • contrib/plugins/server-drivers/[YourDriver]/share/

You can look at .ui files of other drivers for examples.

The Configuration Process

The configuration process needs 3 entities:

  • The Header object
  • The Driver, which contains a Header object
  • The Configuration object, which one instance is created in the configure(void) function of the driver.

The purpose of the configuration process is to fill the Header with user-defined data. The driver creates an instance of the Configuration given a .ui file name, and calls the configure(&IHeader) function on the Header member. The user has to fill in the data in the GUI, then when he presses “Apply”, all data stored into the Configuration object will be used to fill the driver Header.

The basic header contains information about:

  • The subject: identifier, age, gender
  • The channels: number, names
  • The acquisition: sampling frequency
  • The configuration: impedance checking

See OpenViBEAcquisitionServer::IHeader for more details about this header.

The Generic Oscillator driver uses such basic Header. The configuration GUI looks like this:


The basic configuration dialog (interface-Generic-Oscillator.ui)

The corresponding configure(void) function in OpenViBEAcquisitionServer::CDriverGenericOscillator is:

boolean CDriverGenericOscillator::configure(void)
{
  // We use the basic CConfigurationBuilder object, with the corresponding .ui file
  CConfigurationBuilder m_oConfiguration( OpenViBE::Directories::getDataDir() + "applications/acquisition-server/interface-Generic-Oscillator.ui");
  // We configure the Header with it
  return m_oConfiguration.configure(m_oHeader);
}

Most of the time, driver developers will use the basic header with a slightly different configuration. For example, the OpenEEG Modular EEG driver needs the COM port number on which the device is connected. Besides the basic information, user can select the COM port used.


The Modular EEG configuration dialog (interface-OpenEEG-ModularEEG.ui)

The OpenViBEAcquisitionServer::CConfigurationOpenEEGModularEEG inherits from OpenViBEAcquisitionServer::CConfigurationBuilder, and needs a reference to the variable (member of the driver) which will contains the device COM port selected. Thus, the configure(void) function in the driver looks like this:

boolean CDriverOpenEEGModularEEG::configure(void)
{
   // We use a specialized Configuration object that will fill the
   // m_ui32DeviceIdentifier of the driver with the right value
   CConfigurationOpenEEGModularEEG m_oConfiguration(OpenViBE::Directories::getDataDir() + "applications/acquisition-server/interface-OpenEEG-ModularEEG.ui"
                                                     , m_ui32DeviceIdentifier);
   // We configure the basic Header
   return m_oConfiguration.configure(m_oHeader);
}

Coding the Configuration class and designing the GUI with Glade

The simplest solution is to start from the CConfigurationBuilder class and the interface-Generic-Oscillator.ui file. First of all, think about the basic information that the acquisition client will need. You may want to keep what is related to the subject’s information “As is”, but the max-min channels number may change depending on the device, so as the allowed sampling frequencies. Tweak the .ui file according to your device.

Then list what your driver will need besides these information: USB port ? Serial port ? Hostname/port to a third party acquisition software ? You will have to translate this into code:

  • Driver class: add new member variables describing these information.
  • GUI: use Glade to Drag’n'Drop combo-box, spin-button, labels, etc. to make your own GUI.
  • Configuration class: the CConfigurationBuilder class is probably not enough, implement your own inherited class.

Your configuration class will need references to the driver variables, and will redefine the preConfigure(void) and PostConfigure(void) functions of OpenViBEAcquisitionServer::CConfigurationBuilder. See the ovasCConfigurationGTecGUSBamp.h/cpp files for a complete example:

  • preConfigure(void): scan USB ports, fill the gtk-combo-box with available devices
 boolean CConfigurationGTecGUSBamp::preConfigure(void)
 {
        if(!CConfigurationBuilder::preConfigure())
        {
                return false;
        }

        ::GtkComboBox* l_pComboBox=GTK_COMBO_BOX(gtk_builder_get_object(m_pBuilderConfigureInterface,
                                                                        "combobox_device"));

        char l_sBuffer[1024];
        int l_iCount=0;
        boolean l_bSelected=false;

        // autodetection of the connected device
        for(uint32 i=1; i<11; i++)
        {
                ::HANDLE l_pHandle=::GT_OpenDevice(i);
                if(l_pHandle)
                {
                        ::GT_CloseDevice(&l_pHandle);

                        sprintf(l_sBuffer, "USB port %i", i);
                        ::gtk_combo_box_append_text(l_pComboBox, l_sBuffer);
                        if(m_rUSBIndex==i)
                        {
                                ::gtk_combo_box_set_active(l_pComboBox, l_iCount);
                                l_bSelected=true;
                        }
                        l_iCount++;
                }
        }

        if(!l_bSelected && l_iCount!=0)
        {
                ::gtk_combo_box_set_active(l_pComboBox, 0);
        }

        return true;
 }


  • postConfigure(void): take the selected device and update the driver reference

 boolean CConfigurationGTecGUSBamp::postConfigure(void)
 {
        ::GtkComboBox* l_pComboBox=GTK_COMBO_BOX(gtk_builder_get_object(m_pBuilderConfigureInterface,
                                                                        "combobox_device"));

        if(m_bApplyConfiguration)
        {
                int l_iUSBIndex=0;
                if(::sscanf(gtk_combo_box_get_active_text(l_pComboBox), "USB port %i", &l_iUSBIndex)==1)
                {
                        m_rUSBIndex=(uint32)l_iUSBIndex;
                }
        }

        if(!CConfigurationBuilder::postConfigure())
        {
                return false;
        }
        return true;
 }

Please note the calls to CConfigurationBuilder::preConfigure(void) and CConfigurationBuilder::postConfigure(void), so that basic header information are set.

Complex configuration

The previous example may not be enough for a more complex device. For example, the bipolar capacities of the VAmp-16 device needed a specific implementation.

Configuring a driver is hardly dependent on the device itself and the interface provided by the manufacturer : API, communication protocol, connection type, etc. We cannot cover here all the possible aspects, but the developer community can help you in the process!

Feel free to use the forum, IRC or mailists. We will help you asap !

This entry was posted in Acquisition drivers and tagged , , . Bookmark the permalink.