Chapter 15
Runtime Database Access

 15.1 Overview
 15.2 Database Include Files
  15.2.1 dbDefs.h
  15.2.2 dbFldTypes.h
  15.2.3 dbAccess.h
  15.2.4 dbServer.h
  15.2.5 link.h
 15.3 Runtime Database Access Overview
  15.3.1 Database Request Types and Options
  15.3.2 Options
  15.3.3 ACKT and ACKS
 15.4 Database Access Routines
  15.4.1 dbNameToAddr
  15.4.2 Get Routines
  15.4.3 Put Routines
  15.4.4 Process Notify Subsystem
  15.4.5 Utility Routines
  15.4.6 Attribute Routine
  15.4.7 Process Routines
 15.5 Runtime Link Modification
 15.6 Channel Access Monitors
 15.7 Lock Set Routines
  15.7.1 dbScanLock
  15.7.2 dbScanUnlock
  15.7.3 dbLockGetLockId
  15.7.4 dbLockInitRecords
  15.7.5 dbLockSetMerge
  15.7.6 dbLockSetSplit
  15.7.7 dbLockSetGblLock
  15.7.8 dbLockSetGblUnlock
  15.7.9 dbLockSetRecordLock
 15.8 Channel Access Database Links
  15.8.1 Basic Routines
  15.8.2 Attributes of Link
 15.9 dbServer API
  15.9.1 Registering a server layer
  15.9.2 Interacting with Server layers

15.1 Overview

This chapter describes routines for manipulating and accessing an initialized IOC database.

This chapter is divided into several sections:

15.2 Database Include Files

There are a number of database related include files. Of particular interest to this chapter are:

15.2.1 dbDefs.h

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.

15.2.2 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:

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:

  1. 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.
  2. 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.
  3. Process Variable Links - This is one of three types:

    1. PV_LINK: The process variable name.
    2. DB_LINK: A reference to a process variable in the same IOC.
    3. CA_LINK: A reference to a variable located in another IOC.

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.

15.2.3 dbAccess.h

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;

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.

15.2.4 dbServer.h

This header provides an optional API allowing the IOC to display information about the services that are connecting to and using the IOC.

15.2.5 link.h

This header file describes the various types of link fields supported by EPICS.

15.3 Runtime Database Access Overview

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:

15.3.1 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:

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:

15.3.2 Options

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.

15.3.3 ACKT and ACKS

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:

DBR_PUT_ACKT
- Do transient alarms have to be acknowledged? 0 means no, 1 means yes
DBR_PUT_ACKS
- The highest alarm severity to acknowledge. If the current alarm severity is less then or equal to this value the alarm is acknowledged.

15.4 Database Access Routines

15.4.1 dbNameToAddr

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:

  1. Call dbNameToAddr once and only once for each field to be accessed.
  2. 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.

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.

15.4.2 Get Routines

15.4.2.1 dbGetField

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.

15.4.2.2 dbGetLink and dbGetLinkValue

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 ⋆/

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.

15.4.2.3 dbGet

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.

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.

15.4.3 Put Routines

15.4.3.1 dbPutField

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:

  1. If the DISP field is TRUE then, unless it is the DISP field itself which is being modified, the field is not written.
  2. The record is locked.
  3. dbPut is called.
  4. 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.

    1. If the record is already active, ask for the record to be reprocessed when it completes.
    2. Call dbScanPassive after setting putfTRUE to show the process request came from dbPutField.
  5. The record is unlocked.

15.4.3.2 dbPutLink and dbPutLinkValue

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:

  1. Calls dbPut.
  2. Implements maximize severity.
  3. 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:

    1. If the record is already active because of a dbPutField request then ask for the record to be reprocessed when it completes.
    2. otherwise call dbScanPassive.

15.4.3.3 dbPut

Put a value to a database field, format:

long dbPut( 
    struct dbAddr  paddr, 
    shortdbrType,/⋆ DBR_xxx⋆/ 
    voidpbuffer,/⋆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.

15.4.4 Process Notify Subsystem

15.4.4.1 Introduction

The Process Notify subsystem provides the following features:

  1. Processes a record with notification of completion.
  2. Put after the record is claimed but before before the process.
  3. Get after the process but before the record is released.
  4. put, then process, then get.

A process request will be issued if any of the following is true:

At most one process is performed per dbProcessNotify request.

15.4.4.2 dbNotify.h

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);

15.4.4.3 Client Semantics

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.

15.4.4.4 Callback Device Support

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.

15.4.5 Utility Routines

15.4.5.1 dbBufferSize

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

15.4.5.2 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

15.4.5.3 dbGetRset

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.

15.4.5.4 dbIsValueField

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.

15.4.5.5 dbGetFieldIndex

Get field index.

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).

15.4.5.6 dbGetNelements

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.

15.4.5.7 dbIsLinkConnected

Is the link connected.

Format:

int dbIsLinkConnected(struct link plink);

This routine returns (TRUE, FALSE) if the link (is, is not) connected.

15.4.5.8 dbGetPdbAddrFromLink

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.

15.4.5.9 dbGetLinkDBFtype

Get field type of a link.

Format:

int dbGetLinkDBFtype(struct link plink);

15.4.5.10 dbGetControlLimits

Get Control Limits for link.

Format:

long dbGetControlLimits(struct link plink,double low, double high);

15.4.5.11 dbGetGraphicLimits

Get Graphic Limits for link.

Format:

long dbGetGraphicLimits(struct link plink,double low, double high);

15.4.5.12 dbGetAlarmLimits

Get Alarm Limits for link.

Format:

long dbGetAlarmLimits(struct link plink, 
 
double lolo,double low, double high,double hihi);

15.4.5.13 dbGetPrecision

Get Precision for link.

Format:

long dbGetPrecision(struct link plink,short precision);

15.4.5.14 dbGetUnits

Get Units for link.

Format:

long dbGetUnits(struct link plink,char units,int unitsSize);

15.4.5.15 dbGetSevr

Get Severity for link.

Format:

long dbGetSevr(struct link plink,short sevr);

15.4.5.16 dbGetTimeStamp

Get Time Stamp for record containing link.

Format:

long dbGetTimeStamp(struct link plink,TS_STAMP pstamp);

15.4.6 Attribute Routine

15.4.6.1 dbPutAttribute

Give a value to a record attribute.

long dbPutAttribute(char recordTypename, 
    char name,charvalue);

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");

15.4.7 Process Routines

15.4.7.1 dbScanPassive

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.

15.4.7.2 dbProcess

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.

15.5 Runtime Link Modification

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:

15.6 Channel Access Monitors

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.

15.7 Lock Set Routines

User code only calls dbScanLock and dbScanUnlock. All other routines are called by iocCore.

15.7.1 dbScanLock

Lock a lock set:

long void dbScanLock(struct dbCommon precord);

Lock the lock set to which the specified record belongs.

15.7.2 dbScanUnlock

Unlock a lock set:

long void dbScanUnlock(struct dbCommon precord);

Lock the lock set to which the specified record belongs

15.7.3 dbLockGetLockId

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.

15.7.4 dbLockInitRecords

Determine lock sets for each record in database.

void dbLockInitRecords(dbBase pdbbase);

Called by iocInit.

15.7.5 dbLockSetMerge

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.

15.7.6 dbLockSetSplit

Recompute lock sets for given lock set

void dbLockSetSplit(struct dbCommon psource);

This is called when dbPutField modifies links.

15.7.7 dbLockSetGblLock

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.

15.7.8 dbLockSetGblUnlock

Unlock the global lock.

void dbLockSetGblUnlock(void);

15.7.9 dbLockSetRecordLock

If record is not already scan locked lock it.

void dbLockSetRecordLock(struct dbCommon precord);

15.8 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. 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.

15.8.1 Basic Routines

Except for dbCaPutLinkCallback, these routines are normally only called by database access, i.e. they are not called by record support modules.

15.8.1.1 dbCaLinkInit

Called by iocInit to initialize the dbCa library

void dbCaLinkInit(void);

15.8.1.2 dbCaAddLink

Add a new channel access link

void dbCaAddLink(struct link plink);

15.8.1.3 dbCaAddLinkCallback
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.

15.8.1.4 dbCaRemoveLink

Remove channel access link.

void dbCaRemoveLink(struct link plink);

15.8.1.5 dbCaGetLink

Get link value

long dbCaGetLink(struct link plink,short dbrType, 
    void pbuffer,unsigned short psevr,long nRequest);

15.8.1.6 dbCaPutLink

Put link value

long dbCaPutLink(struct link plink,short dbrType, 
    void buffering nRequest);

15.8.1.7 dbCaPutLinkCallback

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:

When the record is processed write_ao is called with pact=0.
If the link is not a CA_LINK it just calls dbPutLink. It leaves pact 0. Thus record support completes.
If it is a CA_LINK it calls dbCaPutLinkCallback and sets pact true. Thus record is asynchronous.
If the record is asynchrnous then sometime later dbCaCallbackProcess is called. It calls the process routine of record support, which calls write_ao with pact true. write_ao just returns success. Record support then completes the second phase of record processing.

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.

15.8.2 Attributes of Link

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.

15.8.2.1 dbCaIsLinkConnected

Is Channel Connected

int dbCaIsLinkConnected(struct link plink)

This routine returns (TRUE, FALSE) if the link (is, is not) connected.

15.8.2.2 dbCaGetNelements

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.

15.8.2.3 dbCaGetSevr

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.

15.8.2.4 dbCaGetTimeStamp

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.

15.8.2.5 dbCaGetLinkDBFtype

Get link type

int dbCaGetLinkDBFtype(struct link plink);

This call, which returns an error if the link is not connected, returns the field type.

15.8.2.6 dbCaGetAttributes

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.

15.8.2.7 dbCaGetControlLimits

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.

15.8.2.8 dbCaGetGraphicLimits

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.

15.8.2.9 dbCaGetAlarmLimits

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.

15.8.2.10 dbCaGetPrecision

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.

15.8.2.11 dbCaGetUnits

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.

15.9 dbServer API

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.

15.9.1 Registering a server layer

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.

15.9.2 Interacting with Server layers

The dbServer.h header makes the following routines available to the IOC code.

15.9.2.1 dbsr - Server Report
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.

15.9.2.2 dbServerClient - Identifying a client thread
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.