Project

General

Profile

Genom3tutorialdemo-genom3 » History » Revision 21

Revision 20 (Aurélie Clodic, 2014-07-04 11:39) → Revision 21/22 (Matthieu Herrb, 2017-03-22 17:22)

h1. Genom3 through an example 

 {{toc}} 


 This section will illustrate genom3 concepts through a concrete example.  

 The demo compoment controls a virtual mobile that can move in a 1D world. Some of the services the module offers are: 
 * read and set the current speed at any moment 
 * move the mobile to a given position 
 * move the mobile of a given distance (from its current position) 
 * monitor when the mobile passes a given position 
 * stop the motion  

 Moreover, the demo component exports a port with the current state of the mobile (position and speed). 


 h3. The component description file: .gen file 

 http://git.laas.fr/git/demo-genom/tree/demo.gen?h=genom3 

 We will describe the .gen description file. It is made up of several parts, each of them being identified with a keyword 
 * @component@ 
 * @ids@ 
 * @port@ 
 * @exception@ 
 * @task@ 
 * @attribute@ 
 * @function@ 
 * @activity@ 

 This .gen reflects the fact that a component need to defi  ne an interface made of a set of services and data ports. A component must provide a service invocation facility as well as unidirectional (input and output) data ports. It must be able to send and receive events. It has to de  fine an internal data structure, named the @ids@, that is used by codels to store global and permanent objects. 
 Each codel has access to the @ids@ objects and use them to communicate with other codels within the component. 
 Finally, a component has to de  fine one or more @execution tasks@ that represent the execution context (e.g. thread) in which services are run. Those tasks may be periodic or aperiodic. 

 !blockdiagram.jpg! 

 The easiest way is to see how all of this is defined is to start from an existing file, so you can download ".gen" file and ".idl" file. We will now explain step by step each part of these files. 

 h4. Data structure definition  

 A component description always start with the de  finition of the data types used in the interface. Types are typically defi  ned in separate   files and @#included@ in the description, so that the de  finitions can be shared amongst other components. The syntax used is the subset of the IDL language (part of the OMG CORBA [5] speci  cation) related to data type de  finitions. Using IDL guarantees the programming language independance and o  ffers a standardized approach. Although IDL is part of CORBA, it should be clear that components are not tied anyhow to CORBA. 

 <pre> 
 #include "demoStruct.idl" 
 </pre> 

 The #include "demoStruct.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. In this example, the file "demoStruct.idl" contains the definition of the position and the speed. This file is preferably located in the same directory as .gen file, since it contributes to the definition of the component interface.  

 <pre> 
 #ifndef IDL_DEMO_STRUCT 
 #define IDL_DEMO_STRUCT 

 module demo { 

   const unsigned long task_period = 400; 
   const double millisecond = 0.001; 

   struct state { 
     double position;    /* current position (m) */ 
     double speed;       /* current speed (m/s) */ 
   }; 

   enum speed { 
     SLOW, 
     FAST 
   }; 
 }; 
 #endif  
 </pre> 


 h4. @component@ 

 The component declaration describes the instance of the GenoM3 component. It is defined by a unique name (an identifier) that also defines an IDL scope for any embedded types. See the "component declaration documentation":http://homepages.laas.fr/mallet/share/doc/genom3/genom3.html/Component-declaration.html#Component-declaration for details. 

 <pre> 
 component demo { 
   version 	 "1.1"; 
   email 		 "openrobots@laas.fr"; 
   lang 		 "c"; 
   require 	 "genom3 >= 2.99.20"; 
 </pre> 


 * version : The component version number, as a string 
 * lang : The programming language of the codels interface 
 * email : A string containing the e-mail address of the author of the component. 
 * requires : A list of dependencies of the component. It indicates an external dependency on a software package that is required. It assumes that the package is using the pkg-config utility. Each string should contain a package name in pkg-config format.  

 NB: component declaration is not over, the final } is left for the end @.gen@ file. 

 h4. @ids@ 

 @ids@ stands for internal data structure, it is used by codels to store global and permanent objects. 
 Each codel has access to the @ids@ objects and use them to communicate with other codels within the component. 

 For our example, we considerto store the state, the speed reference and the position of the mobile:  
 <pre> 
 ids { 
   demo::state state;            /* Current state */ 
   demo::speed speedRef;         /* Speed reference */ 
   double        posRef; 
 }; 
 </pre> 

 @demo::state@ and @demo::speed@ are types defined in the demoStruct.idl file (see above). 

 h4. @port@ 

 A component de  fine input and output data ports, used to implement data flow connections in parallel to services. 
 Ports implement the data flow between components as a publish/subscribe model. Ports have a name and a type and can be either out (for publishing data) or in (for subscribing to a sibling out port). Data ports Data ports are defi  ned via the in or out keyword, followed by an IDL type and the name of the port e.g.:  
 <pre> 
 port in data<type> name; 
 port out data<type> name; 
 </pre> 

 In our example, we choose to export the state of the mobile to let this information accessible for example to another port from another component: 
 <pre> 
 /* ---- Port declaration ---- */ 
   port out demo::state Mobile; 
 </pre> 

 h4. @exception@ 

 It is possible to define exception. 
 <pre> 
 /* ---- exception declaration ---- */ 
   exception TOO_FAR_AWAY {double overshoot;}; 
   exception INVALID_SPEED; 
 </pre> 

 h4. @execution task@ 

 Tasks define an execution context suitable for running activities. A task may define a state machine and associated codels.  
 The state machine starts in the start state when the task is created during component initialization.  
 Task description follows this scheme: 
 <pre> 
 task name { 
 period 100ms; 
 priority 200; 
 stack 20k; 
 codel start tstart(in ::ids) yield main; 
 codel main tmain(in d, out s) yield main, stop; 
 codel stop tstop() yield ether; 
 }; 
 </pre> 

 Tasks can define the following properties: 
 * @period@ The granularity of the codel scheduler. Periodic task will sequence the codels they manage at that frequency. 
 * @delay@ The delay from the beginning of each period after which codels are run. This can be used to delay two tasks running at the same period in the same component. 
 * @priority@ Can be used to prioritize different tasks whithin the same component. 
 * @scheduling real-time@ This indicates that the task requires real-time scheduling. This may not be supported by all templates. 
 * @stack@ Defines the required stack size for this task. The stack size should be big enough to run all codels that the task manages. 


 In our example, we choose to get the task_period from the idl file i.e. @demo::task_period@ parameter: 
 <pre> 
 /* ---- Execution task declaration ---- */ 

   task motion { 
     period 	 demo::task_period ms; 
     priority 	 100; 
     stack 	 4000; 
     codel <start> 	 InitDemoSDI(out ::ids, port out Mobile) yield ether; 
   }; 
 </pre> 

 h4. services declarations 

 A service is an interface for running codels. It can be invoked via a request on the component service port. A service has optional input and output data and a list of failure reports. Input data is stored in the IDS (and output read from there), so that codels can access it. A service might be incompatible with other services of the same component or can be started multiple time, provided it is compatible with itself. It always interrupts other incompatible services when starting. A service invocation triggers an activity that manages codel execution. An activity is described by a Petri net in which places correspond to codels execution and transitions are events generated either externally or implicitely by the return value of codels. 

 Service description follows this scheme: 
 <pre> 
 service name(inout s) { 
 doc "Service description"; 
 task taskname; 
 validate svalidate(); 
 throws ERROR_1, ERROR_2, ...; 
 interrupts name; 
 codel <start> sstart() yield step1; 
 codel <step1> sstep1() yield step1, step2; 
 codel <step2> sstep2() yield ether; 
 codel <stop> sstop() yield ether; 
 }; 
 </pre> 
 where: 
 * @name@ corresponds to the name of the service 
 * @s@ corresponds to the parameter of the service. Parameter could be in/inout/out depending of its type. It is possible to have several parameters. 
 * @task@ corresponds to the task that handle the service 
 * @validate@ corresponds to a function that would be called once at the beginning of the service execution and that aims to check service parameter(s) consistency 
 * @throws@ corresponds to the list of failure report(s) 
 * @interrupts@ corresponds to the service(s) that would be automatically interrupted when the this service is called 

 When an activity starts, the start event is generated and the corresponding codel executed. Similarly, the activity is interrupted whenever the stop event is generated. 
 Asynchronous events trigger the execution of the corresponding codel (if any). A special sleep transition is defi  ned so that an activity can be put in a sleeping state, waiting for external events or a stop to trigger a new transition. The activity stops when all active places in the Petri net have returned the special ether event. If the execution task of a service is periodic, transitions are executed at each period. They are otherwise executed as soon as all the codels corresponding to active places have returned. The codel execution order is unde  fined. 

 !activitydiagram.jpg! 

 It should be noticed that no direct remote procedure call (RPC) for service invocation between components is allowed. RPC should be performed by external applications that take care of setting up the architecture of components. While this di  ffers from traditional approaches, this guarantees that components can be controlled and will not interfere with the system. This also grants an increased reusability since no component explicitely depends on a particular set of services implemented by other components. 

 3 types of services are available through GenoM3: attribute, function and activity. Each follow the same service pattern. 


 h5. @attribute@ 

 @attribute@ service type should be used... 

 <pre> 
  attribute SetSpeed(in speedRef = demo::SLOW 	 :"Mobile speed") 
   { 
     doc 		 "To change speed"; 
     validate 	 controlSpeed (local in speedRef); 
     throw 	 INVALID_SPEED; 
   }; 

   attribute GetSpeed(out speedRef = 	 :"Mobile speed") 
   { 
     doc 		 "To get current speed value"; 
   }; 
 </pre> 

 h5. @function@ 

 <pre> 
  function Stop() 
   { 
     doc 		 "Stops motion and interrupts all motion requests"; 
     interrupts 	 MoveDistance, GotoPosition; 
   }; 
 </pre> 

 h5. @activity@ 

 <pre> 
 activity MoveDistance(in double distRef = 0 	 :"Distance in m") 
   { 
     doc 		 "Move of the given distance"; 
     validate 	 controlDistance(in distRef, in state.position); 

     codel <start> 	 mdStartEngine(in distRef, in state.position, 
                               out posRef) yield exec, ether; 
     codel <exec> 	 mdGotoPosition(in speedRef, in posRef, inout state, 
                                port out Mobile) yield exec, end; 
     codel <end, stop> 	 mdStopEngine() yield ether; 
     interrupts 	 MoveDistance, GotoPosition; 
     task 	 motion; 
     throw 	 TOO_FAR_AWAY; 
   }; 
 </pre> 

 h4. Data flow 

 !dataflow.jpg! 

 h3. Module generation 

 You can copy demo.gen and demoStruct.idl in your demo-genom3 repository. 

 <pre> 
 genom3 skeleton demo.gen 
 </pre> 

 From now on, the component is ready to be compiled and run. You can have a look at the result of the command. 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.  

 h3. Codels writing 

 In the codels directory, you would find these two files: @demo_codels.c@ and @demo_motion_codels.c@. 

 h4. @demo_codels.c@ 

 http://git.laas.fr/git/demo-genom/tree/codels/demo_codels.c?h=genom3 

 In this file, we will found codels executed by the control task such as the validation codels , e.g. controlSpeed (Validation codel of attribute SetSpeed), controlPosition (Validation codel of activity GotoPosition) and controlDistance (Validation codel of activity MoveDistance)..  


 h4. @demo_motion_codels.c@ 

 http://git.laas.fr/git/demo-genom/tree/codels/demo_motion_codels.c?h=genom3 

 In this file, we will found functions attached to the motion task, such as : 
 * <start> codel of the task 
 * <start> and <exec> codel of the GotoPosition activity  
 * etc 

 

 h3. Module compilation 

 We are now ready to compile the module. Note that you could have done it with the empty codels just after generating the skeleton. The result would have been a valid GenoM3 module, runnable, but not doing anyhing of course. 

  # bootstrap the autotools build system 
 <pre> 
 % autoreconf -vi 
 % mkdir build && cd build 
 </pre> 
  # configure the codels with a set of templates, e.g. for pocolibs : 
 <pre> 
 % ../configure --prefix=$INSTALL_DIR --with-templates=pocolibs/server,pocolibs/client/c 
 </pre> 
 or for ros : 
 <pre> 
 % ../configure --prefix=$INSTALL_DIR --with-templates=ros/server,ros/client/c,ros/client/ros --with-templates=ros/server,ros/client/c 
 </pre> 
  # build your module 
 <pre> 
 % make 
 </pre> 

 Iterate until there are no more errors. Then, you can install your module: 
 <pre> 
 % make install 
 </pre> 

 


 h3. Module execution 

 Given the template choosen the way you will run your module may be different.  


 h4. Pocolibs 

 h5. Setup (bash version) 

 <pre> 
 export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$INSTALL_PATH/lib/pkgconfig 
 </pre> 

 h5. Launch 

 First you need to launch pocolibs: 
 <pre> 
 % h2 init 
 </pre> 
 your module: 
 <pre> 
 % demo -b 
 </pre> 
 and genomix: 
 <pre> 
 % genomixd & 
 </pre> 

 Finalement, thanks to eltclsh you will be able to access to your module: 
 <pre> 
 % eltclsh 
 eltclsh > package require genomix 
 eltclsh > genomix::connect  
 genomix1 
 eltclsh > genomix1    load demo 
 eltclsh > ::demo::GetSpeed  
 speedRef ::demo::SLOW 
 </pre> 

 Note that the use of the tab completion will help you to have access to your module available services.  
 Moreover, if you do not know which parameter your module needs, you can call the service and then use tab to have access to the parameters. 

 Use "-ack" if you want to launch a request in background (useful if you do not want to loose access to the shell): 
 <pre> 
 eltclsh > ::demo::GetSpeed -ack 
 </pre> 
 

 h4. ROS 

 h5. Setup (bash version) 

 With $ROS_VERSION, your ros version : 
 <pre> 
 source /opt/ros/$ROS_VERSION/setup.bash 
 export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$INSTALL_PATH/lib/pkgconfig 
 export ROS_PACKAGE_PATH=$ROS_PACKAGE_PATH:$INSTALL_PATH/src/ros-nodes:$INSTALL_PATH/share 
 export PYTHONPATH=/opt/ros/groovy/lib/python2.7/dist-packages:$INSTALL_PATH/lib/python2.7/site-packages 
 </pre> 

 h5. Launch 
 First you need to launch ros master: 
 <pre> 
 % roscore  
 </pre> 
 Once it is initialized you can run your module: 
 <pre> 
 % demo-ros -b 
 </pre> 

 Then you will be able to use all ros facilities to access to your module, e.g.: 
 <pre> 
 % rosnode info demo 
 </pre> 
 will give you access to all availables services and topics. 

 To access to a topic, e.g. /demo/Mobile: 
 <pre> 
 % rostopic echo /demo/Mobile 
 </pre> 

 To call a service, e.g. /demo/SetSpeed: 
 <pre> 
 % rosservice call /demo/SetSpeed '{speedRef: {value: 1}}' 
 </pre>