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:
- select speed
- move the mobile to a given position
- read the current speed at any moment
- suspend the motion
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:
component module declaration
ids
task
attribute
function
activity
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.
Algorithms (or a part of them) are grouped in the directory codels/. The files in that directory give you template to start from, and also let GenoM3 produce a module even if you still do not have written a single line of code.
- The Makefile.am and configure.ac files are also under your control. These are the main files which are used for the compilation of the module.
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 :
InitMotionParameters: which is the <start> codel of the task
gotoposStart, gotoExec: which are <start> and <exec> codel of the GotoPosition activity
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:
configuration of the module by running the configure script
compilation itself, controlled by the Makefile generated in the previous step.
Bootstrap
- bootstrap the autotools build system
gupta[mobile-loco] ./bootstrap.sh autoreconf: Entering directory `.' autoreconf: configure.ac: not using Gettext autoreconf: running: aclocal -I autoconf autoreconf: configure.ac: tracing autoreconf: running: libtoolize --install --copy libtoolize: putting auxiliary files in AC_CONFIG_AUX_DIR, `autoconf'. libtoolize: copying file `autoconf/config.guess' libtoolize: copying file `autoconf/config.sub' libtoolize: copying file `autoconf/install-sh' libtoolize: copying file `autoconf/ltmain.sh' libtoolize: putting macros in AC_CONFIG_MACRO_DIR, `autoconf'. libtoolize: copying file `autoconf/libtool.m4' libtoolize: copying file `autoconf/ltoptions.m4' libtoolize: copying file `autoconf/ltsugar.m4' libtoolize: copying file `autoconf/ltversion.m4' libtoolize: copying file `autoconf/lt~obsolete.m4' autoreconf: running: /usr/bin/autoconf autoreconf: running: /usr/bin/autoheader autoreconf: running: automake --add-missing --copy --no-force configure.ac:9: installing `autoconf/missing' codels/Makefile.am: installing `autoconf/depcomp' autoreconf: Leaving directory `.'
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:
- pocolibs ($TEMPLATES=pocolibs/server,pocolibs/client/c)
- pocolibs/server is the genom pocolibs module itself
- pocolibs/client/c is the pocolibs C client library
- ros ($TEMPLATES=ros/server,ros/client/c,ros/client/ros)
- ros/server is the genom ros module itself
- ros/client/c is the ros C client library
- openprs ($TEMPLATES=openprs/client), you can mix openprs template with the others (e.g.: $TEMPLATES=pocolibs/server,pocolibs/client/c,openprs/client)
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 ...