NAME
nst_remote_var -- create unit to import variables
PROTOTYPE
unitptr nst_remote_var( char *pcInputs, char *pcOutputs, char *pcMethods, char *pcOptions, unitptr uHost)
ARGUMENTS
- char *pcInputs
- a (comma or space separated) list of names of variables (or variable groups) to be made accessible as input pins (or pin groups) of the unit. A semicolon (instead of a comma or space) will end the current and start a new input field.
- char *pcOutputs
- dito for the output side
- char *pcMethods
- a comma separated list of method names. The first list element will become the exec function. To omit an exec function, supply a minus sign as the first name.
- char *pcOptions
- Expects name to identify variable set, optionally followed (no commas! Separator is space!) by a server name. An optional -v or %v enables verbose mode. If no server name is given, the unit will ??? program.
- unitptr uHost
- host unit
SYNOPSIS:
Make selected C or C++ variables in a different process accessible
as input or output pins of a NST remote_var unit when the
processs has a user-written, very simple lookup routine.
The lookup routine is identified by a symbolic name and has to
follow a simple interface convention for 'publishing' a set
of variables in a type-safe way to NST. 'Publishing' is based
on a simple interface declaration string, containing a human-readable
C-style declaration of the to-be-exported variables (similar to
a prog_unit loadlib-interface).
A larger number of variables may be organized into several
smaller subsets, each with their own lookup routine. The
variable subsets may overlap, and the same application variable(s)
may even be 'published' under different symbolic names by
different lookup routines.
Besides variables, an remote_var unit can by the same mechanism
also import a parameterless function as its exec-method.
Additional parameterless functions can be bound as pin-less
method subunits, turning the remote_var unit into a class
container. Finally, the option to use a library mutex
facilitates working with multiple threads.
SERVER NAME AND COMMUNICATION PROTOCOL:
A server name a.b.c.d:portnr selects the TCP protocol with
server address a.b.c.d and port number portnr. Alternatively,
one may also use the form hostname:portnr.
Any other server name name selects the UNIX protocol via
a socket name (this restricts communication to within a
file system).
Remote server and local client must use the same representation
for int and float values (however, this is checked and an error
message issued when the check fails).
RETURN VALUE:
A pointer to the created unit or NULL in the case of an error.
INTERFACE OF CREATED UNIT:
For each variable that has been selected for input (output), a
corresponding input (output) pin. Input (output) variables are
selected by specifying their symbolic name as comma or semicolon-separated
elements of argument string pcInput ( pcOutput). Commas
separate variables as pins of the same field, semicolons
partition the pin sequence into fields.
EXECUTION OF CREATED UNIT:
Write all input variables and fetch all output variables.
If an exec method was specified for the unit, this method
is executed after all input variables were and before
any output variables are set.
If the definitions specify a lock(),unlock() function
pair (cf. below), the above is bracketed between a
pair of lock(),unlock() calls.
THE LOOKUP ROUTINE:
To make a number of variables accessible for an remote_var
unit, the exporting application or library has to export
a specially written lookup routine. Writing such a routine
is very simple and most easily explained along with an
Example: assume you wish to export the following variables
float a,b,*c; double u,v[3]; char ac[]="hello";
as a set named "mygroup". Then, the remote_var unit expects
the lookup routine to be named VARIABLES_mygroup or
VAR_mygroup (tried as a second option, provided only
for shortsighted linkers) and to have parameters
(int*,int) and a void* return value (in both cases,
only the 'base name' mygroup should be specified in pcOptions).
The call
VARIABLES_mygroup(NULL,-1)
must return a pointer to a (static!) description string
that informs the remote_var constructor routine about
the available variables. It differs from the variable's
C-style declaration mainly in that array dimensions specifiers
and initializers have been removed, i.e, in our example
it would read
"float a,b,*c; double u,v[]; char ac[];"
Additionally, any call
VARIABLES_mygroup(&dim,k)
must return the address and set dim to the dimension of
the k-th variable that is mentioned in the description
string (.dim needs only be set for non-scalar variables).
Information in the passed value of dim: if the call is in
the context of a read access, then dim<0.
You can take a non-negative dim>=0 as a hint that the
call is in the context of a write-access for dim elements.
You may (but need not!) resize the selected variable
accordingly. In either case, the return value must be
the dimension of the variable at the time when the routine
returns.
Therefore, a suitable implementation might look as
follows (assuming that DimOfC() is some available routine
that tells the current allocation dimension of the dynamic
variable *c):
void *VARIABLES_mygroup(int *dim, int k)
static char DefStr[]="float a,b,*c; double u,v[]; char ac[]";
switch (k) {
case -1: return DefStr;
case 0: return &a;
case 1: return &b;
case 2: *dim=DimOfC(); return c;
case 3: return &u;
case 4: *dim=3; return v;
case 5: *dim=sizeof(ac)/sizeof(char);
return ac;
}
The general pattern is:
- VARIABLES_xxxx(&dim,k) must return the address of
the k-th variable in the group. If the variable is
non-scalar, dim must be set to its dimension.
- VARIABLES_xxxx(NULL,0) must return a string holding
the definitions (arrays indicated with empty brackets)
of the variables: this permits the calling remote_var
unit to get for each variable a symbolic name, type
info and the positional index k in the group (which
is the position of the variable's definition in the
string).
Note that the latter operation is only done once when
the remote_var unit is constructed. After parsing the
string, it knows once and for all for each variable
the associated serial index k for fast access to the
address and dimension.
The difference between definitions "c[]" and "*c" is
that the former appears as a fixed dimension array
pin, the latter as a dynamic array pin (only in that
case, the variable may also become redimensioned on
the C/C++ side).
The definition string may also use the non-NST types
double and short. For vector variables, they
will be converted to float and int, resp.
Scalar variables (including char and byte) will
always appear as scalar float pins on the NST side,
with byte interpreted as unsigned char.
ADDITIONAL OPTIONS FOR THE LOOKUP ROUTINE:
The definition string may contain before each type
declarator the additional keywords const or
readonly (equivalent to const) in order to
forbid such variables to become selected for
input pins, thereby protecting them against any
change through an remote_var unit.
The somewhat rigid counting scheme for the k-values
can be made more flexible by inserting 'numbering anchors'
of the form #n: (also just n: suffices), which force
k-counting to continue with the value k=n for the
next variable. Note that any renumbering needs
corresponding changes for the case values in the
lookup-routine (and, as a consequence, never affects
the NST side). Using #n: to create generous
numbering gaps facilitates the subsequent introduction
of further definitions without changes to already
existing case-values in the lookup routine.
Note also that the names in the definition string need
not coincide with the C-names: the actual C-variables
for which the lookup routine returns their data can
be chosen at will (but, of course, fatal errors will
occur, if their type specifications and dimensions do
not match with associated specification in the description
string).
To facilitate import of entire groups of variables
by a single symbolic name, groups of variable
definitions may be enclosed in curly
brackes, with a group name prepended:
DefStr="float a,b; foodouble u,v[]; char ac[];"
This will permit to import the three variables
u,v[],ac[] under the single name of foo.
Note that introducing a group name does not change the
serial position index for the other items.
Additionally, the description string may contain
newlines for print formatting (useful with the -v option
of the remote_var unit that sends a copy of the description
string to stderr) and comments. These are introduced
with a '/' or '//', and are ended by the next single
'/' that is not on both sides adjacent to an alphanumeric
char, or the next newline.
With some of these features, our example could also
be written as
void *VARIABLES_mygroup(int *dim, int k) {
static char DefStr[]=
"float A,B, /two scalars/ *C; /a pointer \n"
"foo { #20: / start group + renumber \n"
" double U,V[];// NST converts to float \n"
" #10: const char AC[]; //some fixed text\n"
"} // end group foo";
"float A,B,*C; #20: double U,V[]; #10: char AC[]";
if (!dim) return DefStr;
else switch (k) {
case 0: return &a;
case 1: return &b;
case 2: *dim=DimOfC(); return c;
case 20: return &u;
case 21: *dim=3; return v;
case 10: *dim=1+strlen(ac); return ac;
}
with now capital letter names for the same variables,
and the possibility to import the sequence of the
three variables u,v,ac[] by the single name foo.
Note also that the initial call VARIABLES_mygroup(dim,-1)
that retrieves the definition string may also be used to
accomodate initializiation operations that may be needed,
e.g., to initialize mutex attributes etc.
If a group of variables of the same type
is known to lie contiguous in memory, one may
access them as a single vector, e.g.
float a,b,c[10];
can be accessed from the NST unit as a
single 12-dimensional vector named abcvec by
specifying the definition string as
DefStr[] = "#n: float abcvec[12]"
with a corresponding line
case n: dim=12; return &a;
This is also more efficient (1 lookup routine call
instead of 3).
Char* variables with a constant
initializer, such as
char *pc="hello";
should always be specified as const or readonly to
protect them against write access: the compiler may have put them
as read-only strings into memory. In this case,
an attempt by the NST unit to write into them
will cause a segmentation fault.
PROVIDING ACCESS TO PARAMETERLESS FUNCTIONS:
The definition string may also contain declarations
for parameterless functions, e.g.:
DefStr="float a,b; hello(),bar(); int x; .."
Such function definitions are recognized by a
pair of round brackets and the absence of a
type specifier. Function definitions are included
in the positional numbering of variables and the
lookup routine must simply call the
respective routine when it receives the associated
k-index as its second argument. I.e.
...
case k: return hello();
...
The pcMethods string of the remote_var unit
may then specify a (comma-separated) list of
such function names: the first element in the
list will become the exec-method of the created
unit (and will then be called after the input
pin values were set and before the output pin
values are set). Each further element will give
rise to a pinless method subunit with the
parameterless function as its exec method.
(if the created unit itself shall remain without
binding to a function, supply a minus sign for
the name of the first method).
In the library, the exported methods may use
the exported variables as their context. Thus,
a variable group with a set of parameterless
methods can act much like a general class
(of which only a single instance is possible,
however). Note that a more powerful interface
(at the expense of requiring a bit more
programming effort) to export classes into
NST is offered by the prog_unit loadlib interface).
PLACING INITIALIZATION/DEINITIALIZATION CODE:
A special status applies to parameterless functions of
name _init() or _fini(): if present, _init() ( _fini())
will be called before (after) any of
the exec unit operations (fetching input pin values, calling
the exec method, setting output pin values) are carried out.
This happens automatically on each exec_unit call of the
remote_var unit. It may be useful, e.g., to bracket the
variable access in the server in a pair of lock/unlock
calls to a mutex in order to avoid concurrent access
in the server.
When a var_input unit is created, its first
operation is to retrieve the definition string
with a call
VARIABLES_xxxx(&dim,-1);
This first call will pass in the dim parameter a unique
positive integer that identifies the calling remote_var
unit instance. Later, when the remote_var unit is
removed, there will be a second call, but this time
with dim set to the negative id value. This
permits to invoke instance-specific (or general,
by just looking at the sign of dim) initialization
or de-initialization operations if such are needed.
SUMMARY OF VARIABLE CALLS:
For convenience, here is a short list of the different
calls of the VARIABLES_xxxx routine that the implementation
must be prepared of:
- VARIABLES_xxxx(&dim,-1):
- dim>0 : initialization call.
Must return pointer to static copy of variable declaration
string. Optionally: carry out initialization operations,
may use value of dim as id of the client-side remote_var
unit.
- VARIABLES_xxxx(&dim,-1):
- dim<0 : de-initialization call.
Allows to carry out de-initialization operations in
server. Value -dim will be the same id of the cliend-side
remote_var unit as in the initialization call.
Return NULL pointer.
- VARIABLES_xxxx(&dim,k):
- (k non-negative) : request of
address of variable defined with serial position k in
definition string. If dim>0: this is a read-access,
expecting dim elements to read. If the actual number
of available elements differs, set dim to the actual
value. If dim<0: this is a write-access, expecting
(after having received the address) to deposit -dim>0
elements in the variable. If the actual number of available
locations is smaller, optionally re-allocate the variable
before returning its (new!) address. In all these cases:
replace dim by the actual (non-negative!) number of
elements that are available for access, and return their
starting address.
- VARIABLES_xxxx(NULL,k):
- (k non-negative) : request
to call parameterless function defined at serial
position k in the definition string. Return
value unused.
PROVIDING INFORMATION ABOUT VARIABLE SETS:
A library package may implement a variable
char VARINDEX_sharedlibname[] =
"name1 name2 ... namek";
(or char VARINDEX[]="..." in main())
to provide lookup information about exported
variable sets (or anything the package wishes
to inform about): the string itself may be arbitrarily
formatted, it will just be printed when a
remote_var unit is created with -v or %V
given as option alone, or in response to certain
error situations.
SEE ALSO:
The (somewhat more complex) prog_unit loadlib
interface permits to map entire classes,
including class methods.
FILE
/amnt/loge/users/nistaff02/nistaff/rhaschke/nst7/man/../o.linux//../remotesrc/nst_remote.c