Variables: Difference between revisions

From Rsewiki
Line 75: Line 75:


  >> var foo.kar copy
  >> var foo.kar copy
  <var name="foo.kar" typ="m2" size="52" value="1 0 0; 0 1 0; 0.67 0 0; 0.1 0.2 0.3; 0 0 0; 0 0 0; 0 0 0; 0 0 0; 0 0 0; 0 0 0; 0 0 0; 0 0 0; 0 0 0; 0 0 0; 0 0 0; 0 0 0; 0 0 0"/>
  <var name="foo.kar" typ="m2" size="52" value="1 0 0; 0 1 0; 0.67 0 0; 0.1 0.2 0.3;  
 
0 0 0; 0 0 0; 0 0 0; 0 0 0; 0 0 0; 0 0 0; 0 0 0; 0 0 0; 0 0 0; 0 0 0; 0 0 0; 0 0 0;
0 0 0"/>


===Define variables and methods in plug-in===
===Define variables and methods in plug-in===

Revision as of 10:42, 28 March 2011

Most resource modules will include some configuration or result variables, that should be available form other modules or from an external interface - a configuration file or a operator interface.

Variable Pool

The var-pool is a method of sharing variable values between different modules. This figure is used to illustrate that the 'RoadDrive' module needs data from 'LaserRoad' for road data and access to the 'avoid' module for obstacle avoidance services, and finally to the robot real time control server. Each module is compiled without need for any header files from the other modules.

This inter module communication is handled by the var-pool function inherited into all resources.

The var-pool (loaded by module load=var) is the variable root. Each resource act as a structure and the local variables in each resource belong to this structure. so:

smr.speed

is defined by the resource 'smr' as a variable with the name 'speed'.

Var commands

The var plugin (module load=var) allow creation of any variable anywhere (almost). After loading a module, a structure is (usually) created with some parameter or status values. These can be set or manipulated by the "var" command.

e.g.:

module load="var"
module load="aurule.so.0"
var global.period=0.1               desc="some period time in seconds"
var global.w ="2*pi/global.period"  desc="frequency in radians/sec"
var.global.maxSpeed=15*1000/3600    desc="maksimum speed (default 15km/h)"

The variable on the left hand side is created or modified with the value on the right hand side. The value of "pi" is defined in a math structure loaded by the aurule.so.0 plug-in.

The above example produces a value list of:

>> var global list
<help subject="var list" name="global">
Description:                  new
  period=0.1                 period time
  w=62.8318530718            frequency in radians
  maxSpeed=4.166666666667    maksimum speed (default 15km/h)
(H: has time series, L: is logged, R: replay)
</help>

Arrays and matrices can be generated as:

var foo.bar="1 2 3 4 5"      (array with 5 values)
var foo.mar="1 2 3; 4 5 6"   (matrix 2x3 - 2 rows and 3 columns)
var foo.mar=unit             (changes matrix mar to a unit matrix)
var foo.kar=foo.mar          (make kar a copy of mar)
var foo.kar size="17,3"      (change to 17 rows and 3 columns)
var foo.kar[6]=0.67          (changes element index 6, i.e. row 3 column 1)
var foo.kar[9]="0.1 0.2 0.3" (changes elements from index 9, i.e. row 4)

The above produces the following values

>> var foo list
<help subject="var list" name="foo">
Description:                  new
  bar=1 2 3 4 5              array with 5 elements
  mar="2x3"                  not array with 5 elements
       1       0       0 ;
       0       1       0
  kar="17x3"                   -
       1       0       0 ;
       0       1       0 ;
       0.67    0       0 ;
       0.1     0.2     0.3 ;
       0       0       0 ;
       0       0       0 ;
       0       0       0 ;
       0       0       0 ;
       ...
(H: has time series, L: is logged, R: replay)
</help>

The list command shows matrices truncated to maximum 8x7 values, the full set of values can be shown with the copy command:

>> var foo.kar copy

Define variables and methods in plug-in

A resources may define a number of local variables and functions, like this roadDrive resource:

void UResRoadDrive::createBaseVar()
{
 //
 addVar("version", getResVersion() /   100.0,      "d", "Resource version");
 varUpdateTime = addVar("updateTime",    0.0,      "t", "Last time the road drive command was calles");
 varEdgeDist   = addVar("edgeDist",      0.35,     "d", "Nominel distance to road edge");
 varFwdDist    = addVar("forwardDist",   6.0,      "d", "Exit-pose distance from robot");
 varEdge       = addVar("edge",          0.0,      "d", "Reference edge 0=left, 1=top, 2 = right");
 varTgtPose    = addVar("tgtX",   "5.0 0.0 0.0", "pose", "Array with target position 
                                                         used in obstacle avoidance (set by call)");
 varQ          = addVar("q", "0.2 0 0; 0 0.2 0; 0 0 1", "m2", "(r) a 3x3 matrix");
 varCtrlName   = addVar("driver",   "roaddriver" , "s", "A string variable used to flag the module in control");
 ...
 // and methods
 addMethod("left",   "d",  "Follow left side at this distance");
 addMethod("right",  "d",  "Follow left right at this distance");
 addMethod("top",    "d",  "Follow road center at this distance");
 addMethod("target", "sd", "get target pose with this reference, the 's' (string)
                            parameter may be either 'left', 'right', or 'top', 
                            the 'd' parameter is the desired distance from the 
                            reference line (positive is left)");
 ...
}

Use variable update as an event

A variable structure may be used to trigger events. An event may be defined by a client with a command like:

>> varPush struct=smr cmd="var smr copy"

If this is the case a flag will be set if one (or more) of the variables defined in the 'smr' resource is updated. The command will then be executed by the server thread at first opportunity.


Access variables from plug-in

Variables are added to the system by the 'addVar' call, giving a variable name, an initial value, a type ('d' for double) and a description of the variable (the description is a help to the operator, when inspecting or setting values).

The addVar(.) method returns a pointer to a UVariable type, this is used when locally accessing the variable, e.g.:

d = varFwdDist->getValue();
x = varTgtPose->getValue(0);           // get first value in the pose array 
varTgtPose->setValued(-0.3, 1);        // Set the pose y value (index 1) 
varTgtPose->setValued("2.2 -0.3 0.0"); // Set all values in one call - note that the values are set as a string.
varTgtPose->setPose(pPose);            // Set all values from a pointer to a UPose structure 

The implemented data types are

  • Data type "d" an array of doubles (may have one element only, and then used without an index)
  • Data type "pose" is an array of 3 double values (index 0, 1, 2 for x, y, th)
  • Data type "3D" is an array of 3 values (index 0, 1, 2 for X, Y, and Z)
  • Data type "rot" is an array of 3 values describing a 3D rotation, index 0 is Omega (X-axis), index 1 is Phi (y-axis) and index 2 is Kappa (z-axis)
  • Data type "dq" is two values is describing a data value and a quality.
  • Data type "t" A time value (implemented as a double).
  • Data type "s" is a string variable.

Access variables by name

Varables in other resource modules are accessed using their name, like:

vp = getVarPool();  // vp is of the type UVarPool *
isOK  = vp->getValue("smr.speed", &vel);
isOK &= vp->getValue("odopose.pose[1]", &y);  // gets the current odometry pose Y-value

The value name is using a dot-notation for the module owner - the resource name - followed by a dot and the variable name. If variable is an array, then an index may be added. If the variable exist - both resource and variable - the function returns 'true' and the double sized value is returned at the adress supplied as the second parameter. Sub-structures may be created locally with a 'vp->addStructLocal("name", "help text")' if needed. Sub structures can not be arrays.

Methods

Methods are defined by a name and a parameter list. They must be announced to the var-pool by an 'addMethod' call, like these from above:

 addMethod("left",   "d",  "Follow left side at this distance");
 addMethod("right",  "d",  "Follow right side at this distance");
 addMethod("top",    "d",  "Follow road center at this distance");
 addMethod("target", "sd", "get target pose with this reference, the 's' parameter may 
                            be either 'left', 'right', or 'top', the 'd' parameter is 
                            the desired distance from the reference line (positive is left)");

The parameters are the object implementing the call - typically 'this' will do, the name, the parameter list and a textual description. The parameter list may hold a combination of 'd' (double), 's' (string) and 'c' (class) as in-parameters.

Implement method

The implementation of the method must implemented in the resource function named methodCall:

bool UResRoadDrive::methodCall(const char * name, const char * paramOrder, char ** strings, const double * pars,
                      double * value, UDataBase ** returnStruct, int * returnStructCnt)
{
 bool result = true;
 if ((strcasecmp(name, "road") == 0) and (strcmp(paramOrder, "sd") == 0))
   *value = driveRoad(strings[0], pars[0]);
 else if ((strcasecmp(name, "left") == 0) and (strcmp(paramOrder, "d") == 0))
   *value = driveSide(0, pars[0], 0);
 else if ((strcasecmp(name, "top") == 0) and (strcmp(paramOrder, "d") == 0))
   *value = driveSide(1, pars[0], 0);
 else if ((strcasecmp(name, "right") == 0) and (strcmp(paramOrder, "d") == 0))
   *value = driveSide(2, pars[0], 0);
 else if (...)
   ...
 else
   result = false;
 return result;
}

The function is called by the var-pool module, and the function must determine which of the defined methods that are called. If the method is not implemented, then the function should return 'false'. As can be seen from the code, the function names are not intended to be case sensitive, and the parameter list is tested too, so the same method name may be used with different parameter lists. The parameters in the call are, after the name and the parameter list, the three types of parameters and the return value, in the following order:

  • 'strings' a string array with as many elements as there is s' parameters in the parameter list.
  • 'pars' a double array with as many elements as there is 'd' parameters in the parameter list.
  • 'value' is where the primary result of the function is to be returned. If the method result is expressed in a double then here is the place to return the value. If the reply is a more complex, then this value could be used to flag success (1.0) or failure (0.0).
  • 'returnStruct' is an array of pointers to a structure buffer. This may also be used as an input parameter, with as many pointers in the array as there is 'c' parameters in the parameter list.
  • 'returnStructCnt' is a count of the number of available pointers in the 'returnStruct' array. On return this value must be changed to the actual number of class values returned. If the pointer array actually points at structures, then the method is expected to copy the relevant data to the provided buffer. If the pointer array (its first element) points to NULL, then the method is expected to return a pointer to a (one or more) structure in its own memory space. The receiver is then responsible for using the data before it is changed.

The return structures must be defined as a class inherited from the base class 'UDataBase'. The following types are p.t. defined in this way:

  • UDataDouble - holds just a double value
  • UDataString - holds a c-string
  • UPosition - holds an x,y,z value
  • UPosRot - a 3D pose of e.g. a camera or a laser scanner on a robot
  • ULine - holds a line (3D with 2D support) definition (3D point and 3D (unit) vector)
  • ULineSegment - same as line, but with a length.
  • UMatrix - the basic matrix class
  • UMatrix4 - a 4x4 (or less) matrix for 2 or 3D calculations.
  • UPolygon - a sequence of UPositions to form a poly-line or a polygon.
  • UObstacleGroup - a group of UPolygon obstacles.
  • UPose - a 2D pose (x,y,h) - and from this UPoseT (with time) UPoseVel (with velocity)
  • UManSeq - a sequence of manoeuvres to get from one pose to another

Method call

A call to a method in another module could look like this:

{
 const int MPC = 4; // parameter count
 double pars[MPC], v;
 UManSeq * manNew = NULL;
 bool isOK;
 int n = 1;
 ...
 pars[0] = exitPose.x;
 pars[1] = exitPose.y;
 pars[2] = exitPose.h;
 pars[3] = vel;
 isOK = call("avoid.getAvoidPath", "dddd", NULL, pars, &v, (UDataBase**) &manNew, &n);
 if (isOK and v == 1.0)
   // supply manoeuvre sequence to smr interface resource
   isOK = call("smrCtl.setPlan", "sc", &driver, NULL, &v, (UDataBase**) &manNew, &n);
 ...
}

Where the first call just supplies an double array with 4 elements and expect a pointer to a manoeuvre sequence in return. If the call was a success the return value 'v' will be 1.0, and then the received manoeuvre sequence is send to the smr interface module. This call has a string and a class as parameters. The unused parameter pointers are set to NULL.