This chapter describes routines for manipulating and accessing an initialized IOC database.
This chapter is divided into several sections:
There are a number of database related include files. Of particular interest to this chapter are:
This file contains a number of database related definitions. The most important are:
Note that DB_MAX_CHOICES
applies for code using the runtime routines documented in this chapter, but for Channel Access clients the maximum number of choices and their choice string length are different, and are defined in the db_access.h file.
This file defines the possible field types. A field's type is perhaps its most important attribute. Changing the possible field types is a fundamental change to the IOC software, because many IOC software components are aware of the field types.
The field types are:
A field of type DBF_STRING
, ..., DBF_DOUBLE
can be a scalar or an array.
A DBF_STRING
field contains a NULL
terminated ascii string.
The field types DBF_CHAR
, ..., DBF_DOUBLE
correspond to the standard C data types.
DBF_ENUM
is used for enumerated items, which is analogous to the C language enumeration.
An example of an enum field is field VAL
of a multi bit binary record.
The field types DBF_ENUM
, DBF_MENU
, and DBF_DEVICE
all have an associated set of ASCII strings defining the choices.
For a DBF_ENUM
, the record support module supplies values and thus are not available for static database access.
The database access routines locate the choice strings for the other types.
DBF_INLINK
and DBF_OUTLINK
specify link fields.
A link field can refer to a signal located in a hardware module, to a field located in a database record in the same IOC, or to a field located in a record in another IOC.
A DBF_FWDLINK
can only refer to a record in the same IOC.
Link fields are described in a later chapter.
DBF_INLINK
(input), DBF_OUTLINK
(output), and DBF_FWDLINK
(forward) specify that the field is a link structure as defined in link.h
.
There are three classes of links:
dbPutField
or dbPutLink
.
link.h
for a description of the bus types currently supported.
When first loaded the field is always creates as a PV_LINK
.
When the IOC is initialized each PV_LINK
is converted either to a DB_LINK
or a CA_LINK
.
DBF_NOACCESS
fields are for private use by record processing routines.
This file is the interface definition for the run time database access library, i.e. for the routines described in this chapter.
An important structure defined in this header file is DBADDR
typedef struct dbAddr{ struct dbCommon *precord;/* address of record*/ void* pfield;/* address of field*/ void* pfldDes;/* address of struct fldDes*/ void* asPvt;/* Access Security Private*/ long no_elements; /* number of elements (arrays)*/ short field_type;/* type of database field*/ short field_size;/* size (bytes) of the field*/ short special;/* special processing*/ short dbr_field_type; /*optimal database request type*/ }DBADDR;
precord
to point to the specific record type.
pfield
provides direct access to the data value.
no_elements
has the value 1.
For array fields it is the maximum number of elements that can be stored in the array.
NOTE: pfield
, no_elements
, field_type
,
field_size
, special
, and dbr_field_type
can all be set by record support (cvt_dbaddr
).
Thus field_type
, field_size
, and special
can differ from that specified by pfldDes
.
This header provides an optional API allowing the IOC to display information about the services that are connecting to and using the IOC.
This header file describes the various types of link fields supported by EPICS.
With the exception of record and device support, all access to the database is via the database access routines. Even record support routines access other records only via database or channel access. Channel Access, in turn, accesses the database via database access. Other services can similarly be layered on top of the IOC, using database access to manipulate record field values and cause records to be processed.
Perhaps the easiest way to describe the database access layer is to list and briefly describe the set of routines that constitute database access. This provides a good look at the facilities provided by the database.
Before describing database access, one caution must be mentioned. The usual way to communicate with an IOC database from outside the IOC is via Channel Access, although other similar network server layers can be added. Most client applications should communicate with the database via Channel Access, not via database access, even if they reside in the same IOC process as the database. Since Channel Access provides network independent access to a database, it must ultimately call database access routines. The database access interface was enhanced in 1991, but Channel Access could not be changed to use the new interface as this would break existing client applications. Instead a module was written which translates the original database access API into the new API. This interface between the old and new style database access calls is not discussed in this manual.
The database access routines are:
dbGetLinkValue
and dbGetField
dbPutxxx
functions.
Before describing database access structures, it is necessary to describe database request types and request options.
When dbPutField
or dbGetField
are called one of the arguments is a database request type.
This argument has one of the following values:
NULL
terminated string
ACKT
.
The request types DBR_STRING
,..., DBR_DOUBLE
correspond exactly to the database field data types.
DBR_ENUM
is used for database fields that represent a set of choices or options.
It is used for access to fields of type DBF_ENUM
, DBF_DEVICE
, and DBF_MENU
.
The complete set of database field types are defined in dbFldTypes.h
.
The DBR_PUT_ACKT
and DBR_PUT_ACKS
requests are used to perform global alarm acknowledgment.
dbGetField
also accepts argument options which is a mask containing a bit for each additional type of information the caller desires.
The complete set of options is:
Example
The file dbAccess.h
contains macros for using options.
A brief example should show how they are used.
The following example defines a buffer to accept an array of up to ten float values.
In addition it contains fields for options DBR_STATUS
and DBR_TIME
.
struct buffer { DBRstatus DBRtime float value[10]; } buffer;
The associated dbGetField
call is:
long options,number_elements,status; ... options = DBR_STATUS | DBR_TIME; number_elements = 10; status = dbGetField(paddr,DBR_FLOAT,&buffer,&options,&number_elements);
Consult dbAccess.h
for a complete list of macros.
Structure dbAddr
contains a field dbr_field_type
.
This field holds the database request type that most closely matches the database field type.
Using this request type will put the smallest load on the IOC.
The request types DBR_PUT_ACKT
and DBR_PUT_ACKS
are used for global alarm acknowledgment.
The alarm handler uses these requests.
For each of these types the user (normally channel access) passes an unsigned short value.
This value represents:
Locate a process variable, format:
long dbNameToAddr( char *pname, /*ptr to process variable name */ struct dbAddr *paddr);
The most important goal of database access can be stated simply: Provide quick access to database records and fields within records. The basic rules are:
dbNameToAddr
once and only once for each field to be accessed.
dbGetField
and write values via dbPutField
.
The routines described in this subsection are used by channel access, sequence programs, etc.
Record processing routines, however, use the routines described in the next section rather then dbGetField
and dbPutField
.
Given a process variable name, this routine locates the process variable and fills in the fields of structure dbAddr
.
The format for an IOC process variable name is one of:
<record_name> <record_name>. <record_name>.<field_name> <record_name>.<field_name><modifier> <record_name>.<modifier>
For example the value field of a record with record named sample_name
is:
``sample_name.VAL
".
The record_name is case sensitive.
The field_names available depend on the record type of the record and usually consist of all upper-case letters.
If omitted the field name VAL
is used if it exists.
Currently the only modifier supported is a single dollar sign $
and is only valid on fields which are strings or links.
Additional modifiers may be added in future releases.
dbNameToAddr
locates a record via a process variable directory (PVD).
It fills in a structure (dbAddr
) describing the field.
dbAddr
contains the address of the record and also the field.
Thus other routines can locate the record and field without a search.
Although the PVD allows the record to be located via a hash algorithm and the field within a record via a binary search, it still takes about 80 microseconds (25MHz 68040) to located a process variable.
Once located the dbAddr
structure allows the process variable to be accessed directly.
Get values associated with a process variable, format:
long dbGetField( struct dbAddr *paddr, short dbrType, /* DBR_xxx */ void *pbuffer, /* ptr to returned data */ long *options, /* ptr to options */ long *nRequest, /* ptr to number of elements */ void *pfl); /* used by monitor routines */
This routine locks, calls dbGet
, and unlocks.
Get value from the field referenced by a database link, format:
long dbGetLink( struct db_link *plink, /* ptr to link field */ short dbrType, /* DBR_xxx */ void *pbuffer, /* ptr to returned data */ long *options, /* ptr to options */ long *nRequest); /* ptr to number of elements desired */
options
can be NULL if no options are desired.
nRequest
can be NULL for a scalar.
dbGetLink
is implemented as a macro that calls dbGetLinkValue
and can reference its arguments more than once.
The macro skips the call for constant links.
User code should never call dbGetLinkValue
.
This routine is called by database access itself and by record support and/or device support routines in order to get values for input links. The value can be obtained directly from other records or via a channel access client. This routine honors the link options (process and maximize severity). In addition it has code that optimizes the case of no options and scalar.
Get values associated with a process variable, format:
long dbGet( struct dbAddr *paddr, short dbrType, /* DBR_xxx */ void *pbuffer, /* ptr to returned data */ long *options, /* ptr to options */ long *nRequest, /* ptr to number of elements */ void *pfl); /* used by monitor routines */
This routine retrieves the data referenced by paddr
and converts it to the format specified by dbrType
.
options
is a read/write field.
On entry to dbGet
, options
holds the desired options.
When dbGet
returns, options
gives the options actually honored.
If an option is not honored, the corresponding fields in the buffer are filled with zeros.
nRequest
is also a read/write field.
Upon entry to dbGet
it specifies the maximum number of data elements the
caller is willing to receive.
When dbGet
returns it has been set to the actual number of elements returned.
It is permissible to request zero elements.
This is useful when only option data is desired.
pfl
argument is for use by the Channel Access monitor routines.
All other users must pass in NULL
.
dbGet
calls one of a number of conversion routines in order to convert data from the DBF
types to the DBR
types.
It calls record support routines for special cases such as arrays.
For example, if the number of field elements is greater then 1 and record support routine get_array_info
exists, then it is called.
It returns two values:
the current number of valid field elements and an offset.
The number of valid elements may not match dbAddr.no_elements
, which is really the maximum number of elements allowed.
The offset is for use by records which implement circular buffers, and provides the offset to the current beginning of the array data.
Change the value of a process variable, format:
long dbPutField( struct dbAddr *paddr, short dbrType, /* DBR_xxx */ void *pbuffer, /* ptr to data */ long nRequest); /* number of elements to write */
This routine is responsible for accepting data in one of the DBR_xxx
formats, converting it as necessary, and modifying the database.
Similar to dbGetField
, this routine calls one of a number of conversion routines to do the actual conversion and relies on record support routines to handle arrays and other special cases.
It should be noted that routine dbPut
does most of the work.
The actual algorithm for dbPutField
is:
DISP
field is TRUE
then, unless it is the DISP
field itself which is being modified, the field is not written.
dbPut
is called.
dbPut
is successful then:
If this is the PROC
field or if both of the following are TRUE
:
1) the field is a process passive field,
2) the record is passive.
dbScanPassive
after setting putf
TRUE
to show the process request came from dbPutField
.
Change the value referenced by a database link, format:
long dbPutLink( struct db_link *plink, /* ptr to link field */ short dbrType, /* DBR_xxx */ void *pbuffer, /* ptr to data to write */ long nRequest); /* number of elements to write */
dbPutLink
is actually a macro that calls dbPutLinkValue
and can reference its arguments more than once.
The macro skips the call for constant links.
User code should never call dbPutLinkValue
.
This routine is called by database access itself and by record support and/or device support routines in order to put values into other database records via output links.
For Channel Access links it calls dbCaPutLink
.
For database links it performs the following functions:
dbPut
.
PROC
or if both of the following are true: 1) process_passive
is TRUE
and 2) the record is passive then:
dbPutField
request then ask for the record to be reprocessed when it completes.
dbScanPassive
.
Put a value to a database field, format:
long dbPut( struct dbAddr *paddr, shortdbrType,/* DBR_xxx*/ void*pbuffer,/*addr of data*/ longnRequest);/*number of elements to write*/
This routine is responsible for accepting data in one of the DBR_xxx
formats, converting it as necessary, and modifying
the database.
Similar to dbGet
, this routine calls one of a number of conversion routines to do the actual conversion and relies on record support routines to handle arrays and other special cases.
The Process Notify subsystem provides the following features:
A process request will be issued if any of the following is true:
At most one process is performed per dbProcessNotify
request.
The dbNotify.h header defines the following:
typedef enum { processRequest, putProcessRequest, processGetRequest, putProcessGetRequest } notifyRequestType; typedef enum { putDisabledType, putFieldType, putType } notifyPutType; typedef enum { getFieldType, getType } notifyGetType; typedef enum { notifyOK, notifyCanceled, notifyError, notifyPutDisabled } notifyStatus; typedef struct processNotify { /* Fields for private use by dbNotify implementation: */ ellCheckNode restartNode; void * pnotifyPvt; /* Fields set by dbNotify: */ notifyStatus status; int wasProcessed; /* (0,1) => (no,yes) */ /*Fields set by user: */ notifyRequestType requestType; struct dbAddr *paddr; /* set by dbNameToAddr*/ int (*putCallback)(struct processNotify *,notifyPutType type); void (*getCallback)(struct processNotify *,notifyGetType type); void (*doneCallback)(struct processNotify *); void * usrPvt; /* for private use of user */ } processNotify; epicsShareFunc void dbProcessNotify(processNotify *pprocessNotify); epicsShareFunc void dbNotifyCancel(processNotify *pprocessNotify);
The client must allocate an instance of processNotify
, which can be used for an arbitrary number of calls to dbProcessNotify
.
Before calling dbProcessNotify
the following fields must be given values:
requestType
- The request type.
paddr
- A struct dbAddr, which is given values by a call to dbNameToAddr
.
putCallback
- If the request is a putProcessRequest
or a putProcessGetRequest
this must be given a value.
It is called before the record is processed.
This routine is expected to issue a database put.
The return value should be (0, 1) if the callback operation (was not, was) successful.
getCallback
- If the request is a processGetRequest
or a putProcessGetrequest
this must be given a value.
It is called after the record is processed but before the record is released.
This routine is expected to issue a database get.
doneCallback
- This must be given a value.
It is called after the record is processed and after the optional getCallback
.
This routine may issue a new dbProcessNotify
if desired.
userPvt
- A field for the client and its callback routines to use as needed; this pointer is not used by the processNotify code.
The notifyPutType
argument to putCallback
is one of these values:
putDisabledType
- Puts are disabled.
The client must not issue a put.
dbPutFieldType
- The client may issue a dbPutField
request.
This is returned when paddr
refers to a link field.
For link fields the record will never be processed as a result of the dbProcessNotify
.
dbPutType
- The client can issue a dbPut request.
The record may or may not be processed after the client callback returns.
The notifyGetType
argument to getCallback
will be one of these values:
getFieldType
- The client may issue a dbGetField
request.
This is returned when paddr
refers to a link field.
For link fields the record will never be processed as a result of the dbProcessNotify
.
getType
- The client may issue a dbGet
request.
The notifyStatus
argument to doneCallback
is one of these values:
notifyOK
- The dbProcessNotify
request was successful and the record was processed.
notifyNoProcessOK
- The dbProcessNotify
request was successful but the record was not processed.
notifyError
- An error occured.
Example code can be found in the routine dbtpn
which is defined in base/src/ioc/db/dbNotify.c
. It uses both putProcesssRequest
and processGetRequest
.
EPICS base provides soft device support that uses processNotify for both input and output record types. All use the device type name ``Asyn Soft Channel''.
The input types issue a processGetRequest
:
devAiSoftCallback
- Supports aiRecord.
devBiSoftCallback
- Supports biRecord.
devMbbiSoftCallback
- Supports mbbiRecord.
devMbbiDirectSoftCallback
- Supports mbbiDirectRecord.
devLiSoftCallback
- Supports longinRecord.
devSiSoftCallback
- Supports stringinRecord.
The output types issue a channel access ca_put_callback
request.
devAoSoftCallback
- Supports aoRecord.
devBoSoftCallback
- Supports boRecord.
devCalcoutSoftCallback
- Supports calcoutRecord.
devMbboSoftCallback
- Supports mbboRecord.
devMbboDirectSoftCallback
- Supports mbboDirectRecord.
devSiSoftCallback
- Supports longoutRecord.
Determine the buffer size for a dbGetField
request, format:
long dbBufferSize( shortdbrType,/* DBR_xxx*/ longoptions,/* options mask*/ longnRequest);/* number of elements*/
This routine returns the number of bytes that will be returned to dbGetField
if the request type, options, and number of elements are specified as given to dbBufferSize
.
Thus it can be used to allocate storage for buffers.
NOTE: This should become a Channel Access routine
Determine the size a value field, format:
dbValueSize(short dbrType); /* DBR_xxx*/
This routine returns the number of bytes for each element of type dbrType
.
NOTE: This should become a Channel Access routine
Get address of a record support entry table.
Format:
struct rset *dbGetRset(DBADDR *paddr);
This routine returns the address of the record support entry table for the record referenced by the DBADDR
.
Is this field the VAL field of the record?
Format:
int dbIsValueField(struct dbFldDes *pdbFldDes);
This is the routine that makes the get_value
record support routine obsolete.
Format:
int dbGetFieldIndex(DBADDR *paddr);
Record support routines such as special
and cvt_dbaddr
need to know which field the DBADDR
references.
The include file describing the record contains define statements for each field.
dbGetFieldIndex
returns the index that can be matched against the define statements (normally via a switch statement).
Get number of elements in a field.
Format:
long dbGetNelements(struct link *plink,long *nelements);
This sets *nelements
to the number of elements in the field referenced by plink.
Format:
int dbIsLinkConnected(struct link *plink);
This routine returns (TRUE, FALSE) if the link (is, is not) connected.
This macro was provided in earlier versions of Base, but has been removed from 3.15 onward. Code that was using it to gain access to the internal components of the the link's dbAddr structure should be converted to make use of the other routines described in this chapter instead.
Format:
int dbGetLinkDBFtype(struct link *plink);
Format:
long dbGetControlLimits(struct link *plink,double *low, double *high);
Format:
long dbGetGraphicLimits(struct link *plink,double *low, double *high);
Format:
long dbGetAlarmLimits(struct link *plink, double lolo,double *low, double *high,double hihi);
Format:
long dbGetPrecision(struct link *plink,short *precision);
Format:
long dbGetUnits(struct link *plink,char *units,int unitsSize);
Format:
long dbGetSevr(struct link *plink,short *sevr);
Get Time Stamp for record containing link.
Format:
long dbGetTimeStamp(struct link *plink,TS_STAMP *pstamp);
Give a value to a record attribute.
long dbPutAttribute(char *recordTypename, char *name,char*value);
This sets the record attribute name
for record type recordTypename
to value
.
For example the following would set the version for the ai record.
dbPutAttribute("ai","VERS","V800.6.95")
dbScanLink
dbScanFwdLink
Process record if it is passive, format:
long dbScanPassive( struct dbCommon *pfrom, struct dbCommon *pto); /* addr of record*/ long dbScanLink( struct dbCommon *pfrom, struct dbCommon *pto); void dbScanFwdLink(struct link *plink);
dbScanPassive
and dbScanLink
are given the record requesting the scan, which may be NULL
, and the record to be processed.
If the record is passive and pact
is FALSE
then dbProcess
is called.
Note that these routine are called by dbGetLink
, dbPutField
, and by recGblFwdLink
.
dbScanFwdLink
is given a link that must be a forward link field.
It follows the rules for scanning a forward link.
That is for DB_LINKs it calls dbScanPassive and for CA_LINKS it does a dbCaPutLink if the PROC field of record is being addressed.
Request that a database record be processed, format:
long dbProcess(struct dbCommom *precord);
Request that record be processed. Record processing is described in detail below.
Database links can be changed at run time but only via a channel access client, i.e. via calls to dbPutField
but not to dbPutLink
.
The following restrictions apply:
DBR_STRING
or a nil-terminated array of DBR_CHAR or DBR_UCHAR characters
DTYP
field must be set before the link field.
There are facilities within the Channel Access communication infrastructure which allow the value of a process variable to be monitored by a channel access client.
It is a responsibility of record support (and db common) to notify the channel access server when the internal state of a process variable has been modified.
State changes can include changes in the value of a process variable and also changes in the alarm state of a process variable.
The routine db_post_events
is called to inform the channel access server that a process variable state change event has occurred.
#include <caeventmask.h> int db_post_events(void *precord, void *pfield, unsigned intselect);
The first argument, ``precord", should be passed a pointer to the record which is posting the event(s). The second argument, ``pfield", should be passed a pointer to the field in the record that contains the process variable that has been modified. The third argument, ``select", should be passed an event select mask. This mask can be any logical or combination of {DBE_VALUE, DBE_LOG, DBE_ALARM}. A description of the purpose of each flag in the event select mask follows.
The function db_post_events
returns 0 if it is successful and -1 if it fails.
It appears to be common practice within EPICS record support to ignore the status from db_post_events
.
At this time db_post_events
always returns 0 (success).
Because so many records at this time depend on this behavior it is unlikely that it will be changed in the future.
The function db_post_events
is written so that record support will never be blocked attempting to post an event because a slow client is not able to process events fast enough.
Each call to db_post_events
causes the current value, alarm status, and time stamp for the field to be copied into a ring buffer.
The thread calling db_post_events
will not be delayed by any network or memory allocation overhead.
A lower priority thread in the server is responsible for transferring the events in the event queue to the channel access clients that may be monitoring the process variable.
Currently, when an event is posted for a DBF_STRING field or a field containing array data the value is NOT saved in the ring buffer and the client will receive whatever value happens to be in the field when the lower priority thread transfers the event to the client. This behavior may be improved in the future.
User code only calls dbScanLock
and dbScanUnlock
.
All other routines are called by iocCore
.
Lock a lock set:
long void dbScanLock(struct dbCommon *precord);
Lock the lock set to which the specified record belongs.
Unlock a lock set:
long void dbScanUnlock(struct dbCommon *precord);
Lock the lock set to which the specified record belongs
Get lock set id:
long dbLockGetLockId(struct dbCommon *precord);
Each lock set is assigned a unique ID. This routine retrieves it. This is most useful to determine if two records are in the same lock set.
Determine lock sets for each record in database.
void dbLockInitRecords(dbBase *pdbbase);
Called by iocInit
.
Merge records into same lock set.
void dbLockSetMerge(struct dbCommon *pfirst, struct dbCommon *psecond);
If specified records are not in same lock set the lock sets are merged.
Called by dbLockInitRecords and also when links are modified by dbPutField
.
Recompute lock sets for given lock set
void dbLockSetSplit(struct dbCommon *psource);
This is called when dbPutField
modifies links.
Global lock for modifying links.
void dbLockSetGblLock(void);
Only one task at a time can modify link fields. This routine provides a global lock to prevent conflicts.
Unlock the global lock.
void dbLockSetGblUnlock(void);
If record is not already scan locked lock it.
void dbLockSetRecordLock(struct dbCommon *precord);
The routines described here are used to create and manipulate Channel Access connections from database input or output links. At IOC initialization an attempt is made to convert all process variable links to database links. For any link that fails, it is assumed that the link is a Channel Access link, i.e. a link to a process variable defined in another IOC. The routines described here are used to manage these links. User code never needs to call these routines. They are automatically called by iocInit and database access.
At iocInit
time a task dbCaLink
is spawned.
This task is a channel access client that issues channel access requests for all channel access links in the database.
For each link a channel access search request is issued.
When the search succeeds a channel access monitor is established.
The monitor is issued specifying ca_field_type
and ca_element_count
.
A buffer is also allocated to hold monitor return data as well as severity.
When dbCaGetLink
is called data is taken from the buffer, converted if necessary, and placed in the location specified by the pbuffer
argument.
When the first dbCaPutLink
is called for a link an output buffer is allocated, again using ca_field_type
and ca_element_count
.
The data specified by the pbuffer argument is converted and stored in the buffer.
A request is then made to dbCaLink
task to issue a ca_put
.
Subsequent calls to dbCaPutLink
reuse the same buffer.
Except for dbCaPutLinkCallback, these routines are normally only called by database access, i.e. they are not called by record support modules.
Called by iocInit
to initialize the dbCa
library
void dbCaLinkInit(void);
Add a new channel access link
void dbCaAddLink(struct link *plink);
void dbCaAddLinkCallback(struct link *plink, dbCaCallback connect,dbCaCallback monitor,void *userPvt);
connect
will be called whenever the link connects or disconnects.
monitor
will be called whenever a monitor event occurs.
connect
and or monitor
may be null.
Remove channel access link.
void dbCaRemoveLink(struct link *plink);
Get link value
long dbCaGetLink(struct link *plink,short dbrType, void *pbuffer,unsigned short *psevr,long *nRequest);
Put link value
long dbCaPutLink(struct link *plink,short dbrType, void *buffering nRequest);
This is meant for use by device or record support that wants a put to complete before completing record processing.
long dbCaPutLinkCallback(struct link *plink,short dbrType, const void *pbuffer,long nRequest,dbCaPutCallback callback);
<base>/src/std/dev/devAoSoftCallback.c provides an example of how to use this function. It contains:
static long write_ao(aoRecord *pao) { struct link *plink = &pao->out; long status; if(pao->pact) return(0); if(plink->type!=CA_LINK) { status = dbPutLink(&pao->out,DBR_DOUBLE,&pao->oval,1); return(status); } status = dbCaPutLinkCallback(plink,DBR_DOUBLE,&pao->oval,1, (dbCaCallback)dbCaCallbackProcess,plink); if(status) { recGblSetSevr(pao,LINK_ALARM,INVALID_ALARM); return(status); } pao->pact = TRUE; return(0); }
What happens is the following:
There is a possibility that the link is changed between the two phases of record processing. If this happens the user supplied callback will still get called exactly once but the link may have been modified.
The routines in this section are meant for use by device support to find out information about link fields. They must be called with dbScanLock held, i.e. normally they are called by the read or write method provided by device support.
Is Channel Connected
int dbCaIsLinkConnected(struct link *plink)
This routine returns (TRUE, FALSE) if the link (is, is not) connected.
Get Number of Elements
long dbCaGetNelements(struct link *plink,long *nelements);
This call, which returns an error if the link is not connected, sets the native number of elements.
Get Alarm Severity
long dbCaGetSevr(struct link *plink,short *severity);
This call, which returns an error if the link is not connected, sets the alarm severity.
Get Time Stamp
long dbCaGetTimeStamp(struct link *plink,TS_STAMP *pstamp));
This call, which returns an error if the link is not connected, sets pstamp to the time obtained by the last CA monitor.
Get link type
int dbCaGetLinkDBFtype(struct link *plink);
This call, which returns an error if the link is not connected, returns the field type.
Get Attributes
long dbCaGetAttributes(struct link *plink, void (*callback)(void *usrPvt),void *usrPvt);
When ever dbCa receives a connection it issues a CA get request to obtain the control, graphic, and alarm limits and to obtain the precision and units. By calling dbCaGetAttributes the caller can be notified when this get completes.
Get Control Limits
long dbCaGetControlLimits(struct link *plink,double *low, double *high);
This call returns an error if the link is not connected or if the CA get request for limits, etc. has not completed. If it returns success it has set the control limits.
Get graphic Limits
long dbCaGetGraphicLimits(struct link *plink,double *low, double *high);
This call returns an error if the link is not connected or if the CA get request for limits, etc. has not completed. If it returns success it has set the graphic limits.
Get Alarm Limits
long dbCaGetAlarmLimits(struct link *plink, double *lolo, double *low, double *high, double *hihi);
This call returns an error if the link is not connected or if the CA get request for limits, etc. has not completed. If it returns success it has set the alarm limits.
Get Precision
long dbCaGetPrecision(struct link *plink,short *precision);
This call returns an error if the link is not connected or if the CA get request for limits, etc. has not completed. If it returns success it has set the precision.
Get Units
long dbCaGetUnits(struct link *plink,char *units,int unitsSize);
This call returns an error if the link is not connected or if the CA get request for limits, etc. has not completed. If it returns success it has set the units.
Software that provides external access to the IOC database is a server layer. It is helpful for the IOC developer to be able to see information about the servers that are making use of the database access routines for purposes of diagnosing problems and reporting usage statistics. The dbServer API is provided to allow the IOC code to display specific kinds of information from the servers connected to it, without having to integrate those servers into the IOC code. Server layers are thus encouraged to register a dbServer structure with the IOC database to allow this information to be retrieved as needed.
The dbServer structure is defined in dbServer.h as follows:
typedef struct dbServer { ELLNODE node; const char *name; /* Print level-dependent status report to stdout */ void (* report) (unsigned level); /* Get number of channels and clients connected */ void (* stats) (unsigned *channels, unsigned *clients); /* Get identity of client initiating the calling thread */ /* Must return 0 (OK), or -1 (ERROR) from unknown threads */ int (* client) (char *pBuf, size_t bufSize); } dbServer;
A server layer should instantiate one of these and pass a pointer to it to dbRegisterServer:
void dbRegisterServer(dbServer *psrv);
The individual function pointers in the structure are optional, use NULL if a specific routine has not been implemented for this server. Additional function pointers may be added to the end of this structure in future releases, while aiming to keep API-compatibility with older versions. The functions provided by the server layer are used as follows.
user@host
), and return 0. If the calling thread does not belong to this server it should just return -1.
The dbServer.h header makes the following routines available to the IOC code.
void dbsr(unsigned level);
This routine scans through the list of registered servers, printing the server's name and then calling its report function if one exists.
This is an iocsh command that is intended to replace casr
, and is only called on demand by the user.
int dbServerClient(char *pBuf, size_t bufSize);
When the IOC processes a record that has its TPRO field set, this routine is called to obtain a server context for the printed record name.
It iterates through all of the registered servers in turn calling their client()
routines until one of them returns OK or the end of the list is reached.
If no server returns OK the routine returns -1.