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:
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
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:
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:
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.
The associated dbGetField call is:
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:
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:
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:
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:
This routine locks, calls dbGet, and unlocks.
Get value from the field referenced by a database link, format:
NOTES:
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:
This routine retrieves the data referenced by paddr and converts it to the format specified by dbrType.
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:
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 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.
Change the value referenced by a database link, format:
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:
Put a value to a database field, format:
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:
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:
The notifyPutType argument to putCallback is one of these values:
The notifyGetType argument to getCallback will be one of these values:
The notifyStatus argument to doneCallback is one of these values:
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:
The output types issue a channel access ca_put_callback request.
Determine the buffer size for a dbGetField request, format:
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:
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:
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:
This is the routine that makes the get_value record support routine obsolete.
Get field index.
Format:
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:
This sets *nelements to the number of elements in the field referenced by plink.
Is the link connected.
Format:
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.
Get field type of a link.
Format:
Get Control Limits for link.
Format:
Get Graphic Limits for link.
Format:
Get Alarm Limits for link.
Format:
Get Precision for link.
Format:
Get Units for link.
Format:
Get Severity for link.
Format:
Get Time Stamp for record containing link.
Format:
Give a value to a record attribute.
This sets the record attribute name for record type recordTypename to value. For example the following would set the version for the ai record.
dbScanLink
dbScanFwdLink
Process record if it is passive, format:
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:
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:
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.
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:
Lock the lock set to which the specified record belongs.
Unlock a lock set:
Lock the lock set to which the specified record belongs
Get lock set id:
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.
Called by iocInit.
Merge records into same lock set.
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
This is called when dbPutField modifies links.
Global lock for modifying links.
Only one task at a time can modify link fields. This routine provides a global lock to prevent conflicts.
Unlock the global lock.
If record is not already scan locked lock it.
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
Add a new channel access link
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.
Get link value
Put link value
This is meant for use by device or record support that wants a put to complete before completing record processing.
<base>/src/std/dev/devAoSoftCallback.c provides an example of how to use this function. It contains:
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
This routine returns (TRUE, FALSE) if the link (is, is not) connected.
Get Number of Elements
This call, which returns an error if the link is not connected, sets the native number of elements.
Get Alarm Severity
This call, which returns an error if the link is not connected, sets the alarm severity.
Get Time Stamp
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
This call, which returns an error if the link is not connected, returns the field type.
Get Attributes
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
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
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
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
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
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:
A server layer should instantiate one of these and pass a pointer to it to dbRegisterServer:
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.
The dbServer.h header makes the following routines available to the IOC code.
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.
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.