part 1: How to write your first module ?

This section will illustrate a concrete use of genom3. The mobile-loco module will control a virtual mobile that can move in a 2D world. Some of the services the module offers are:

To implement this, we first create a directory named mobile-loco.

mkdir mobile-loco
cd mobile-loco

Write .gen File

In that directory, we will write the description file mobile-loco.gen. The file mobile-loco.gen is made up of several parts, each of them being identified with a keyword:

The entire file will look like this:

#include "mobile-loco-struct.idl"
#include "mobile-loco-interface.gen"

/* -------------------------- MODULE DECLARATION --------------------------- */
component mobileloco {
 version  "1.0";
 email    "openrobots@laas.fr";
 lang     "c";
 provides mobilelocointerface;

/* ------------- DEFINITION OF THE MODULE’s INTERNAL DATABASE -------------- */
 ids {
  mobilelocointerface::position start_position;
  mobilelocointerface::position current_position;
  mobilelocointerface::position end_position;
 };

/* ------------------ TASK DEFINITION -------------------- */

task motion {
 period     mobileloco::task_period ms;
 priority   100;
 stack      4000;
 codel <start> InitMotionParameters(out ::ids, port out E_current_position) yield ether;
};

/* ------------------ SERVICES DEFINITION: The ATTRIBUTES -------------------- */

attribute SetSpeed(in current_speed=    :"Mobile speed")
{
 doc            "To increase speed";
 validate       controlSpeed (local in current_speed);
 throw          INVALID_SPEED;
};

attribute GetSpeed(out current_speed =  :"Mobile speed")
{
 doc            "To get current speed value";
};

/* ------------------ SERVICES DEFINITION: The Functions -------------------- */

function Stop()
{
 doc            "Stops motion and interrupts all motion requests";
 interrupts     GotoPosition;
};


/* ------------------ SERVICES DEFINITION: The activities -------------------- */

activity GotoPosition (in mobilelocointerface::position goto_position =  :"Goto position")
{
 doc                  "Move to the given position";
 validate             controlPosition (in goto_position);
 codel <start>        gotoposStart(in goto_position, inout ::ids) yield exec, ether;
 codel <exec>         gotoposExec(inout ::ids, port out E_current_position) yield exec, end, stop;
 codel <end,stop>     gotoposStop(inout ::ids) yield ether;
 interrupts           GotoPosition, Stop;
 task                 motion;
 throw                INVALID_POSITION;
};

};

Data Structure Definition

The #include "mobile-loco-struct.idl" statement works as a header file in C and includes idl data structure. This file contains all the necessary declarations for the definition of the internal database. These structures are then used in the .gen file. (The #include "mobile-loco-interface.gen" will be useful later when we would be able to link the module with another one). In this example, the file "mobile-loco-struct.idl" contains the definition of the position and the speed. This file is preferably located in the same directory as mobile-loco.gen, since it contributes to the definition of the module interface. rq: if you get demo-mobile-loco module through git, the idl definition is quite different and is moved to the mobile-loco-interface.gen file to interface with demo-mobile-disp

#ifndef IDL_MOBILE_LOCO_STRUCT
#define IDL_MOBILE_LOCO_STRUCT

module mobileloco {

 /* task period */
 const unsigned long task_period = 400;

 /* mobile position definition*/
 struct position {
 double x;
 double y;
 };

 /* speed definition */
 enum speed_enum {
 SLOW,
 MEDIUM,
 FAST
 };
 struct speed {
 speed_enum s;
 };
};
#endif

Module generation

Once your description file (.gen) ready, you need to generate your module:

gupta[mobile-loco] genom3 skeleton mobile-loco.gen
creating ./codels/mobileloco_motion_codels.c
creating ./codels/mobileloco_codels.c
creating ./libmobileloco_codels.pc.in
creating ./libmobileloco_codels-uninstalled.pc.in
creating ./bootstrap.sh
creating ./autoconf/ag_templates.m4
creating ./configure.ac
creating ./Makefile.am
creating ./codels/Makefile.am

From now on, the module is ready to be compiled and run, but let's look at the result of the execution of the command:

gupta[mobile-loco] ls
autoconf/      codels/      mobileloco-genom.pc.in                  Makefile.am      mobile-loco-struct.idl
bootstrap.sh  configure.ac  mobileloco-genom-uninstalled.pc.in  mobile-loco.gen  mobile-loco-interface.gen

GenoM3 created two new directories codels/ and autoconf/, and several new files.

Codels writing

Let's have a look in the codels directory.

gupta[codels] ls
Makefile.am  Makefile.in  mobileloco_codels.c  mobileloco_motion_codels.c

mobileloco_codels.c

In this file, we will found codels related to validation, e.g. controlSpeed (Validation codel of attribute SetSpeed) and controlPosition (Validation codel of activity GotoPosition).

rq: take care, code from git is quite different due to interface with mobile-disp but both works

At the beginning, the template will look like:

gupta[codels] more mobileloco_codels.c
#include "acmobileloco.h"

#include "mobileloco_c_types.h"


/* --- Attribute SetSpeed ----------------------------------------------- */

/** Validation codel controlSpeed of attribute SetSpeed.
 *
 * Returns ok.
 * Throws INVALID_SPEED.
 */
mobileloco_event
controlSpeed(const mobileloco_speed *current_speed)
{
  /* skeleton sample: insert your code */
  /* skeleton sample */ return mobileloco_ok;
}


/* --- Activity GotoPosition -------------------------------------------- */

/** Validation codel controlPosition of activity GotoPosition.
 *
 * Returns ok.
 * Throws INVALID_POSITION.
 */
mobileloco_event
controlPosition(const mobileloco_position *goto_position)
{
  /* skeleton sample: insert your code */
  /* skeleton sample */ return mobileloco_ok;
}


/* --- Activity Monitor ------------------------------------------------- */

/** Validation codel controlPosition of activity Monitor.
 *
 * Returns ok.
 * Throws .
 */
/* already defined in service GotoPosition validation */

Let's have a look on how we can write such functions:

controlSpeed

mobileloco_event
controlSpeed(const mobileloco_speed *current_speed)
{
  if((current_speed->s != mobileloco_SLOW) &&
     (current_speed->s != mobileloco_MEDIUM) &&
     (current_speed->s != mobileloco_FAST))
    return mobileloco_INVALID_SPEED;
  return mobileloco_ok;
}

controlPosition

mobileloco_event
controlPosition(const mobileloco_position *goto_position)
{
  if((goto_position->x > 100.0) ||
     (goto_position->x < -100.0) ||
     (goto_position->y > 100.0) ||
     (goto_position->y < -100.0))
    return mobileloco_INVALID_POSITION;
  return mobileloco_ok;
}

mobileloco_motion_codels.c

In this file, we will found functions attached to the motion task, such as :

gupta[codels] more mobileloco_motion_codels.c
#include "acmobileloco.h"

#include "mobileloco_c_types.h"


/* --- Task motion ------------------------------------------------------ */


/** Codel InitMotionParameters of task motion.
 *
 * Triggered by start.
 * Yields to ether.
 */
mobileloco_event
InitMotionParameters(mobileloco_ids *ids,
                     const mobileloco_E_current_position *E_current_position)
{
  /* skeleton sample: insert your code */
  /* skeleton sample */ return mobileloco_ether;
}


/* --- Activity GotoPosition -------------------------------------------- */

/** Codel gotoposStart of activity GotoPosition.
 *
 * Triggered by start.
 * Yields to exec, ether.
 * Throws INVALID_POSITION.
 */
mobileloco_event
gotoposStart(const mobileloco_position *goto_position,
             mobileloco_ids *ids)
{
  /* skeleton sample: insert your code */
  /* skeleton sample */ return mobileloco_exec;
}

/** Codel gotoposExec of activity GotoPosition.
 *
 * Triggered by exec.
 * Yields to exec, end, stop.
 * Throws INVALID_POSITION.
 */
mobileloco_event
gotoposExec(mobileloco_ids *ids,
            const mobileloco_E_current_position *E_current_position)
{
  /* skeleton sample: insert your code */
  /* skeleton sample */ return mobileloco_exec;
}

/** Codel gotoposStop of activity GotoPosition.
 *
 * Triggered by end, stop.
 * Yields to ether.
 * Throws INVALID_POSITION.
 */
mobileloco_event
gotoposStop(mobileloco_ids *ids)
{
  /* skeleton sample: insert your code */
  /* skeleton sample */ return mobileloco_ether;
}


/* --- Activity Monitor ------------------------------------------------- */

/** Codel monitStart of activity Monitor.
 *
 * Triggered by start.
 * Yields to start, stop.
 */
mobileloco_event
monitStart(const mobileloco_position *monitored_position,
           const mobileloco_ids *ids)
{
  /* skeleton sample: insert your code */
  /* skeleton sample */ return mobileloco_start;
}

/** Codel monitStop of activity Monitor.
 *
 * Triggered by stop.
 * Yields to ether.
 */
mobileloco_event
monitStop(void)
{
  /* skeleton sample: insert your code */
  /* skeleton sample */ return mobileloco_ether;
}

Let's have a look on how we can write such functions:

gotoposExec

mobileloco_event
gotoposExec(mobileloco_ids *ids,
            const mobileloco_E_current_position *E_current_position)
{
  double step;
  int end_x = 0;
  int end_y =0 ;

  switch(ids->current_speed.s){
  case mobileloco_SLOW:
    step=0.1;
    break;
  case mobileloco_MEDIUM:
    step=0.5;
    break;
  case mobileloco_FAST:
    step=1.0;
    break;
  }

  if(x_direction==FORWARD) {
    if(ids->end_position.x > (ids->current_position.x + step)){
      ids->current_position.x = ids->current_position.x + step;
    } else {
      ids->current_position.x = ids->end_position.x;
      end_x = 1;
    }
  } else {
    if(ids->end_position.x < (ids->current_position.x - step)){
      ids->current_position.x = ids->current_position.x - step;
    } else {
      ids->current_position.x = ids->end_position.x;
      end_x = 1;
    }
  }

  if(y_direction==FORWARD) {
    if(ids->end_position.y > (ids->current_position.y + step)){
      ids->current_position.y = ids->current_position.y + step;
    } else {
      ids->current_position.y = ids->end_position.y;
      end_y = 1;
    }
  } else {
    if(ids->end_position.y < (ids->current_position.y - step)){
      ids->current_position.y = ids->current_position.y - step;
    } else {
      ids->current_position.y = ids->end_position.y;
      end_y = 1;
    }
  }

  E_current_position->data()->x= ids->current_position.x;
  E_current_position->data()->y= ids->current_position.y;
  E_current_position->write();

  if ((end_x == 1) && (end_y==1)) {
    return mobileloco_end;
  } else {
    return mobileloco_exec;
  }
}

gotoposStop

mobileloco_event
gotoposStop(mobileloco_ids *ids)
{
  ids->start_position.x=ids->current_position.x;
  ids->start_position.y=ids->current_position.y;
  return mobileloco_ether;
}

Module compilation

There are two steps in compilation of the module:

Bootstrap

Build directory

In order to keep objects files separated from the sources, for instance when you want to generate the module for several different architectures, or just in order to have a simple mean to clean up everything that was produced during the building phase, it is strongly recommended to create a separate build directory and run every command from there.

gupta[mobile-loco] mkdir build
gupta[mobile-loco] cd build

Configuration

All the OpenRobots software and tools are generally installed in a specific directory (for instance ${HOME}/openrobots). This is the main information that needs to be specified to the configure script. Don't forget to run configure from the build directory. Then, the options will be different given the templates you want to generate, you have several possibilities:

e.g. here, if we set $TEMPLATES=pocolibs/server,pocolibs/client/c

gupta[build] ../configure --prefix=$INSTALL_DIR --with-templates=$TEMPLATES
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking build system type... x86_64-unknown-linux-gnu
checking host system type... x86_64-unknown-linux-gnu
...
...
...
checking for pocolibs... yes
checking for genom3_pocolibs... yes
checking for genom3... yes
configure: creating ./config.status
config.status: creating Makefile
config.status: creating mobileloco-c-client.pc
config.status: creating mobileloco-c-client-uninstalled.pc
config.status: creating autoconf/acheader.h
config.status: executing depfiles commands
config.status: executing libtool commands
configure: done configuring for pocolibs/client/c

Compilation

To compile the module, just run make from the build directory. The GNU make utility is required, but this is the standard make on linux systems.

gupta[build] make
Making all in codels
make[1]: Entering directory `/home/aclodic/Work/01_Demo/mobile-loco/build/codels'
make  all-am
make[2]: Entering directory `/home/aclodic/Work/01_Demo/mobile-loco/build/codels'
make[2]: Nothing to be done for `all-am'.
...
...
...
lib/genom/client/c mobileloco_la-mobileloco_plugin.lo
libtool: link: gcc -shared  .libs/mobileloco_la-mobileloco_plugin.o   -Wl,-rpath -Wl,/home/aclodic/Work/01_Demo/mobile-loco/build/pocolibs/client/c/.libs -Wl,-rpath -Wl,/home/aclodic/Work/01_Demo/INSTALL/lib ./.libs/libmobileloco-c-client.so  -pthread   -pthread -Wl,-soname -Wl,mobileloco-1.0.so -o .libs/mobileloco-1.0.so
libtool: link: (cd ".libs" && rm -f "mobileloco.so" && ln -s "mobileloco-1.0.so" "mobileloco.so")
libtool: link: ar cru .libs/mobileloco.a  mobileloco_la-mobileloco_plugin.o
libtool: link: ranlib .libs/mobileloco.a
libtool: link: ( cd ".libs" && rm -f "mobileloco.la" && ln -s "../mobileloco.la" "mobileloco.la" )
make[1]: Leaving directory `/home/aclodic/Work/01_Demo/mobile-loco/build/pocolibs/client/c'
make[1]: Entering directory `/home/aclodic/Work/01_Demo/mobile-loco/build'
make[1]: Nothing to be done for `all-am'.
make[1]: Leaving directory `/home/aclodic/Work/01_Demo/mobile-loco/build'

Installation

The built binaries and libraries (and some associated files) need to be copied to their final locations. This is achieved by executing make install. Depending of the {$INSTALL_PATH} you used, this may require root privilege.

gupta[build] make install
Making install in codels
make[1]: Entering directory `/home/aclodic/Work/01_Demo/mobile-loco/build/codels'
make  install-am
make[2]: Entering directory `/home/aclodic/Work/01_Demo/mobile-loco/build/codels'
make[3]: Entering directory `/home/aclodic/Work/01_Demo/mobile-loco/build/codels'
test -z "/home/aclodic/Work/01_Demo/INSTALL/lib" || /bin/mkdir -p "/home/aclodic/Work/01_Demo/INSTALL/lib"
 /bin/bash ../libtool   --mode=install /usr/bin/install -c   libmobileloco_codels.la '/home/aclodic/Work/01_Demo/INSTALL/lib'
libtool: install: /usr/bin/install -c .libs/libmobileloco_codels-1.0.so /home/aclodic/Work/01_Demo/INSTALL/lib/libmobileloco_codels-1.0.so
libtool: install: (cd /home/aclodic/Work/01_Demo/INSTALL/lib && { ln -s -f libmobileloco_codels-1.0.so libmobileloco_codels.so || { rm -f libmobileloco_codels.so && ln -s libmobileloco_codels-1.0.so libmobileloco_codels.so; }; })
libtool: install: /usr/bin/install -c .libs/libmobileloco_codels.lai /home/aclodic/Work/01_Demo/INSTALL/lib/libmobileloco_codels.la
libtool: install: /usr/bin/install -c .libs/libmobileloco_codels.a /home/aclodic/Work/01_Demo/INSTALL/lib/libmobileloco_codels.a
libtool: install: chmod 644 /home/aclodic/Work/01_Demo/INSTALL/lib/libmobileloco_codels.a
libtool: install: ranlib /home/aclodic/Work/01_Demo/INSTALL/lib/libmobileloco_codels.a
libtool: finish: PATH="/home/aclodic/Work/01_Demo/INSTALL:/home/aclodic/Work/01_Demo/INSTALL/bin:/home/aclodic/Work/01_Demo/INSTALL/sbin:/home/aclodic/bin:/usr/local/bin:/home/aclodic/bin:/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:.:/sbin" ldconfig -n /home/aclodic/Work/01_Demo/INSTALL/lib
...
...
...
test -z "/home/aclodic/Work/01_Demo/INSTALL/lib/pkgconfig" || /bin/mkdir -p "/home/aclodic/Work/01_Demo/INSTALL/lib/pkgconfig"
 /usr/bin/install -c -m 644 mobileloco-c-client.pc '/home/aclodic/Work/01_Demo/INSTALL/lib/pkgconfig'
make[2]: Leaving directory `/home/aclodic/Work/01_Demo/mobile-loco/build/pocolibs/client/c'
make[1]: Leaving directory `/home/aclodic/Work/01_Demo/mobile-loco/build/pocolibs/client/c'
make[1]: Entering directory `/home/aclodic/Work/01_Demo/mobile-loco/build'
make[2]: Entering directory `/home/aclodic/Work/01_Demo/mobile-loco/build'
make[2]: Nothing to be done for `install-exec-am'.
test -z "/home/aclodic/Work/01_Demo/INSTALL/lib/pkgconfig" || /bin/mkdir -p "/home/aclodic/Work/01_Demo/INSTALL/lib/pkgconfig"
 /usr/bin/install -c -m 644 libmobileloco_codels.pc '/home/aclodic/Work/01_Demo/INSTALL/lib/pkgconfig'
make[2]: Leaving directory `/home/aclodic/Work/01_Demo/mobile-loco/build'
make[1]: Leaving directory `/home/aclodic/Work/01_Demo/mobile-loco/build'

Module execution

Once compiled, the module is ready to be executed. The module is located in the bin subdirectory of the directory specified as prefix in the configuration step. This is an executable whose name will depend of the $TEMPLATE you choose. E.g., in our case, if we use pocolibs template, we will have mobileloco-pocolibs executable and if you use ros template, we will have mobileloco-ros.

pocolibs

tcl

h2 init &
mobileloco-pocolibs &
genomixd &
eltclsh
eltclsh > package require genomix
1.0
eltclsh > genomix::connect
genomix1
eltclsh > genomix1 load mobileloco
pocolibs execution environment version 2.12.99
Copyright (c) 1999-2011 CNRS-LAAS
mobileloco
eltclsh > mobileloco::
::mobileloco::E_current_position ::mobileloco::GotoPosition       ::mobileloco::SetSpeed           ::mobileloco::abort_activity     ::mobileloco::connect_remote
::mobileloco::GetSpeed           ::mobileloco::Monitor            ::mobileloco::Stop               ::mobileloco::connect_port       ::mobileloco::genom_state
eltclsh > ::mobileloco::GetSpeed
current_speed {s ::mobileloco::SLOW}

pkill mobileloco-pocolibs
pkill genomix
h2 end

ros

tcl

ros-core &
mobileloco-ros &
genomixd &
eltclsh
eltclsh > package require genomix
1.0
eltclsh > genomix::connect
genomix1
eltclsh > genomix1 load mobileloco
pocolibs execution environment version 2.12.99
Copyright (c) 1999-2011 CNRS-LAAS
mobileloco
eltclsh > mobileloco::
::mobileloco::E_current_position ::mobileloco::GotoPosition       ::mobileloco::SetSpeed           ::mobileloco::abort_activity     ::mobileloco::connect_remote
::mobileloco::GetSpeed           ::mobileloco::Monitor            ::mobileloco::Stop               ::mobileloco::connect_port       ::mobileloco::genom_state
eltclsh > ::mobileloco::GetSpeed
current_speed {s ::mobileloco::SLOW}

pkill mobileloco-pocolibs
pkill genomix
h2 end

ros

Launch

ros-core &
mobileloco-ros &

Get info on your module:

export ROS_PACKAGE_PATH=$ROS_PACKAGE_PATH:$INSTALLDIR/build/ros/server/genom_mobileloco
export PYTHONPATH=/opt/ros/fuerte/lib/python2.6/dist-packages:$INSTALLDIR/lib/python2.6/site-packages
rosnode -info mobileloco

Send a command:

/opt/ros/electric/stacks/common/actionlib/tools/axclient.py /mobileloco/GotoPosition genom_mobileloco/GotoPosition
...