v5.0 / 23 of 23 / 01 sep 99 / gvg
* VEE is an excellent language for many tasks, but in some cases a customer may want to build an application in another language, and yet still make use of VEE.
As of VEE 3.2, a new feature named "Callable VEE" is available that allows a C programmer to call VEE UserFunctions from his or her C programs. Any other language that can access C routines can also use Callable VEE, though information may not be readily avaible from HP on how this could be done.
This chapter provides a description of Callable VEE.
* The Callable VEE capability is based on the earlier VEE-to-VEE Remote
Procedure Call (RPC) capability also developed for VEE, which allows one VEE
to access UserFunctions of another VEE on the same machine, or on a remote
machine on a network. Callable VEE is a modification of this capability to
allow a C-to-VEE RPC capability.
In either case -- VEE-to-VEE RPC, or C-to-VEE RPC -- the scheme relies on
interprocess communications (IPC) based on TCP/IP networking protocols and
requires that the host support them. In both cases, the program calling the
VEE UserFunctions, the "client", can access them on either the same, or a
remote system, the "server".
Note that, since VEE is still an interpreted language, the server system will
still need to have VEE present and accessible to run the UserFunctions. The
UserFunctions cannot be executed on their own, they have to be organized into
a UserLibrary that the resident VEE can load and execute.
When VEE is executed on the remote system, it will appear on the display with
no tool bar or menu. If the UserFunctions generate displays, they will
appear, but otherwise VEE will remain featureless. You can, however, set a
debug mode in Callable VEE to allow you to single-step the UserFunction from
VEE on the remote system.
* The essential tools provided with VEE to support Callable VEE are:
On HP-UX systems, "veesm" is automatically run by the "inet" daemon process.
On a PC, it must be explicitly put into the Windows Startup Group. It will
allow a remote client to bring up VEE as a server, and VEE will take it from
there.
This library supports two "Application Program Interfaces", or APIs -- one to
set up and control the RPC between the C program and VEE, which performs the
actions:
-- and one that performs conversions between C and VEE data types. You can
find all the prototypes for each API in two header files provided with VEE,
both stored in the "include" directory of the VEE installation and having the
names "veeRPC.h" and "veeData.h".
The following sections explain the VEE RPC and VEE DATA APIs in detail. Note
that there is a matched pair of (very useful) sample programs to demonstrate
Callable VEE, "callVEE.c" and "callVEE.vee"; both can be found in the "new"
directory under "examples" in the VEE installation.
* As noted, the VEE RPC API handles setting up, maintaining, and closing the
connection between the C client program and the VEE server.
* First, a few background details. The API's routines will use one of three
handles in their operation:
* LOADING & UNLOADING A SERVER: Now on to the most essential API functions,
the two that load and unload a VEE server. To load a VEE server you use:
The "display" argument specifies a remote display (using a network address in
text, "babylon:0,0", or numeric, "15.11.55.101:0,0", form) on a networked
X-Windows system. The "geometry" argument specifies the VEE window size and
placement. For example, "800x500+0-0" puts an 800x500 VEE window in the
lower-left corner of the display.
The "aTimeoutInSeconds" argument gives the number of seconds to wait on the
load. This value will be used for all later calls in the session unless
changed by "vrpcSetTimeout()". The "flags" argument is not normally used.
You can, however, set it to the value VEERPC_CREATE_NEW to load a new copy of
VEE on a server instead of using one already loaded.
To unload a VEE server you use:
* LOADING & UNLOADING A LIBRARY: Once you have loaded the server, you then
need to load a User Library. This is done with:
Once loaded, you can specify either normal or debugging execution mode for
the library with:
You can similarly unload the User Library with:
* SELECTING USERFUNCTIONS: Now that you are connected to the server and have
a User Library loaded, you can select UserFunctions. The first thing you may
want to do is determine what functions are in the UserLibrary with:
You can then get a function handle with:
Now, if you want to get information on the function, you can get it with:
To call a UserFunction, you invoke:
Of course, since most UserFunctions will return sometime, you will want to
get a value back, and for that you use:
Finally, you can call and receive in a single function using:
* UTILITY FUNCTIONS: Other utility functions in the VEE RPC API include:
You can set the default C client behavior for receiving data with:
Finally, you can query the revision number of the remote "veesm" with:
* ERROR CODES: Error codes defined for the VEEPRC API include:
* As shown in the previous section, performing a Call or Receive with a
UserFunction requires handling data in the VEE Data Container (VDC) format,
which is a set of data structures required by VEE for its internal operation.
Communicating with VEE from your C program requires an ability to translate
between VDCs and conventional C data types, and the VEEDATA API provides this
ability (and a few others).
* DATA TYPES, SHAPES, & MAPPINGS: The fundamental VDC types are listed in
the "veeData.h" header file as:
* SCALAR DATA HANDLING: Anyway, to convert scalars from C data to VDCs, you
use the following functions:
You can change the values in the VDCs with another set of routines:
Having created a scalar VDC or returned one from a function, you can get the
C data type out of it with yet another set of routines:
The exceptions are the "vdcGetStringScalarValue( )" function, with returns a
string directly from the function (or a null string if something goes wrong),
and the "vdcGetCoordScalarValue()" function, which returns a pointer to an
array of N-dimensional coordinate data (with N returned as an argument).
Finally, you can interrogate coordinate types for their number of coordinate
dimensions or set the coordinate dimensions to new values if desired:
You can convert back from VDCs to C arrays with:
Once created, you can also check interrogate or manipulate the arrays:
[23.1] CALLABLE VEE OVERVIEW
[22.2] VEE RPC API
[23.3] VEEDATA API
[23.1] CALLABLE VEE OVERVIEW
[22.2] VEE RPC API
typedef void* VRPC_SERVICE; // Handle to VEE server.
typedef void* VRPC_LIBRARY; // Handle to VEE UserFunction Library.
typedef void* VRPC_FUNCTION; // Handle to VEE UserFunction.
The API calls are organized as described in the following subsections.
VRPC_SERVICE VEEAPI vrpcCreateService( char *hostName,
char *display,
char *geometry,
double aTimeoutInSeconds,
unsigned long flags );
This function loads a VEE server on the host given by
"hostName". The "hostName" can be in text,
"mycomputer@lvld.agilent.com", or in numeric,
"15.11.55.105", form. It returns, not surprisingly, a server
handle. You get a NULL (effectively a zero) back if something goes
wrong. You can then get the precise error information with
"veeGetErrorNumber()" and "veeGetErrorString()" as
outlined in the next section.
VRPC_SERVICE VEEAPI vrpcDeleteService( VRPC_SERVICE aService );
The only argument is the server handle obtained when you originally loaded
the server. You get a NULL pointer back if all is OK, otherwise you get a
non-null pointer.
VRPC_LIBRARY VEEAPI vrpcLoadLibrary( VRPC_SERVICE aService,
char *FunctionName );
This function accepts as arguments a server handle and the pathname of a
User Library specified by "FunctionName". It returns a library handle.
If it fails, you get a NULL back.
void VEEAPI vrpcSetExecutionMode( VRPC_LIBRARY aLibrary,
unsigned long executionMode );
In this function, you specify the handle for the library and an
"executionMode" flag, which can be set to VRPC_DEBUG_EXECUTION, which
specifies single-stepping through the UserFunction on the target system, and
then set back to the default VRPC_NORMAL_EXECUTION. This returns a 0 if
successful, an error code if not.
VRPC_LIBRARY VEEAPI vrpcUnLoadLibrary( VRPC_LIBRARY aLibrary );
All you need for arguments is the library handle.
char** VEEAPI vrpcGetFunctionNames( VRPC_LIBRARY aLibrary,
long *numberOfFunctions );
This accepts a library handle as an argument. It returns a pointer to an
array of null-terminated strings giving the function names directly, and the
"numberOfFunctions" in the library as a argument. You get a NULL pointer
back if an error occurs. The string array exists in your process space.
VRPC_FUNCTION VEEAPI vrpcFindFunction( VRPC_LIBRARY aLibrary,
char *aFunctionName );
You specify the library handle and a string giving the UserFunction name as
arguments, and get back the function handle, or a NULL if something goes
wrong.
struct VRPC_FUNC_INFO* VEEAPI vrpcFunctionInfo( VRPC_FUNCTION aFunction );
This returns a data structure of the form:
typedef struct VRPC_FUNC_INFO
{
char *functionName; // Name of function.
long numArguments; // Number of input pins on function.
enum veeType *argumentTypes; // List of argument types.
veeShape *argumentShapes; // List of argument shapes.
long numResults; // Number of output pins on function.
enum veeType *resultTypes; // List of output types.
veeShape *resultShapes; // List of output shapes.
};
-- or a NULL if something goes wrong. The memory for this is taken up in
your process space, so if you want to get rid of it you use:
struct VRPC_FUNC_INFO* VEEAPI
vrpcFreeFunctionInfo( struct VRPC_FUNC_INFO *funcinfo );
* CALLING USERFUNCTIONS: Now that you have waded through all of this. You
can now actually call a UserFunction!
long VEEAPI vrpcCall( VRPC_FUNCTION aFunction,
VDC *arguments );
-- where you specify a function handle and an input array of VEE Data
Containers (VDC). Handling VDCs is the function of the VEE DATA API and is
covered in the next section. This function does not "block", it returns
immediately, whether it worked on not. It returns 0 if all is OK, and an
error code if not.
VDC* VEEAPI vrpcReceive( VRPC_FUNCTION aFunction,
unsigned long waitMode );
You specify a function handle and a "waitMode" flag, which can have one of
three values:
VRPC_NO_WAITING The call returns immediately with or without results.
VRPC_WAIT_SLEEPING Wait for data until timeout (server sleeps).
VRPC_WAIT_SPINNING Wait for data until timeout (server busy).
If the function fails, a NULL is returned.
VDC* VEEAPI vrpcCallAndReceive( VRPC_FUNCTION aFunction,
VDC *arguments );
This function blocks, waiting for the function to complete or until a timeout
occurs.
long VEEAPI vrpcSetTimeout( VRPC_SERVICE aService,
double aTimeoutInSeconds );
This function allows you to change the timeout. You specify a server handle
and the timeout in seconds. You get back a zero if all is OK, and an error
code if all is not OK.
long VEEAPI vrpcSetBehavior( VRPC_SERVICE aService,
unsigned long flags );
You specify a server handle and the flag, and get back 0 or an error code.
The flags are as follows:
VRPC_WAIT_SLEEPING Wait for data until timeout (client sleeps).
VRPC_WAIT_SPINNING Wait for data until timeout (client busy).
You can also OR in a flag, VRPC_BUFFER_EXPAND, to specify that the C client
will allocate and retain larger buffers in response to increasing sizes of
data returned from the server.
long VEEAPI vrpcGetServerVersion( VRPC_SERVICE aService );
You give this a server handle and get back either a revcode or a 0 (if you
have an error).
Returned when a connection to the VEE server cannot be made:
850: eUnknownHost Host name or IP address unresolvable.
851: eNoServiceManager Veesm cannot be found on server host.
861: eServiceManagerTO Service manager timed-out.
863: eServiceNotFound Unable to find VEE service.
864: eServiceNotStarted Unable to start VEE service.
866: eConnectRefused Connection to veesm or inetd refused.
868: eFailedSecurity Failed security check on UNIX.
Fatal errors that occur after connection to a VEE server
(means the connection has been terminated):
852: eHostDown VEE server host down.
853: eConnectTimedOut Connection has timed out.
855: eConnectBroken Connection has broken.
Errors that reflect an internal non-fatal state within the service:
865: eSomeInternalError An non-fatal internal error.
869: eVeeServiceError Error within the user function.
870: eWouldBlock Returned for non-blocking RPC.
871: eDebugTermination User pressed stop during debug session.
Error returned by a RPC Function call:
851: eInvalidArgument Invalid argument.
[23.3] VEEDATA API
enum veeType
{
VEE_LONG, // 32-bit signed integer (no 16-bit INTs in VEE!).
VEE_DOUBLE, // IEEE 754 64-bit floating-point number.
VEE_COMPLEX, // Complex number -- 2 doubles in rectangular form.
VEE_PCOMPLEX, // Complex number -- 2 doubles in polar form.
VEE_STRING, // 8-bit ASCII null-terminated string.
VEE_NIL, // Empty container returned by function call.
VEE_COORD, // 2 or more doubles give XY or XYZ or ... data.
VEE_ENUM, // An ordered list of strings.
VEE_RECORD, // VEE record-structures data.
VEE_WAVEFORM, // A 1D array of VEE_DOUBLE with a time mapping.
VEE_SPECTRUM // A 1D array of VEE_PCOMPLEX with a time mapping.
};
For convenience, the "veeData.h" file defines C data types for translation
with VEE data types:
typedef short int16;
typedef long int32;
typedef struct {double rval, ival;} veeComplex;
typedef struct {double mag, phase;} veePComplex;
typedef struct {double xval, yval;} vee2DCoord;
typedef struct {double xval, yval, zval;} vee3DCoord;
typedef void veeDataContainer;
typedef veeDataContainer* VDC;
The data types can also have a specified number of dimensions, or "numDims",
given by:
enum _veeShape
{
VEE_SHAPE_SCALAR, // Just a single data element.
VEE_SHAPE_ARRAY1D, // A one-dimensional array.
VEE_SHAPE_ARRAY2D, // A two-dimensional array.
VEE_SHAPE_ARRAY3D, // A three-dimensional array.
VEE_SHAPE_ARRAY, // An array with from 4 to 10 dimensions.
VEE_SHAPE_ANY // Placeholder for undefined shape.
};
Arrays can be "mapped". Normally they aren't, but the VEE_WAVEFORM and
VEE_SPECTRUM data types are mapped types where the array elements correspond
to time intervals. Mappings are given by:
enum veeMapType
{
VEE_MAPPING_NONE, // No mapping.
VEE_MAPPING_LINEAR, // Linear mapping.
VEE_MAPPING_LOG // Log mapping.
};
Normally you don't need to worry about this.
VDC VEEAPI vdcCreateLongScalar( int32 aLong );
VDC VEEAPI vdcCreateDoubleScalar( double aReal );
VDC VEEAPI vdcCreateStringScalar( char *aString );
VDC VEEAPI vdcCreateComplexScalar( double realPart,
double imaginaryPart);
VDC VEEAPI vdcCreatePComplexScalar( double magnitude,
double phase );
VDC VEEAPI vdcCreate2DCoordScalar( double xval,
double yval);
VDC VEEAPI vdcCreate3DCoordScalar( double xval,
double yval,
double zval );
VDC VEEAPI vdcCreateCoordScalar( int16 fieldCount,
double *values );
Given the definitions of VEE data types, these functions should be
self-explanatory. They all return a pointer to a VDC, or a NULL if they
fail. There are of course no scalars of VEE_WAVEFORM or VEE_SPECTRUM types
as they are always 1D arrays by definition.
int32 VEEAPI vdcSetLongScalar( VDC aVD,
int32 aLong );
int32 VEEAPI vdcSetDoubleScalar( VDC aVD,
double aReal );
int32 VEEAPI vdcSetStringScalar( VDC aVD,
char *aStr );
int32 VEEAPI vdcSetComplexScalar( VDC aVD,
double realPart,
double imaginaryPart );
int32 VEEAPI vdcSetPComplexScalar( VDC aVD,
double magnitude,
double phase );
int32 VEEAPI vdcSet2DCoordScalar( VDC aVD,
double xval,
double yval );
int32 VEEAPI vdcSet3DCoordScalar( VDC aVD,
double xval,
double yval,
double zval );
int32 VEEAPI vdcSetCoordScalar( VDC aVD,
int16 aFieldCount,
double* values );
As above, these functions return either 0 or an error code.
int32 VEEAPI vdcGetLongScalarValue( VDC aVD,
int32 *aLong );
int32 VEEAPI vdcGetDoubleScalarValue( VDC aVD,
double *aReal );
char* VEEAPI vdcGetStringScalarValue( VDC aVD );
int32 VEEAPI vdcGetComplexScalarValue( VDC aVD,
veeComplex *aComplex );
int32 VEEAPI vdcGetPComplexScalarValue( VDC aVD,
veePComplex *aPComplex );
int32 VEEAPI vdcGet2DCoordScalarValue( VDC aVD,
vee2DCoord *aCoord );
int32 VEEAPI vdcGet3DCoordScalarValue( VDC aVD,
vee3DCoord *aCoord );
double* VEEAPI vdcGetCoordScalarValue( VDC aVD,
int16 *aFieldCount );
In general, these functions take the data out of the first argument, a VDC,
and put it into the second, with is a C variable (with some types as defined
at the beginning of this section). They return 0 if no error and an error
code if there is an error.
int16 VEEAPI vdcNumCoordDims( VDC aVD );
int32 VEEAPI vdcCoordSetNumCoordDims( VDC, int16 );
* ARRAY DATA HANDLING: You create VDC arrays with the following set of
functions:
VDC VEEAPI vdcCreateLong1DArray( int32 numpts,
int32 *values );
VDC VEEAPI vdcCreateString1DArray( int32 numpts,
char **strings );
VDC VEEAPI vdcCreateDouble1DArray( int32 numpts,
double *values );
VDC VEEAPI vdcCreateComplex1DArray( int32 numpts,
veeComplex *values );
VDC VEEAPI vdcCreatePComplex1DArray( int32 numpts,
veePComplex *values );
VDC VEEAPI vdcCreate2DCoord1DArray( int32 numpts,
vee2DCoord *values );
VDC VEEAPI vdcCreate3DCoord1DArray( int32 numpts,
vee3DCoord *values );
VDC VEEAPI vdcCreateCoord1DArray( int32 numpts,
int16 aFieldCount,
double *values );
VDC VEEAPI vdcCreateWaveform( int32 numpts,
double from,
double thru,
VMT mapType,
double *data );
VDC VEEAPI vdcCreateSpectrum( int32 numpts,
double from,
double thru,
VMT mapType,
veePComplex *data );
These are straightforward. You specify an array size, any additional data
needed to represent the array (such as mapping data for VEE_WAVEFORM and
VEE_SPECTRUM types), and the array data, and get back a VDC, or a NULL if
something goes wrong.
int32* VEEAPI vdcGetLong1DArray( VDC aVD,
int32 *numpts );
double* VEEAPI vdcGetDouble1DArray( VDC aVD,
int32 *numpts );
char** VEEAPI vdcGetString1DArray( VDC aVD,
int32 *numpts );
veeComplex* VEEAPI vdcGetComplex1DArray( VDC aVD,
int32 *numpts );
veePComplex* VEEAPI vdcGetPComplex1DArray( VDC aVD,
int32 *numpts );
vee2DCoord* VEEAPI vdcGet2DCoord1DArray( VDC aVD,
int32 *numpts );
vee3DCoord* VEEAPI vdcGet3DCoord1DArray( VDC aVD,
int32 *numpts );
double* VEEAPI vdcGetCoord1DArray( VDC aVD,
int32 *numpts,
int16 *aFieldCount );
double* VEEAPI vdcGetWaveform( VDC aVD,
int32 *numpts,
double *from,
double *thru,
VMT *maptype );
veePComplex* VEEAPI vdcGetSpectrum( VDC aVD,
int32 *numpts,
double *from,
double *thru,
VMT *maptype );
These are also straightforward. They take a VDC, return a pointer to the
array of data directly, and return the size of the array (or any other
relevant information) as arguments.
int32 VEEAPI vdcSetNumDims( VDC,
int16 );
int16 VEEAPI vdcGetNumDims( VDC );
int32 VEEAPI vdcSetDimSizes( VDC,
int32* );
int32* VEEAPI vdcGetDimSizes( VDC );
int32 VEEAPI vdcCurNumElements( VDC );
* ENUM TYPES: VEE enumerated types, as noted, are ordered lists of strings;
they are handled by the following set of routines:
VDC VEEAPI vdcCreateEnumScalar( int16 numberOfPairs );
Creates an empty VEE_ENUM structure with the given number of
string-ordinal pairs, and returns a NULL VDC on error.
int32 VEEAPI vdcEnumAddEnumPair( VDC aVD,
char* aString,
int32 aValue );
Places an enumerated pair in the defined VEE ENUM structure, returns the
updated structure, and returns 0 or an error code.
int32 VEEAPI vdcEnumDeleteEnumPairWithOrdinal( VDC aVD,
int32 anOrd );
Deletes an enumerated pair as given by the ordinal value argument, and
returns 0 or an error code.
int32 VEEAPI vdcSetEnumScalar( VDC aVD,
int32 anOrdinal );
Sets an ordinal value for use by other "vdcEnum" routines. Returns 0 or
an error code.
int32 VEEAPI vdcEnumDeleteEnumPairWithStr( VDC aVD,
char* aString );
Places a string in the VEE ENUM structure with the ordinal value assigned
by "vdcSetEnumScalar()".
int32 VEEAPI vdcGetEnumOrdinal( VDC aVD );
Returns the current ordinal number selection assigned by
"vdcSetEnumScalar()".
char* VEEAPI vdcGetEnumString( VDC aVD );
Returns the string associated with the current ordinal number, or a NULL
string if something goes wrong.
* MAPPING FUNCTIONS: If you are so inclined, the VEE DATA API allows you to
manipulate the mappings of arrays:
int32 VEEAPI vdcAtDimPutLowerLimit( VDC aVD,
int16 aDim,
double aValue );
// Specify mapping for lower limit.
int32 VEEAPI vdcAtDimPutUpperLimit( VDC aVD,
int16 aDim,
double aValue );
// Specify mapping for upper limit.
int32 VEEAPI vdcAtDimPutRange( VDC aVD,
int16 aDim,
double lowerLimit,
double upperLimit );
// Combines "vdcAtDimPutLowerLimit" & "vdcAtDimPutUpperLimit".
int32 VEEAPI vdcAtDimPutMapping( VDC aVD,
int16 aDim,
VMT aMapping );
// Set the mapping between limits as defined above.
int32 VEEAPI vdcMakeMappingsSame( VDC VD1,
VDC VD2 );
// Map two containers in the same way.
int32 VEEAPI vdcUnMap( VDC aVD );
// Delete mapping information from container.
* OTHER FUNCTIONS: Other VEE DATA functions include:
enum veeType VEEAPI vdcType( VDC aVD );
Get type of VDC, returns VEE_NOTDEFINED1 on error.
VDC VEEAPI vdcCopy( VDC oldVD );
Make a copy of a VDC. Return NULL on error.
VDC VEEAPI vdcFree( VDC aVD );
Destroy a container and release its memory. Return NULL on error.
int16 VEEAPI veeGetErrorNumber( void );
char* VEEAPI veeGetErrorString( void );
Get error number/message of last error.