Table of Contents Previous Chapter Chapter 6: Runtime Database Access
This chapter describes routines for manipulating and accessing an initialized IOC database.
This chapter is divided into the following sections:
- Database related include files. All of interest are listed and those of general interest are discussed briefly.
- Runtime database access. These routines are used within an IOC to access an initialized database.
- Old Database Access. This is the interface still used by Channel Access and thus by Channel Access clients.
Directory epics/share/epicsH contains a number of database related include files. A complete list is:
- dbDefs.h: Miscellaneous database related definitions
- dbBase.h: Base pointers for database structures
- choice.h: Choice Structures (Cvt, Gbl, Rec, and Dev)
- cvtTable.h: Conversion Structures
- dbAccess.h: Runtime database access definitions
- dbDefs.h: Basic database related definitions
- dbFldTypes.h: Field type definitions
- dbRecDes.h: Record and field description structures
- dbRecType.h: Record type structures
- dbRecords.h: Database record locations structures
- devSup.h: Device support structures
- drvSup.h: Driver support structures
- link.h: Link structures
- recSup.h: Record support structures
- sdrHeader.h: Self Defining Record header structures
dbDefs.h, dbFldTypes.h, and dbBase.h are discussed in this section. dbAccess.h is discussed with runtime database access. The other include files are of interest to someone needing details about the database structures. See last chapter for details.
Fundamental Database Definitions
dbDefs.h
This file contains a number of database related definitions. The most important are:
- PVNAME_SZ: The number of characters allowed in the record name.
- FLDNAME_SZ: The number of characters allowed in a field name. This has the value 4. The process variable directory routines depend on this value because they treat the value as an unsigned long data type.
- MAX_STRING_SIZE: The maximum string size for string fields or menu choices.
- DB_MAX_CHOICES: The maximum number of choices for a choice field.
dbFldTypes.h
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:
- DBF_STRING: ASCII character string
- DBF_CHAR: Signed character
- DBF_UCHAR: Unsigned character
- DBF_SHORT: Short integer
- DBF_USHORT: Unsigned short integer
- DBF_LONG: Long integer
- DBF_ULONG: Unsigned long integer
- DBF_FLOAT: Floating point number
- DBF_DOUBLE: Double precision float
- DBF_ENUM: An enumerated field
- DBF_GBLCHOICE: A global choice field
- DBF_CVTCHOICE: A conversion choice field
- DBF_RECCHOICE: A record specific choice field
- DBF_DEVCHOICE: A device choice field
- DBF_INLINK: Input Link
- DBF_OUTLINK: Output Link
- DBF_FWDLINK: Forward Link
- DBF_NOACCESS: A private field for use by record access routines
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_DEVCHOICE 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:
- Constant - The value associated with the field is a floating point value initialized with a constant value. This is somewhat of a misnomer because constant link fields can be modified via dbPutField or dbPutLink.
- Hardware links - The link contains a data structure which describes a signal connected to a particular hardware bus. See link.h for a description of the bus types currently supported.
- Process Variable Links - This is one of three types:
- PV_LINK: The process variable name.
- DB_LINK: A reference to a process variable in the same IOC.
- CA_LINK: A reference to a variable located in another IOC.
DCT always creates 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.
dbBase.h
The database and all its associated structures are located via the following set of variables defined in structure dbBase (defined in dbBase.h):
struct dbBase {
struct choiceSet *pchoiceCvt;
struct arrChoiceSet *pchoiceGbl;
struct choiceRec *pchoiceRec;
struct devChoiceRec *pchoiceDev;
struct arrBrkTable *pcvtTable;
struct recDes *precDes;
struct recType *precType;
struct recHeader *precHeader;
struct recDevSup *precDevSup;
struct drvSup *pdrvSup;
struct recSup *precSup;
struct pvd *pdbPvd; /* DCT pvd - remove when DCT goes away */
void *ppvd;/* pointer to process variable directory*/
char *pdbName;/* pointer to database name*/
struct sdrSum *psdrSum;/* pointer to default sum */
long sdrFileSize; /*size of default.dctsdr file*/
long pvtSumFlag; /*internal use only*/
};
With the exception of record and device support, all access to the database is via the channel or 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.
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. It may seem strange that the structure of the IOC database is not described at this point but this would result in confusing detail. The structure is explained in the last chapter.
Before describing database access, one caution must be mentioned. The only way to communicate with an IOC database from outside the IOC is via Channel Access. In addition, any special purpose software, i.e. any software not described in this document, should communicate with the database via Channel Access, not database access, even if it resides in the same IOC 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 changed in 1991, but Channel Access was never changed. Instead a module was written which translates old style database access calls to new. This interface between the old and new style database access calls is discussed in the last section of this chapter.
The database access routines are:
- dbCommonInit Initialize database common
- dbNameToAddr: Locate a database variable.
- dbGetField: Get values associated with a database variable.
- dbGetLink: Get value of field referenced by database link
- dbFastLinkGet: Fast get value of field referenced by database link
- dbGet: Routine called by dbGetLink and dbGetField and dbCa functions
- dbPutField: Change the value of a database variable.
- dbPutLink: Change value referenced by database link
- dbFastLinkPut: Fast change value referenced by database link
- dbPutNotify: A database put with notification on completion
- dbNotifyCancel: Cancel dbPutNotify
- dbPut: Routine called by dbPutxxx and by the dbCa functions.
- dbBufferSize: Determine number of bytes in request buffer.
- dbValueSize: Number of bytes for a value field.
- dbScanPassive: Process record if passive
- dbProcess: Process Record
- dbScanLockInit: Initialize scan locking
- dbScanLock: Lock database
- dbScanUnlock: Unlock database
- dbCaAddInLink: Initialize a channel access database input link
- dbCaAddOutLink: Initialize a channel access database output link
- dbCaGetLink: Get the current value for a channel access database input link
- dbCaPutLink: Put a value to a channel access database output link
Database Request Types and Options
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:
- DBR_STRING: returns a NULL terminated string
- DBR_CHAR: returns a signed char
- DBR_UCHAR: returns an unsigned char
- DBR_SHORT: returns a short integer
- DBR_USHORT: returns an unsigned short integer
- DBR_LONG: returns a long integer
- DBR_ULONG: returns an unsigned long integer
- DBR_FLOAT: returns an IEEE floating point value
- DBR_DOUBLE: returns an IEEE double precision floating point value
- DBR_ENUM: returns a short which is the enum item
The request types DBR_STRING, ..., DBR_DOUBLE correspond exactly to valid data types for database fields. DBR_ENUM corresponds to database fields that represent a set of choices or options. In particular it corresponds to the fields types DBF_ENUM, DBF_DEVCHOICE, DBF_CVTCHOICE, DBF_GBLCHOICE, and DBF_RECCHOICE. The complete set of database field types are defined in dbFldTypes.h.
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:
- DBR_STATUS: returns the alarm status and severity
- DBR_UNITS: returns a string specifying the engineering units
- DBR_PRECISION: returns a long integer specifying floating point precision.
- DBR_TIME: returns the time
- DBR_ENUM_STRS: returns an array of strings
- DBR_GR_LONG: returns graphics info as long values
- DBR_GR_DOUBLE: returns graphics info as double values
- DBR_CTRL_LONG: returns control info as long values
- DBR_CTRL_DOUBLE: returns control info as double values
- DBR_AL_LONG: returns alarm info as long values
- DBR_AL_DOUBLE: returns alarm info as double values
dbAccess.h
Before describing the routines a few data structures must be described. The structures are defined in dbAccess.h. The first structure is dbAddr.
struct dbAddr{
struct dbCommon *precord: /* address of record */
void *pfield; /* address of field */
void *pfldDes; /* address of struct fldDes */
long no_elements; /* number of elements (arrays) */
short record_type; /* type of record being accessed */
short field_type; /* type of database field */
short field_size; /* size (bytes) of the field being
accessed */
short special; /* special processing */
short choice_set; /* index of choiceSet GBLCHOICE &
RECCHOICE*/
short dbr_field_type} /* field type as seen by database
request DBR_STRING, ...,
DBR_ENUM, DBR_NOACCESS*/
- precord: Address of record. Note that its type is a pointer to a structure defining the fields common to all record types. The common fields appear at the beginning of each record. A record support module can cast precord to point to the specific record type.
- pfield: Address of the field within the record. Note that pfield provides direct access to the data value.
- pfldDes: This points to a structure containing all details concerning the field. See "Database Structures" on page 101 for a description of this structure.
- no_elements: A string or numeric field can be either a scalar or an array. For scalar fields no_elements has the value 1. For array fields it is the maximum number of elements that can be stored in the array.
- record_type: An index specifying the record type. See "Database Structures" on page 101 for how this is used.
- special: Some fields require special processing. This specifies the type. Special processing is described later in this manual.
- choice_set: For global and record choice fields (described below), this specifies a choice set.
- dbr_field_type: This specifies the optimal database request type for this field, i.e. the request type that will require the least CPU overhead.
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 is the database request type that most closely matches the database field type. Using this request type will put the smallest load on the IOC.
Channel Access provides routines similar to dbGetField, and dbPutField. It provides remote access to dbGetField, dbPutField, and to the database monitors described below.
Database Access Routines
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:
- Call dbNameToAddr once and only once for each field to be accessed.
- Read field values via 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.
dbNameToAddr
Locate a process variable, format:
dbNameToAddr(
char *pname, /*ptr to process variable name */
struct dbAddr *paddr);
Given a process variable name, this routine locates the process variable and fills in the fields of structure dbAddr. The process variable name is of the form "<record_name>.<field_name>". For example the value field of a record with record name `sample_name" is "sample_name.VAL". Note that the name is case sensitive. All field names are all upper case letters.
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.
dbCommonInit
This routine is called by iocInit to initialize fields in database common. It is only included for completeness.
dbGetField
Get values associated with a process variable, format:
dbGetField(
struct dbAddr *paddr,
short dbrType, /* DBR_xxx */
void *pbuffer, /*addr of returned data */
long *options, /*addr of options */
long *nRequest, /*addr of number of elements */
void *pfl); /*used by monitor routines */
Thus routine locks, calls dbGet, and unlocks.
dbGetLink
Get value from the field referenced by a database link, format:
dbGetLink(
struct db_link *pdbLink, /*addr of database link */
struct dbCommon *pdest, /*addr of destination record */
short dbrType, /* DBR_xxx */
void *pbuffer, /*addr of returned data */
long *options, /*addr of options */
long *nRequest); /*addr of number of elements desired*/
This routine is called by database access itself and by record support and/or device support routines in order to get values from other database records via input links. It calls dbGet to obtain data and also implements the process passive and maximize severity link options.
dbFastLinkGet
dbFastLinkGet(
struct link *plink,
struct dbCommon *precord,
void *pdest);
This routine gets a value from an input link to pdest. Do not call this routine unless you have a properly initialized Channel Access or database link. This routine is not intended to be called directly by record support, use recGblGetFastLink() instead.
dbGet
Get values associated with a process variable, format:
dbGet(
struct dbAddr *paddr,
short dbrType, /* DBR_xxx */
void *pbuffer, /*addr of returned data
long *options, /*addr of options */
long *nRequest, /*addr of number of elements */
void *pfl); /*used by monitor routines */
Thus routine retrieves the data referenced by paddr and converts it to the format specified by dbrType.
"options" is a read/write field. Upon entry to dbGet, options specifies the desired options. When dbGetField returns, options specifies the options actually honored. If an option is not honored, the corresponding fields in 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 equals the actual number of elements returned. It is permissible to request zero elements. This is useful when only option data is desired.
"pfl" is a field used by the Channel Access monitor routines. All other users must set pfl=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.
dbPutField
Change the value of a process variable, format:
dbPutField(
struct dbAddr *paddr,
short dbrType, /* DBR_xxx*/
void *pbuffer, /*addr of 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:
- If the DISP field is TRUE then, unless it is the DISP field itself which is being modified, the field is not written.
- The record is locked.
- dbPut is called.
- If the 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.
- If the record is already active ask for the record to be reprocessed when it completes.
- Call dbScanPassive after setting putf TRUE to show the process request came from dbPutField.
- The record is unlocked.
dbPutLink
Change the value referenced by a database link, format:
dbPutLink(
struct db_link *pdbLink, /*addr of database link */
struct dbCommon *psource, /*addr of source record */
short dbrType, /* DBR_xxx*/
void *pbuffer, /*addr of data to write */
long nRequest); /*number of elements to write*/
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. It performs the following functions:
- Calls dbPut.
- Implements maximize severity.
- If the field being referenced is PROC or if both of the following are true: 1) process_passive is TRUE and 2) the record is passive then:
- If the record is already active because of a dbPutField request then ask for the record to be reprocessed when it completes.
- otherwise call dbScanPassive.
dbFastLinkPut
Fast putLink, forward:
dbFastLinkPut(
struct link *plink,
struct dbCommon *precord,
void *psource);
This routine puts the value from psource to an output link. Do not call this routine unless you have a properly initialized Channel Access or database link. This routine is not intended to be called directly by record support, use recGblPutFastLink() instead.
dbPutNotify
Put And Notify When Complete, format:
typedef struct putNotify{
/*The following members MUST be set by user*/
void (*userCallback)(struct putNotify *);
struct dbAddr *paddr; /*dbAddr set by dbName ToAddr*/
void *pbuffer; /*address of data*/
long nRequest; /*number of elements to be written*/
short dbrType; /*database request type*/
void *usrPvt; /*for private use of user*/
/*The following is status of request. Set by dbPutNotify*/
long status;
/*The following are private to database access*/
CALLBACK callback;
void *list; /*list of records for which to wait*/
int nwaiting;
notifyCmd cmd;
unsigned char rescan; /*Should dbPutNotify be called again*/
}PUTNOTIFY;
long dbPutNotify(PUTNOTIFY *pputnotify);
void dbNotifyCancel(PUTNOTIFY *pputnotify);
The following routine is used only by old database access.
int dbPutNotifyMapType(PUTNOTIFY *pputnotify,short dbr_type)
The status value stored in PUTNOTIFY can be one of the following:
- 0: Success: Callback was already called.
- S_db_Pending: Success: Callback will be called later.
- S_db_Blocked: The request failed because a dbPutNotify conflict occurred.
- S_xxxx: The request failed due to some other error.
dbPutNotify is a request to notify the caller when all records that are processed as a result of the put complete processing. The complication occurs because of asynchronous records. The following is true:
- The user supplied callback is called when all processing is complete or when a S_db_Blocked is detected. If everything completes synchronously the callback routine will be called BEFORE dbPutNotify returns. The user supplied callback routine must not issue any calls that block such as Unix I/O requests.
- In general a set of records may need to be processed as a result of a single dbPutNotify. If database access detects that another dbPutNotify request has resulted in a record in the set being already active then the user callback is called with status=S_db_Blocked.
- If a record in the set is found to be active because of a dbPutField request then when the record completes a new dbPutNotify will be issued.
- If a record is found to be active for some other reason then nothing is done. This is what is done now and any attempt to do otherwise could easily cause existing databases to go into an infinite processing loop.
- It is expected that the caller will arrange a timeout in case the dbPutNotify takes too long. In this case the caller can call dbNotifyCancel.
dbPut
Put a value to a database field, format:
dbPut(
struct dbAddr *paddr,
short dbrType, /* DBR_xxx*/
void *pbuffer, /*addr of 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 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.
dbBufferSize
Determine the buffer size for a dbGetField request, format:
long dbBufferSize(
short dbrType, /* DBR_xxx*/
long options, /* options mask*/
long nRequest); /* 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
dbValueSize
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
dbScanPassive
Process record if it is passive, format:
dbScanPassive(
struct dbCommon *pfrom,
struct dbCommon *pto); /* addr of record*/
This request specifies the record requesting the scan, which may be NULL, and the record to be processed. If the record is passive and pact=FALSE then dbProcess is called. Note that this routine is called by dbGetLink, dbPutField, and by record processing routines for forward links. In addition to calling dbProcess this routine is responsible for creating lists needed for dbPutNotify.
dbProcess
Request that a database record be processed, format:
dbProcess(struct dbCommom *precord); /* addr of record*/
Request that record be processed. Record processing is described in detail below.
Database Locking
Database locking is described in the next chapter. For now lets just state that a database record must be locked before it is modified and unlocked afterwards.
The routines provided are:
dbScanLockInit( /* called only by iocInit*/
int nset); /* number of lock sets*/
dbScanLock(struct dbCommon *precord); /*addr of record*/
dbScanUnlock(struct dbCommon *precord); /*addr of record*/
Channel Access Database Links
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.
The routines provided are:
dbCaAddInLink(
struct link *plink,
void *precord,
char *pfieldName);
dbCaAddOutLink(
struct link *plink,
void *precord,
char *pfieldName);
dbCaGetLink(
struct link *plink);
dbCaPutLink(
struct link *plink);
For a description of these routines see:
Links in a Distributed database: Theory and Implementation,
Nicholas T. Karonis and Martin R. Kraimer, December 1991
Channel Access has not yet been modified to support the database access routines described above. The database access interface was changed because as more database field types and request options were defined the previous database access interface become harder and harder to modify. In order to make the transition to the new database access without obsoleting all software that used Channel Access an interface module was written. Thus module translates old database calls to new. Several of the Channel Access arguments directly map to database access arguments. Thus existing Channel Access clients use the old database access interface.
Since this manual concentrates on IOC software, this is not the place to describe the old database interface. Other documents describe it. The header file db_access.h also provides descriptive information.
Table of Contents Next Chapter