|
Simple message passing routines:
Devices are addressed by name, and operations are performed by sending a message string and an
additional optional data object identifier. This data object is self describing in the sense that it con
tains indicators of the data type (int, float, etc.) and size (array lengths). Data returned from the
control system will also be through a data object.
|
status = cdevSend (char* devName, char* message,
cdev_data_t out, cdev_data_t in);
|
The data objects act as small databases able to hold multiple tagged data items. Items are inserted
into or extracted from the database via access routines. These access routines are also responsible
for any necessary data type conversions. Tags are passed to the cdevData routines as integers, and
are looked up in the tag database by specifying an ascii tag name. The most common tags are
"value", "timeStamp", "status", and "severity".
Data object manipulation routines:
|
cdevDataAllocate (cdev_data_t* id);
cdevDataFree (cdev_data_t id);
cdevDataTagC2I (char* tag, int *tagid);
cdevDataTagI2C (int tagid, char** tag); /* caller must free string */
cdevDataInsertTag (int tag, char* ctag);
cdevDataInsert (cdev_data_t id, int tagid, int type, void * data);
int cdevDataInsertArray(cdev_data_t id, int tagid, int type, void *data, size_t len, size_t
ndim);
cdevDataGet (cdev_data_t id, int tagid, int type, void * data);
cdevDataFind (cdev_data_t id, int tagid, void ** data);
int cdevDataGetType (cdev_data_t id, int tagid);
int cdevDataGetDim (cdev_data_t id, int tagid);
int cdevDataGetElems (cdev_data_t id, int tagid);
cdevDataGetBounds (cdev_data_t id, int tagid, int *bounds, int bsize);
cdevDataSetBounds (cdev_data_t id, int tagid, int *bounds, int bsize);
void cdevDataRemoveAll(cdev_data_t id);
cdevDataRemove (cdev_data_t id, int tagid);
cdevDataChangeTag (cdev_data_t id, int tagid, int newtagid);
cdevDataAsciiDump (cdev_data_t id, FILE *fp);
void cdevDataCopy(cdev_data_t from, cdev_data_t *to);
|
For example, to read the current from a magnet named NLQ1:
|
cdev_data_t result;
float current;
int status, valtag;
status = cdevDataAllocate (&result);
status = cdevDataTagC2I ("value",&valtag);
status = cdevSend("NLQ1","read current",NULL,result);
status = cdevDataGet(result, valtag, CDEV_FLOAT, current);
|
The first call to a device automatically connects to the requested service, initializing any underly
ing packages as needed. These routines are synchronous (operation completes before return);
asynchronous routines are discussed below.
The message (second) argument selects what operation and attribute to access. Many messages
are by convention of the form "verb attribute", and typical verbs include get, set, monitorOn, and
monitorOff.
Binding a device and message
In order to avoid the overhead of parsing the device name and message on each call, it is possible
to get a handle which binds the device and message into a request.
|
status = cdevRequestAllocate(char* device, char*message, CDEVREQUESTID* reqid);
|
Operations may be performed on this request object without specifying device and message.
|
status = cdevRequestSend(reqid, out, result);
|
Request objects maintain a connection to a server, and the state of this connection may be
obtained:
|
state = cdevRequestState (reqid);
|
The current access rights (read, write, none) for this request object may be obtained:
|
access = cdevRequestAccess (reqid);
|
Given the request object, it is also possible to extract the device name and message:
|
cdevRequestDevice (reqid, char** name); /* caller must free string */
cdevRequestMessage (reqid, char** msg);
|
Monitoring and Asynchronous I/O
A message sent to a device may result in an asynchronous reply, or more than one reply. In addi
tion, abnormal conditions may occur in the device which are of interest to the application. Each of
these results in a message back to the client which is asynchronous with respect to program exe
cution. Several calls are provided to deal with asynchronous data.
An asynchronous version of the message send call will send the mes
sage, but not wait for the reply. Two async forms are supported: one returning data to a caller's
data object, and the other returning data to a callback function. The reply argument is replaced by
an additional structure which specified a user callback function.
|
status = cdevSendNoBlock(char* device, char* message,
cdev_data_t out, cdev_data_t result);
status = cdevSendCallback(char* device, char* message,
cdev_data_t out, CDEVCALLBACK* callback);
|
Similarly for request objects:
|
status = cdevRequestSendNoBlock(cdev_data_t out, cdev_data_t result);
status = cdevRequestSendCallback(cdev_data_t out, CDEVCALLBACK* callback);
|
Monitoring:
|
|
Monitoring is started and stopped by two using the message "monitorOn xxx" and
"monitorOff xxx".
|
status = cdevSendCallback("deviceName", "monitorOn attributeName",NULL, callback);
|
Synchronization
|
|
In order to synchronize with asynchronous messages, both a poll and a pend
call are available, as well as an explicit flush. A pend with 0.0 seconds waits forever.
|
cdevFlush ();
cdevPoll ();
cdevPend (float seconds);
|
If seconds==0.0, wait forever.
Grouping of operations will be similar to that specified for EZCA, with slight varia
tions. The grouping calls are useful for synchronizing with a set of asynchronous calls.
|
cdevGroupAllocate(GROUPID* groupid);
cdevGroupStart(groupid);
cdevGroupEnd(groupid);
cdevGroupPoll(groupid);
cdevGroupPend(groupid);
cdevGroupAllFinished (groupid);
cdevGroupStatus (groupid, int *status, int *nstatus); /* initially nstatus = len of status */
|
File Descriptors
|
|
To integrate other asynchronous systems, such as X windows, it is often neces
sary to obtain file descriptors for select operations.
|
status = cdevGetFD(int fd[], int * numFD);
|
Context
|
|
Operations on a device take place within a context. The context is maintained by the
device as a cdev_data_t structure. The following routines are used to get and set the context; set
ting items within the context is done by the cdevData* routines.
|
cdevGetContext(char *devname, cdev_data_t *id);
cdevSetContext(char *devname, cdev_data_t id);
|
Private Data
|
|
Each device also maintains a private data pointer, to be used by the application
developer for any purpose desired.
|
cdevGetPrivate (char *devname, void **data);
cdevSetPrivate (char *devname, void *data);
|
These same four routines are available for request objects:
|
cdevRequestGetContext (reqid, cdev_data_t *id);
cdevRequestSetContext (reqid, cdev_data_t id);
cdevRequestGetPrivate (reqid, void **data);
cdevRequestSetPrivate (reqid, void *data);
|
Error Reporting and Handling
This group of routines is also modeled after EZCA (assume identical functionality).
Turn automatic error reporting on and off:
|
cdevAutoErrorOn ();
cdevAutoErrorOff ();
|
Set the severity threshold at which errors should be reported:
|
cdevSetThreshold (int new);
cdevReportError (int severity, char *name,
CDEVREQUESTID request, char* format, ...);
cdevGetErrorString ();
cdevSetErrorHandler (function);
|
Note: For simplicity of use, a very compact set of error codes is implemented in cdev. The follow
ing error codes are defined in the header file cdevErrCode.h.
CDEV_SUCCESS = 0 Success
CDEV_INVALIDOBJ = 1 Invalid cdev object used
CDEV_INVALIDARG = 2 Invalid argument passed to cdev method
CDEV_INVALIDSVC = 3 Wrong service during dynamic loading
CDEV_NOTCONNECTED = 4 Not connected to low-level network service
CDEV_IOFAILED = 5 Low-level network service IO failed
CDEV_CONFLICT = 6 Conflicting of data types or data tags
CDEV_NOTFOUND = 7 Cdev cannot find specified data or tag
CDEV_TIMEOUT = 8 Time out
CDEV_CONVERT = 9 cdevData conversion error
Name Services
There is a special device named cdevDirectory which provides a set of query capabilities:
|
status = cdevSend("cdevDirectory", "query",
cdev_data_t selection, cdev_data_t result);
|
The selection data object specifies one or more tags used to select device names. The result con
tains a list of devices accessed through the "value" tag.
|