RHD:Plug-in architecture: Difference between revisions

From Rsewiki
 
(One intermediate revision by the same user not shown)
Line 217: Line 217:
You can read values from ''write'' variables by the call:
You can read values from ''write'' variables by the call:


     int  getWriteVariable(int variable, int value_index);
     int  getWriteVariable(int varidx, int value_index);


The variable integer is the index from when the ''write'' variable was created, and the ''value_index'' in 0 if there is just one value, or the index to the value, if variable is an array.
The ''varidx'' integer is the index from when the ''write'' variable was created, and the ''value_index'' is 0 if there is just one value, or the index to the value, if the variable is an array.


===test the plugin===
===test the plugin===

Latest revision as of 12:11, 18 September 2011

Load a plug-in

Plugins are loaded when they are mentioned in the rhdconfig file, e.g. like

<?xml version="1.0" ?>
<rhd>
 <sheduler>
   <period value="3000000"/>
   <type value="itimer"/>
 </sheduler>
 <server>
   <port value="24902"/>
   <clients number="10" allwriters="1"/>
 </server>
 <plugins basepath="/usr/local/smr/lib/rhdplugin/">
   <joycontrol enable="true" lib="libjoycontrol.so.1" critical="false" safety="1">
       <joystick port="/dev/input/js0"/>
   </joycontrol>
   <sf9dof enable="true"
         lib="libsf9dof.so.1"
         critical="false"
         dev="/dev/ttyS1"
         baudrate="38400"
         debug="0">
   </sf9dof>
 </plugins>
</rhd>

Here the plugins section has an attribute basepath that states where the plug-in files are to be found.

Two plugins are loaded in this example, a joycontrol plug-in from the file libjoycontrol.so.1, in the joycontrol XML group is an embedded XML tag, called joystick with the needed configuration data.

Further is a sf9dof plugin, where the plugin configuration data is placed as attributes directly in the sf9dof tag.

Plug-in function

A plugin should read the configuration file, and all relevant configuration values should be controllable from the configuration file.

The plug-in is expected to have a function called initXML(char * filename) that reads the configuration file and initializes the plug-in as needed. If all went well, then the function should return 0.

At every sample time, just after data may have been received from weriter clients, a function called periodic(int tick) is called. This can be used to react on new variable values, or other regular tasks.

On exit, when the rhd terminates, the terminate() function is called, to allow to stop controlled devices and to close logfiles etc.

XML-read

When you make a new rhd-plgin, start with an existing one, and modify as needed.

The first thing is the initialization - the read of the configuration file.

The initXML(char * filename) can mostly be left as is. This is the place where some configuration data structures can be set with default values. This should be done in the start of the initXML() function. Later in char * filename there is a call to XML_parse(...), this is where the rhdconfig file is parsed, and most values should be decoded in the lsStartTag(void *data, const char *el, const char **attr)

A (simple) example is in the sf9dof.c plugin from the sparkfun gyro-accelerometer-compas plug-in:

void XMLCALL lsStartTag(void *data, const char *el, const char **attr)
{ // a start tag is detected
 int i;
 parseInfo *info = (parseInfo *) data;
 info->depth++;
 if (info->depth < info->skip || info->skip == 0)  {
   switch (info->depth)    {
     case 1:
       if (strcmp("rhd",el) == 0) ;
       else info->skip = info->depth;
       break;
     case 2:
       if (strcmp("plugins",el) == 0)
         ; // no attributes here - but at next level
       else
         // skip this group
         info->skip = info->depth;
       break;
     case 3:
       // this one handles sf9dof only
       if (strcmp("sf9dof",el) == 0)
       { // get enable bit and device name
         const char * att, * val;
         for(i = 0; attr[i]; i+=2) {
           att = attr[i];
           val = attr[i + 1];
           if ((strcmp("enable",att) == 0) && (strcmp("true",val) == 0))
             info->enable = 1;
           else if (strcmp("dev", att) == 0) 
           {
             char * s = serif.serialDev;
             strncpy(serif.serialDev, val, MxDL);
             printf("%s\n", s);
           }
           else if (strcmp("baudrate", att) == 0)
           {
             serif.baudrate = strtol(val, NULL, 0);
           }
           else if (strcmp("debug", att) == 0)
           {
             serif.debugFlag = strtol(val, NULL, 0);
             if (serif.debugFlag)
               printf("   SF9DOF started in DEBUG mode!\n");
           }
         }
         if (!info->enable)
           printf("   SF9DOF: Use is disabled in configuration\n");
       }
       else
         info->skip = info->depth;
       break;
     default: // unknown tag series
       break;
   }
 }
}

Here the main parameters to extract is the serial device name and baudrate (see the rhdconfig.xml file on top of this page) they are extracted at "depth level 3" in the tag called "sf9dof".

Initialize device

After the parsing of the XML file the device needs to be initialized. This is initiated at the end of the initXML(char * filename) with a call to initSf9dof():

extern int initXML(char *filename)
{
 int result;
 parseInfo xmlParse; 

 ...

   result = (XML_Parse(parser, xmlBuf, len, done) != XML_STATUS_ERROR);

 ...

 if (result && xmlParse.enable)
 { // all is fine - start plugin
   result = initSf9dof();
 }
 return result;
}

The init function should open and configure the devise for operation and create the variables write and read that should be exchanged with the rhd clients.

Very often a read thread needs to be started to handle the communication with the device,

The function could look something like:

intinitSf9dof(void)
{ //Open first serial port
 int result;
 rxtask.running = 0;
 rxtask.startNewRxCycle = 0;
 // open device
 serif.ttyDev = open(serif.serialDev, O_RDWR /*| O_NONBLOCK*/);
 result = serif.ttyDev != -1;
 if (result == 0)
   fprintf(stderr,"   SF9DOF: Can't open device: %s\n", serif.serialDev);
 ...
 if (result == 1)
 { // start thread to handle bus
   pthread_attr_t attr;
   pthread_attr_init(&attr);
   pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
   if (pthread_create(&rxtask.sf9dof_thread, &attr, sf9dof_task, 0))
   { perror("   SF9DOF: Can't start sf9dof receive thread"); result = 0; }
 }
 if (result == 1)
 { /****** Create database variables if all is ok **************/
   createSf9dofvariables();
   ...
 }
 return result; 
}

A read thread sf9dof_task(void *) is started to communicate with the device, and a createSf9dofvariables() is called to create the data exchange variables.

Create database variables

The createSf9dofvariables() could look like:

void createSf9dofvariables()
{ // version
 sf9dof.varVersion = createVariable('r', 1, "version");
 // accelerometer values
 sf9dof.varAcc = createVariable('r', 3, "sf9acc");
 // gyro values
 sf9dof.varGyro = createVariable('r', 3, "sf9gyro");
 // magnetometer values
 sf9dof.varMag = createVariable('r', 3, "sf9mag");
 // calculated rotation from acc
 sf9dof.varRoll = createVariable('r', 2, "sf9roll");
 // calculated rotation from acc
 sf9dof.varPitch = createVariable('r', 2, "sf9pitch");
 // calculated heading from compas - based on magnetic north
 sf9dof.varCompas = createVariable('r', 2, "sf9compas");
 /// update rate
 sf9dof.varUpdRate = createVariable('r', 1, "sf9updateRate");
}

In this case there is read variables only, but exchange the 'r' with a 'w' and it becomes a write variable. An index to the created variables are returned, and they must be used, when the values should be updated (or the write variables read.

Update variables

An example that updates the sf9gyro and sf9updateRate variable could look like

...
setArray(sf9dof.varGyro, 3, g);
setVariable(sf9dof.varUpdRate, 0, updCnt/5);
...

The first sets all 3 elements if the gyro value, the next sets the one value only. The timestamp and update flag are set by the calls too.

You can read values from write variables by the call:

    int  getWriteVariable(int varidx, int value_index);

The varidx integer is the index from when the write variable was created, and the value_index is 0 if there is just one value, or the index to the value, if the variable is an array.

test the plugin

Use rhdtest to test the plugin.

The rhdtest could look something like:

  ******************* RHD Client V1.0 ***************************
  r: (1) tick[1]: (15712)
  r: (0) version[1]: (0)
  r: (1) sf9acc[3]: (23)(-3)(-288)
  r: (1) sf9gyro[3]: (373)(374)(385)
  r: (1) sf9mag[3]: (-80)(-320)(1061)                             
  r: (1) sf9roll[2]: (175)(433985)
  r: (1) sf9pitch[2]: (-179)(-403190)
  r: (1) sf9compas[2]: (-165)(-963756)
  r: (1) sf9updateRate[1]: (86)
  w: (0) steeringangleref[1]: (0)
  w: (0) armAxis[6]: (0)(0)(0)(233)(0)(0)
  w: (0) speedl[1]: (25)
  w: (0) speedr[1]: (0) 
  w: (0) reset[1]: (0)                            

  Host: mercury    Access: w    Database: 33 r / 8 w   Period: 0.033 s

  >> _

write variables can be set with a commands like:

>> set speedl=25
>> set armAxis[3]=233