c2cWorldSendStream
DISCLAIMER
This document is currently under construction so please bear with the
type-o's and other errors.
Bug reports, comments, corrections, and suggestions should be sent to:
CAVEComm support
Table of Contents
1. Introduction
-
- 1.1 Library model
- 1.2 Communications transport
2. Library structure
-
- 2.1 Application types
- 2.2 Communications setup
- 2.3 Streaming data
- 2.4 Receiving data
- 2.5 Callback prototypes
- 2.6 Programming levels
- 2.7 Debugging
3. CAVEComm library
-
- 3.1 General routines
- 3.2 Broker routines
- 3.3 Data routines
- 3.4 World routines
- 3.5 Global variables
- 3.6 Data structures
4. CAVEComm configuration file
5. Sample applications
-
- 5.1 CAVE sample application
- 5.2 Simulation sample application
6. References
7. Credits
1. Introduction
The CAVEComm library is a set of routines designed to generalize the
communications between virtual environments and supercomputers.
1.1 Library model
The model for the library is that of a client/server. Somewhere at a
known URL is a broker whose purpose is to mediate the communications
between various client applications (CAVE, simulations, etc.).
The broker
The broker maintains tables of all clients and of all sessions (applications)
that are registered with it.
The table of clients maintains such elementary information as the client's
capabilities (machine type, model, etc.) as well as basic application information
(CAVE application, IBM simulation, etc.). Clients can query the broker
for this information.
The table of sessions maintains information on all sessions (applications)
that any registered client can run. The table maintains such information
as executable names, data files, command line arguments, etc. Of course,
a client cannot attach to sessions outside of the realm of their physical
machine. For example, a client cannot run a CAVE application unless they
have CAVE functionality on their machine. Clients can query the broker for
this session information.
The client
Each CAVEComm client is an application (CAVE, simulation, etc.) that
has the power to attach to any other application on the broker. A simulation
that is registered with the broker can be accessed by any other application
on the broker such as visualization applications or virtual environments.
1.2 Communications transport
The CAVEcomm library utilizes the
Nexus communications library developed at the
Mathematics and Computer Science Department at
Argonne National Laboratory.
While Nexus is the communications transport, the user will not require any
prerequisite knowledge in using it. All Nexus communications calls are
encapsulated in the library; thus, the user is unaware of the Nexus presence.
By using Nexus, the CAVEcomm library is able to support various communications
protocols such as TCP/IP sockets, UDP (reliable and unreliable), ATM, AAL5,
multicast, as well as various message passing libraries.
2. Library structure
2.1. Application types
There are basically two types of applications as seen by the CAVEComm library,
those that have CAVE library dependence (need the CAVE library for execution which
includes CAVEs, Simulators, and Immersadesks), and those that do not have CAVE
library dependence. The library reacts differently at higher programming levels
based on what type of application is using it. At the lowest levels of programming
with the CAVEComm library, it doesn't matter what type of application is being
run. At the higher programming levels, the application type defines particular
actions that are executed (transparent to the user).
CAVE applications
A CAVE application traditionally has a master process, a tracking process, and some
number of drawing processes based on how many walls are active in the CAVE. The
CAVEComm library changes this paradigm slightly by adding a thread to the above
scenario. This thread is responsible for streaming the six pre-defined CAVE streams
(S_CAVE_USER, S_HEAD_TRACKER, S_WAND_TRACKER, S_WAND_BUTTONS, S_WAND_JOYSTICK,
and S_WORLD_POSITION) continually as a CAVE application goes through its normal
operations.
Non-CAVE applications
A non-CAVE application will look the same as if it did not have CAVEComm
functionality. There are no threads created by the library nor is there any default
streaming of data.
2.2 Communications setup
All communication among users of the CAVEComm library happen asynchronously. Explicit
sending of data is required, however, receipt of data happens asynchronously via
stream subscription and user callbacks.
2.3 Streaming data
All data streaming is handled by generic broadcast mechanisms that manage sending data
to all subscribed to it. The user merely calls a general data stream function
and the library broadcasts the data to all subscribed to it. If no user is subscribed
to the sending stream, then the routine merely returns without any sending. For a stream
to be cast, it must first be registered so that the library can allow others to
subscribe and as well as for management purposes.
Users can explicitly create and send streams packed as they so desire. The user initializes
a data buffer, packs it, sends it along, and finally, frees the initialized buffer.
Here's an example of some different stream sends:
#define STREAM_ID 10
void SendData(void)
{ /* SendData */
c2cBuffer *databuffer; /* Data buffer */
int x; /* Some data to send */
float y;
/* User data send */
c2cInitPackBuffer(&databuffer); /* Initialize a buffer */
c2cPackInt(&databuffer,&x,1); /* Pack an integer */
c2cPackFloat(&databuffer,&y,1); /* Pack a float */
c2cSendStream(myid,&databuffer,STREAM_ID); /* Send the generic stream */
c2cFreePackBuffer(&databuffer); /* Free the buffer */
/* Predefined stream send */
c2cSendWandJoystick(CAVE_JOYSTICK_X,CAVE_JOYSTICK_Y); /* Send joystick stream */
} /* SendData */
2.4 Receiving data
All data is received asynchronously, thus the user never makes an explicit
read for data nor does the user ever block waiting for incoming data. All
data when received is routed to the user via callbacks that are executed
when data is ready.
The CAVEComm process is interrupted whenever data is received
and a callback is executed while the processor is interrupted.
The length of this interrupt lasts the duration of the
complete execution of the callback. While the process is in
a state of interrupt, incoming communications are blocked until
the callback has completed execution. This gives the user two
requirements when coding the callbacks. The first requirement would
be to make the callback as efficient and compact as possible. The
longer the callback takes to execute, the longer incoming data is
blocked for processing. The second requirement is that CAVEComm
communications programming within the interrupt (ie: coded into the
callback) is a poor practice and a process can easily deadlock if any part
of the communications performs a wait for incoming data.
The user goes through a four step process to receive a
remote data stream. The first thing to do is subscribe to the stream. The
next step happens without the aid of the user. Data is received and the
callback is executed. It is then up to the user to unpack the data (step three)
and finally, free the data buffer (step four).
2.5 Callback prototypes
All data transfer within the CAVEComm library is asynchronous so when an application
receives data, a callback is executed and the data is passed as parameters to that
callback. The number and types of parameters of the callback is based on what type
of data stream stream is being received. The breakdown is as follows:
S_CAVE_USER
- void callbackname(CAVEUser *user,CAVEId id);
- The parameter user is a structure containing all tracking, button, joystick, and
world information of the remote CAVE. Id is the CAVEId of the sending CAVE as known to
both of them on the broker.
S_HEAD_TRACKER
- void callbackname(float x,float y,float z,float a,float e,float r,CAVEId id);
- The parameters x,y,z refer to the location of the head tracker in CAVE space
and the parameters a,e,r refer to the azimuth, elevation, and roll angles of
the tracker. Id is the CAVEId of the sending CAVE as known to both of them
on the broker.
S_WAND_TRACKER
- void callbackname(float x,float y,float z,float a,float e,float r,CAVEId id);
- The parameters x,y,z refer to the location of the wand tracker in CAVE space
and the parameters a,e,r refer to the azimuth, elevation, and roll angles of
the tracker. Id is the CAVEId of the sending CAVE as known to both of them
on the broker.
S_WAND_BUTTON
- void callbackname(int b1,int b2,int b3,int b4,CAVEId id);
- The parameters b1,b2,b3,b4 refer to the state of the wand buttons on a given
CAVE. Id is the CAVEId of the sending CAVE as known to both of them on the
broker.
S_WAND_JOYSTICK
- void callbackname(float joyx,float joyy,CAVEId id);
- The parameters joyx,joyy refer to the state of the joystick valuators on a given
CAVE. Id is the CAVEId of the sending CAVE as known to both of them on the
broker.
S_WORLD_POSITION
- void callbackname(float x,float y,float z,float a,float e,float r,CAVEId id);
- The parameters x,y,z refer to the location of the remote CAVE in world coordinate
space and the parameters a,e,r refer to the azimuth, elevation, and roll angles of
the CAVE in that space. Id is the CAVEId of the sending CAVE as known to
both of them on the broker.
User Streams
- void callbackname(void *databuffer,CAVEId id);
- The parameter databuffer points to a buffer containing user data (to be unpacked with
c2cGetn calls). Id is the CAVEId of the sending CAVE as known to both of them
on the broker.
There is also a callback that can be executed upon the subscription to
a stream as specified by c2cRegisterStream.
The callback prototype is as follows:
Stream Subscribe Callback
- void callbackname(StreamType stream,CAVEId id);
- The stream parameter specifies what type of stream was subscribed
to to initiate the callback and id is the identifier of
the subscriber on the broker.
2.6. Programming levels
The CAVEComm library provides user different levels of functionality.
At the highest level, a user can track a remote user with a minimum of
five function calls. While this level provides the most ease in use,
it doesn't take full advantage of all the libraries features nor does it
provide the most power or flexibility. Access to other library features
and increased power and flexiblilty is accomplished via calling lower level
routines.
Here is an example of a program that does the same thing, however, implemented
at both the high and low level:
Low level
void Datacallback(CAVEUser *user,CAVEId id)
{ /* Datacallback */
Usercallback(user,id); /* Call the user callback for drawing */
} /* Datacallback */
c2cConfig config; /* Configuration file buffer */
HostId hid; /* Id of the host to connect to */
CAVEId cid; /* Id of our application on broker */
c2cReadConfigFile(NULL,&config); /* Read config file */
c2cInit(argc,argv,&config.env); /* Start c2c */
hid = c2cBrokerAttach(config.broker); /* Attach to broker */
cid = c2cRegister(hid,&c2cEnv); /* Register on broker */
c2cSubscribe(hid,cid,S_CAVE_USER,Datacallback); /* Subscribe to data */
High level
c2cWorldInit(argc,argv); /* Start c2c */
c2cTrackUserInit("Myself",Usercallback); /* Subscribe */
c2cDrawUser("Myself"); /* Draw the user */
Both programs subscribe to themselves for complete CAVE user information. Both
functions will execute the drawing function Usercallback for drawing
the remote user when data is ready. The higher level greatly simplifies the
coding for such an action.
2.7 Debugging
Debugging parallel and networking applications can be quite difficult especially
using standard debugging tools. The CAVEComm library provides users with
one mechanism for debugging their source programs. The application debugging
tool described is that of a layered text printing paradigm.
Users put function calls in their code that mimic the standard printf calls
(include a format string and any parameters). Also included is an integer
stating what layer at which the print is to occur. At run-time,
the library checks the layer of the print statemtent and prints
text to standard output. The user specifies the maximum level to print at
run-time. All prints with a layer less that or equal to this maximum will
be printed.
There are three ways for the user to specify the maximum debugging level for
their application. The first way would be to put it in their
configuration file. The next method of setting the
debugging level would be to include the command line parameter -c2cdbg
followed by an integer level ( -c2cdbg level ). The last method would
to to explicitly set it in the application via a call to
c2cSetDebugLevel.
This type of debugger gives the user a simple tool already built in to trace
their application's operations on various levels. By adding these print
statements to application code (using the c2cPrintf
call), the user can easily check for correct program operation.
The CAVEComm library uses this debugging tool itself. All the CAVEComm
library debugging statements start at level 100. To view the debugging prints
of the CAVEComm library, use a maxmimum debugging level of 140. All
CAVEComm routines are logged to standard output.
Here's an example of some CAVEComm library debugging code:
c2cPrintf(30,"Testing c2c debugging tool\n");
c2cPrintf(100,"starting c2cWorldInit\n");
c2cPrintf(100,"reading configuration file\n");
c2cReadConfigFile(NULL,&config); /* Get setup information */
c2cPrintf(100,"starting c2c functionality\n");
c2cInit(argc,argv,&config.env); /* Start c2c functionality */
c2cPrintf(100,"attaching to broker\n");
c2cWorld.hostid = c2cBrokerAttach(config.broker); /* Attach to broker */
c2cPrintf(100,"registering with broker\n");
c2cWorld.caveid = c2cRegister(c2cWorld.hostid,&c2cEnv); /* Register with broker */
c2cPrintf(30,"finished registering with id of %d\n",c2cWorld.caveid);
Here's the result of the above CAVEComm library debugging code with a maximum
debugging level of 100:
c2cdbg 30: Testing c2c debugging tool
c2cdbg 100: starting c2cWorldInit
c2cdbg 100: reading configuration file
c2cdbg 100: starting c2c functionality
c2cdbg 100: attaching to broker
c2cdbg 100: registering with broker
c2cdbg 30: finished registering with id of 4
Here's the result of the above CAVEComm library debugging code with a maximum
debugging level of 30:
c2cdbg 30: Testing c2c debugging tool
c2cdbg 30: finished registering with id of 4
3. CAVEComm library
All functions in the CAVEComm library start with the prefix c2c.
The CAVEComm library contains functions are broken down into the following sections:
3.1 General routines
Basic routines for CAVEComm functionality
3.2 Broker routines
Broker routines handle operations that are specific to dealing broker.
3.3 Data routines
Data routines handle the management of various data streams.
3.4 World routines
World routines are abstractions of many of the CAVEComm calls providing
the user with minimal program modification and CAVEComm library knowledge.
3.5 Global variables
Global variables that the user can access maintained within the CAVEComm
library.
3.1 General routines
- int c2cGetDebugLevel(void);
- Get the current maximum debugging level in an application.
- void c2cInit(int *argc, char *argv[],CaveEnv *env);
- Start all communications functionality. Its arguments are the
argument count, argument vector, and a structure describing the application's
operating environment. It MUST be the first CAVEComm
library call made (except for c2cReadConfigFile
or if the world routines are utilized). If the
CAVEComm library is used with the CAVE library, the call to c2cInit must occur
AFTER the call to CAVEInit is made.
- void c2cPrintf(int level, char *format, ...);
- This function is very much similar to the printf function found in
the standard I/O C library. The level parameter tells the
library at what level to print the text. The text is of the standard
printf setup consisting of a character string describing how to format
the text and any applicable variables contained in the format.
- int c2cReadConfigFile(char *filename,c2cConfig *config);
- Read filename for application definitions. The definitions are
placed into config which is a structure containing all known
application definitions. If filename is NULL, the default file
.c2cConfig is attempted to be opened. If filename or .c2cConfig
cannot be opened, E_OPEN_CONFIG_FILE is returned, otherwise, E_SUCCESS
is returned.
- void c2cSetDebugLevel(int level);
- Set the current maximum debugging level in an application.
- void c2cTerminate(void);
- End all communications functionality. It is the last CAVEComm library
call made (unless the world routines
are utilized).
3.2 Broker routines
- HostId c2cBrokerAttach(URL url);
- Attach to the broker listed by url. The URL must be of the
format c2cBroker://{ip hostname}:{ip port}/. Supplying a URL
of a different format will give unpredictable results. A HostId is
returned uniquely describing that broker.
- int c2cBrokerDetach(HostId host);
- Detach from the broker specified by host. No further references
can be made to host after it is detached. E_INVALID_HOST is returned if
HostId doesn't match a host that was previously attatched to with
c2cBrokerAttach, otherwise, E_SUCCESS is returned.
- int c2cBrokerKill(HostId host);
- Terminate the broker specified by host. All database information
on the broker is disposed of and all programs connected are terminated.
Most programs will never issue this call. E_INVALID_HOST is returned if
HostId doesn't match a host that was previously attatched to with
c2cBrokerAttach, otherwise, E_SUCCESS is returned.
- CAVEId c2cGetClientId(HostId host,char *name);
- Query the broker host and return the assigned CAVEId of the client name. The
CAVEId is returned if the client name is found, otherwise, E_INVALID_CLIENT is returned.
If the host is invalid, E_INVALID_HOST is returned.
- int c2cGetClients(HostId host,int *num_clients,CaveEnv **client_list);
- Retrieve a list of all clients currently registered on broker host.
The number of clients registered is returned is in num_clients
along with an array of all the registered CaveEnv structures. E_INVALID_HOST is returned if
HostId doesn't match a host that was previously attatched to with
c2cBrokerAttach, otherwise, E_SUCCESS is returned.
- SessionId c2cGetSessionId(HostId host,char *name);
- Query the broker host and return the assigned SessionId of the session name. The
SessionId is returned if the session name is found, otherwise, E_INVALID_SESSION is returned.
If the host is invalid, E_INVALID_HOST is returned.
- int c2cGetSessions(HostId host,int *num_sessions,CaveSession **session_list);
- Retrieve a list of all sessions currently registered on broker host.
The number of sessions registered is returned is in num_sessions
along with an array of all the registered CaveSession structures. E_INVALID_HOST is returned if
HostId doesn't match a host that was previously attatched to with
c2cBrokerAttach, otherwise, E_SUCCESS is returned.
- int c2cKillSession(HostId host,SessionId session,CAVEId cave);
- Not yet implemented.
- CAVEId c2cRegister(HostId host,CaveEnv *env);
- Register your application on broker host with the environment
env. All subsuquent queries to the broker with respect to clients
will reflect your application's presence. E_INVALID_HOST is returned if
HostId doesn't match a host that was previously attatched to with
c2cBrokerAttach. E_CLIENT_EXISTS is returned if
if your client name is already used on the broker. If there are no errors, a
CAVEId (0 or higher) is returned.
- int c2cSessionAttach(HostId host,SessionId session, CAVEId cave);
- Not yet implemented.
- SessionId c2cSessionCreate(HostId host,CaveSession *ses);
- Create session on broker host with session ses. All
subsuquent queries to the broker with respect to sessions will reflect
your application session's presence. E_INVALID_HOST is returned if
HostId doesn't match a host that was previously attatched to with
c2cBrokerAttach. E_SESSION_EXISTS is returned if
if your session name is already used on the broker. If all goes well, E_SUCCESS
is returned.
- int c2cSessionDetach(HostId host,CAVEId cave);
- Not yet implemented.
- int c2cSubscribe(HostId host,CAVEId datasource,StreamType stream,void *callback);
- Inform broker host that your application would like receive stream
data of type stream from datasource and call callback
when any incoming data is to be processed. The remote application will
then start streaming the requested data to your applicaiton. E_INVALID_HOST is returned if
HostId doesn't match a host that was previously attatched to with
c2cBrokerAttach. E_INVALID_CAVE_ID is returned if
if datasource is invalid on host. E_INVALID_STREAM is returned
datasource doesn't have stream available. E_SUCCESS is returned upon
successful subscription to stream.
- int c2cUnregister(HostId host,CAVEId cave);
- Tell the broker host to remove your application from the client
list. All subsequent queries to the broker with respect to clients will
not reflect your applications presence. E_INVALID_HOST is returned if
HostId doesn't match a host that was previously attatched to with
c2cBrokerAttach. E_INVALID_CAVE_ID is returned
if cave is invalid on host. E_SUCCESS is returned upon
successful subscription to stream.
- int c2cUnsubscribe(HostId host,CAVEId datasource,StreamType stream,void *callback);
- Inform broker host that your application would like cancel the
streaming of data of type stream from datasource with the
matching callback callback. The remote application no longer
streams data to your application. E_INVALID_HOST is returned if
HostId doesn't match a host that was previously attatched to with
c2cBrokerAttach. E_INVALID_CAVE_ID is returned if
if datasource is invalid on host. E_INVALID_STREAM is returned if
datasource doesn't have stream available. E_INVALID_CALLBACK is returned if
a callback is given that wasn't used for a subscription to stream.
E_SUCCESS is returned upon successful unsubscription to stream.
3.3 Data routines
- void c2cFreeDataBuffer(void *buffer);
- Free the data buffer bufffer (accessed with c2cGetn calls) from memory.
- void c2cFreePackBuffer(c2cBuffer **buffer);
- Free the data buffer buffer (accessed with c2cPackn calls) from memory.
- void c2cGetChar(void *buffer,char *data,int size);
- Get size characters from buffer and put them into data.
- void c2cGetDouble(void *buffer,double *data,int size);
- Get size doubles from buffer and put them into data.
- void c2cGetFloat(void *buffer,float *data,int size);
- Get size floats from buffer and put them into data.
- void c2cGetInt(void *buffer,int *data,int size);
- Get size integers from buffer and put them into data.
- void c2cGetLong(void *buffer,long *data,int size);
- Get size long integers from buffer and put them into data.
- void c2cInitPackBuffer(c2cBuffer **buffer);
- Initialize data buffer buffer for future packing.
- void c2cPackChar(c2cBuffer **buffer,char *data,int size);
- Add size blocks of character data to buffer for broadcast later.
- void c2cPackDouble(c2cBuffer **buffer,double *data,int size);
- Add size blocks of double data to buffer for broadcast later.
- void c2cPackFloat(c2cBuffer **buffer,float *data,int size);
- Add size blocks of float data to buffer for broadcast later.
- void c2cPackInt(c2cBuffer **buffer,int *data,int size);
- Add size blocks of integer data to buffer for broadcast later.
- void c2cPackLong(c2cBuffer **buffer,long *data,int size);
- Add size blocks of long data to buffer for broadcast later.
- int c2cRegisterStream(StreamType stream,void *subscribecallback,void *unsubscribecallback);
- Register a stream in your application. No streams can be subscribed to
until they are registered. If subscribecallback is supplied (it is not NULL),
that callback will be executed every time the stream is subscribed to. If
unsubscribecallback is supplied (it is not NULL), that callback will be executed
every time the stream is unsubscribed to. E_STREAM_REGISTERED is returned if the
stream is already registered. E_SUCCESS is returned otherwise.
- int c2cSendStream(CAVEId sourceid, c2cBuffer **buffer,StreamType stream);
- Cast a buffer of data buffer (previously packed with c2cPackn routines)
of stream type stream to all applications subscribed to that stream with a source
identifier of sourceid. The source identifier tells the remote processes who sent
them the stream. E_INVALID_STREAM is returned if the stream wasn't registered, otherwise,
E_SUCCESS is returned.
- int c2cSendHeadTracker(float x,float y,float z,float a,float e,float r);
- Cast local CAVE head tracker information (passed via parameters) to all applications
subscribed to the stream S_HEAD_TRACKER. E_INVALID_STREAM
is returned if the stream wasn't registered, otherwise, E_SUCCESS is returned.
- int c2cSendUser(CAVEId sourceId, CAVEUser *user);
- Cast local CAVE user (all trackers, buttons, joystick and world info) to
all applications subscribed to the stream S_CAVE_USER. E_INVALID_STREAM
is returned if the stream wasn't registered, otherwise, E_SUCCESS is returned.
- int c2cSendWandButtons(CAVEId sourceId, int b1,int b2,int b3,int b4);
- Cast local CAVE wand button information (passed via parameters) to all applications
subscribed to the stream S_WAND_BUTTON. E_INVALID_STREAM
is returned if the stream wasn't registered, otherwise, E_SUCCESS is returned.
- int c2cSendWandJoystick(CAVEId sourceId, float joyx,float joyy);
- Cast local CAVE wand joystick information (passed via parameters) to all applications
subscribed to the stream S_WAND_JOYSTICK. E_INVALID_STREAM
is returned if the stream wasn't registered, otherwise, E_SUCCESS is returned.
- int c2cSendWandTracker(float x,float y,float z,float a,float e,float r);
- Cast local CAVE wand tracker information (passed via parameters) to all applications
subscribed to the stream S_WAND_TRACKER. E_INVALID_STREAM
is returned if the stream wasn't registered, otherwise, E_SUCCESS is returned.
- int c2cSendWorldPosition(float x,float y,float z,float a,float e,float r);
- Cast local CAVE world position (passed via parameters) to all applications
subscribed to the stream S_WORLD_POSITION. E_INVALID_STREAM
is returned if the stream wasn't registered, otherwise, E_SUCCESS is returned.
- int c2cUnregisterStream(StreamType stream)
- Unregister a stream with your application. No remote applications can
subscribe to a stream once it is unregistered. E_INVALID_STREAM
is returned if the stream wasn't registered, otherwise, E_SUCCESS is returned.
3.4 World routines
- void c2cDrawAllUsers(void);
- Draw all users that are tracked via c2cTrackUserInit.
- void c2cDrawSomeUsers(int count,char *users[]);
- Draw a list of users that are tracked via
c2cTrackUserInit. The number of users in the list as well as the
application names of the users to draw are passed as parameters.
- int c2cDrawUser(char *user);
- Draw a user that is tracked via
c2cTrackUserInit. The user to draw is specified by giving his
applicaion name for user. E_INVALID_CLIENT is returned if an invalid
client name is given. E_SUCCESS is returned otherwise.
- void c2cTrackUserInit(char *user,void *callback);
- Start tracking user (specified by his application name). The rendering
of the user can be user defined by supplying a callback. Whenever a draw
command is issued, this callback will be executed. If NULL is given as the
callback, a default stick-man representation of the user will be rendered.
E_INVALID_CLIENT is returned if user is invalid. E_INVALID_STREAM is returned if
user doesn't have his tracking available. E_SUCCESS is returned upon
successful tracking initializaion.
- int c2cTrackUserExit(char *user,void *callback);
- Cancel the tracking of user (specified by his application name). The
callback of the tracked user MUST match that given when initiated. E_INVALID_CLIENT
is returned if user is invalid. E_INVALID_STREAM is returned if
user doesn't have his tracking available. E_SUCCESS is returned upon
successful tracking exiting.
- void c2cWorldDataInit(void);
- Initializes necessary data structures needed for world communications. This
MUST be the first CAVEComm library call issued in an application
(except for c2cReadConfigFile). If the application
running is a CAVE application, it MUST occur before the call to CAVEInit
is made. In addition, if any CAVEComm application wishes to utilize the user
tracking facilities, they MUST make a call to c2cWorldDataInit (whether the
application is CAVE based or not).
- void c2cWorldDataSubscribe(char *user,StreamType stream,void *callback);
- Subscribe to user (specified by his application name) for data stream
stream and to call callback when the data is received. E_INVALID_CLIENT
is returned if user is invalid. E_INVALID_STREAM is returned if
user doesn't have stream available. E_SUCCESS is returned upon
successful data subscription.
- void c2cWorldDataUnsubscribe(char *user,StreamType stream,void *callback);
- Unsubscribe to the data stream stream that was previously subscribed
to from user (specified by his application name) with callback.
E_INVALID_CLIENT is returned if user is invalid. E_INVALID_STREAM is returned if
user doesn't have stream available. E_INVALID_CALLBACK is returned if
a callback is given that wasn't used for a subscription to stream.
E_SUCCESS is returned upon successful data unsubscription.
- void c2cWorldExit(void);
- Terminate all World functionality. This MUST be the last CAVEComm
library call issued in an application.
- CAVEId c2cWorldGetClientId(char *name);
- Query the broker and return the assigned CAVEId of the client name. The
CAVEId is returned if the client name is found, otherwise, E_INVALID_CLIENT is returned.
- SessionId c2cWorldGetSessionId(char *name);
- Query the broker and return the assigned SessionId of the session name. The
SessionId is returned if the session name is found, otherwise, E_INVALID_SESSION is returned.
- void c2cWorldInit(int *argc,char *argv[]);
- Initiate all World functionality. If the CAVEComm library is used with
the CAVE library, it must occur AFTER the call to CAVEInit is made.
- int c2cWorldSendStream(c2cBuffer **buffer,StreamType stream);
- Cast a buffer of data buffer (previously packed with c2cPackn routines)
of stream type stream to all applications subscribed to that stream. E_INVALID_STREAM
is returned if the stream wasn't registered, otherwise, E_SUCCESS is returned.
3.5 Global variables
The CAVEComm library maintains global variables that the user can
access. They are as follows:
- CaveEnv c2cEnv
- This variable maintains all local environment information that was registered
with c2cInit.
3.6 Data structures
The CAVEComm library has data structures that are accessible by the user. They
are as follows:
CaveEnv
/* Structure defining CAVE environment */
typedef struct caveenv
{
char name[C2C_NAME_SIZE]; /* Unique name for this CAVE */
int type; /* Type of session this is (CAVE, I-Desk, etc...) */
int id; /* Id of the client */
}CaveEnv;
CaveSession
/* Structure describing a CAVE session */
typedef struct cavesession
{
CAVEId owner; /* Who owns the session on the broker */
char name[C2C_NAME_SIZE]; /* Unique name describing the session */
char pathname[C2C_PATH_SIZE]; /* Where to find the application */
char execname[C2C_EXE_SIZE]; /* Executable name */
char args[C2C_ARGS_SIZE]; /* Command line arguments */
int id; /* Id of the session */
}CaveSession;
CAVEUser
/* Structure defining CAVE user */
typedef struct caveuser
{
float headx,heady,headz, /* Head tracker location */
heada,heade,headr, /* Head tracker orientation */
wandx,wandy,wandz, /* Wand tracker location */
wanda,wande,wandr, /* Wand tracker orientation */
worldx,worldy,worldz, /* World location */
worlda,worlde,worldr; /* World orientation */
int but1,but2, /* Button information */
but3,but4;
float joyx,joyy; /* joystick information */
}CAVEUser;
c2cConfig
/* Structure defining a CAVEComm session */
typedef struct c2cconfig
{
URL broker; /* URL to connect to broker with */
CaveEnv env; /* Local environment parameters */
CaveSession session; /* Session info for this CAVE app */
}c2cConfig;
4. CAVEComm configuration file
The CAVEComm library looks for a configuration file (.c2cConfig by
default) that defines certain aspects of the application. The format for
the configuration file is keyword [option]. Keywords are not case
sensitive. Application definitions are as follows:
- Broker "Broker URL"
- A known URL of the broker that you are to register and attach to. The
broker URL MUST be of the format c2cBroker://{ip hostname}:{ip port}/.
- DebugLevel level
- Set the level for debugging the application. Debugging will be set at
level as soon as the parser finishes processing the DebugLevel keyword.
- ClientName "Client application name"
- The name of the application that is used when registering on the broker.
- ClientAppType type
- The application type describes what type of application it is (CAVE,
IMMERSADESK, SIMULATION).
- SessionName "Session name"
- The name of the session given to the broker upon session creation.
- SessionPath "Pathname to application"
- The pathname of the executable for the session. It is given to the broker
upon session creation.
- SessionExe "Session executable name"
- The executable name of the application for the session. It is given to the
broker upon session creation.
- SessionArgs "Command line arguments"
- The command line arguments needed to run the application. They are given
to the broker upon session creation.
5. Sample applications
5.1 CAVE sample application
Below is a sample application using the CAVEComm library in a CAVE application:
/*
Simple CAVE-to-CAVE application with CAVE functionality
This program contacts the CAVE application called "Argonne CAVE" and
tracks its user. You will see the remote user navigating through the
coordinate space with the default (stick-man) user representation
*/
#include /* CAVE library */
#include /* CAVE-to-CAVE library */
void UserDraw(void); /* Prototype for drawing function */
int connection;
void main(int argc,char *argv[])
{ /* main */
int rc; /* Return Code */
c2cWorldDataInit(); /* Perform PreInitialization */
CAVEConfigure(&argc,argv,NULL); /* Get CAVE configuration info */
CAVEInit(); /* Start CAVE application */
CAVEDisplay(UserDraw,0); /* Assign drawing function */
c2cWorldInit(&argc,argv); /* Start CAVE-to-CAVE functionality */
rc = -1;
connection = 0;
while (rc != E_SUCCESS)
{
printf("No One to ATTACH to yet\n");
rc = c2cTrackUserInit("Argonne CAVE2",NULL);/* Track Argonne user with default user rendering */
sleep(1);
}/* End of WHILE Loop */
connection = 1;
while(!getbutton(ESCKEY)) /* Wait for escape key */
{ /* While */
/* Do computation here or spin endlessly */
c2cUpdate(); /* <---------------------------------- NEW FUNCTION */
} /* While */
c2cTrackUserExit("Argonne CAVE2",NULL);/* Stop tracking Argonne user */
c2cWorldExit(); /* End CAVE-to-CAVE functionality */
CAVEExit(); /* End the CAVE application */
} /* main */
/*
User drawing function with CAVE-to-CAVE functionality
*/
void UserDraw(void)
{ /* UserDraw */
float bg[3] = {0,0,0};
c3f(bg);
clear(); /* Clear frame buffers */
zclear();
c2cDrawAllUsers(); /* Draw remote user */
} /* UserDraw */
Here is its corresponding .c2cConfig file:
c2cBroker://cavesound.mcs.anl.gov:1743/
ClientApptype CAVE
Clientname "CAVEComm Test"
5.2 Simulation sample application
Below is an example of a client/server CAVEComm application. The server
casts its application name to everyone subscribed to it (in this case,
everyone subscribed to stream APP_NAME_STREAM.
Server program:
/*
Simple CAVE-to-CAVE application without CAVE functionality
This server sends a stream of data (its app name) to everyone
subscribed to it
*/
#include /* CAVE-to-CAVE library */
#include ; /* Standard I/O library */
#define APP_NAME_STREAM 10 /* Requested stream id */
void main(int argc,char *argv[])
{ /* main */
c2cBuffer *databuffer; /* Data buffer to pack */
long longdata = 12345678;
float floatdata = 9876.543;
double doubledata = 5738.5674;
c2cWorldInit(&argc,argv); /* Start CAVE-to-CAVE functionality */
c2cRegisterStream(APP_NAME_STREAM,NULL,NULL); /* Register stream to cast */
while(1) /* Wait for escape key */
{ /* While */
c2cInitPackBuffer(&databuffer); /* Initialize pack buffer */
c2cPackChar(&databuffer,&c2cEnv.name,/* Pack the sending buffer */
C2C_NAME_SIZE);
c2cPackInt(&databuffer,&c2cEnv.type,1);
c2cPackLong(&databuffer,&longdata,1);
c2cPackFloat(&databuffer,&floatdata,1);
c2cPackDouble(&databuffer,&doubledata,1);
c2cWorldSendStream(&databuffer,APP_NAME_STREAM);/* Broadcast your stream */
c2cFreePackBuffer(&databuffer); /* Free sending data buffer */
c2cUpdate();
} /* While */
c2cWorldExit(); /* End CAVE-to-CAVE functionality */
} /* main */
Here is the server's corresponding .c2cConfig file:
Broker c2cBroker://cavesound.mcs.anl.gov:1743/
ClientApptype SIMULATION
Clientname "Argonne Simulation Server"
Client program:
/*
Simple CAVE-to-CAVE application without CAVE functionality
This client receives a stream and processes it in UserData callback
*/
#include /* CAVE-to-CAVE library */
#include /* Standard I/I */
#define APP_NAME_STREAM 10 /* Requested stream id */
void UserData(void *data, CAVEId id); /* Callback prototype */
void main(int argc,char *argv[])
{ /* main */
c2cWorldInit(&argc,argv); /* Start CAVE-to-CAVE functionality */
c2cWorldDataSubscribe("Argonne Simulation Server", /* Subscribe to stream */
APP_NAME_STREAM,UserData);
while(1) /* Spin endlesslyy */
{ /* While */
c2cUpdate(); /* Do some computation here or spin endlessly */
} /* While */
c2cWorldExit(); /* End CAVE-to-CAVE functionality */
} /* main */
/*
This is the callback called when the stream data is received
*/
void UserData(void *databuffer, CAVEId id)
{ /* UserData */
char remotedata[C2C_NAME_SIZE]; /* Data buffers */
int type;
long longdata;
float floatdata;
double doubledata;
c2cGetChar(databuffer,remotedata,C2C_NAME_SIZE); /* Read the data */
c2cGetInt(databuffer,&type,1);
c2cGetLong(databuffer,&longdata,1);
c2cGetFloat(databuffer,&floatdata,1);
c2cGetDouble(databuffer,&doubledata,1);
c2cFreeGetBuffer(databuffer); /* Get rid of data buffer */
printf("Received data character %s\n",remotedata);
printf(" integer %d\n",type);
printf(" long %d\n",longdata);
printf(" float %f\n",floatdata);
printf(" double %f\n",doubledata);
printf("from source %d\n",id);
} /* UserData */
Here is the client's corresponding .c2cConfig file:
Broker c2cBroker://cavesound.mcs.anl.gov:1743/
ClientApptype SIMULATION
Clientname "Argonne Simulation Client"
6. References
1. T. Disz, M. Papka, M. Pellegrino, R. Stevens, Sharing visualization
experiences among remote virtual environments. Proceedings of the
International Workshop on High Performance Computing for Computer Graphics and
Visualization, Swansea, Wales in July 1995.
7. Credits
The CAVEComm library is based on the
Nexus communications library written and supported by Argonne's Nexus Group
and the P2P shared memory library supported by Argonne's MPI Group.
Last edited October 1, 1995