Table of Contents Previous Chapter Chapter 5: Static Database Access

Chapter 5: Static Database Access

1. Overview

An IOC database is created on a Unix system via a Database Configuration Tool and stored in a Unix file. Two flavors of Unix files are supported: a binary file which uses an extension of .database, and an ASCII format which uses an extension of .db (it is recommended that only the .db format be used). A database file is loaded into an IOC via a dbLoad command and initialized via the iocInit command. A database file contains a number of self defining records which are described later in this manual. EPICS provides two sets of database access routines: Static Database Access and Runtime Database Access. Static database access can be used on Unix or IOC database files. Runtime database access only works on initialized IOC databases. Static database access is described in this chapter and runtime database access in the next chapter.

Static database access provides a simplified interface to a database, i.e. much of the complexity is hidden. All choice fields are accesses via a common type called DCT_MENU. A set of routines are provided to simplify access to link fields. All fields can be accessed as character strings. This interface is called static database access because it can be used to access an uninitialized, as well as an initialized database.

Database Configuration Tools (DCTs) should manipulate an EPICS database only via the static database access interface. An IOC database is created on a Unix system via a database configuration tool and stored in a Unix file with a file extension of ".database". Two routines (dbRead and dbWrite) access a Unix database file. These routines read/write a database file to/from a memory resident EPICS database. All other access routines manipulate the memory resident database.

An include file dbStaticLib.h contains all the definitions needed to use the static database access library. Two structures (DBBASE and DBENTRY) are used to access a database. The fields in these structures should not be accessed directly. They are used by the static database access library to keep state information for the caller.

2. Definitions

DBBASE

Multiple memory resident databases can be accessed simultaneously. The user must provide definitions in the form:

  #include <dbStaticLib.h>
  DBBASE  *pdb;
  pdb=dbAllocBase();

DBENTRY

A typical declaration for a database entry structure is:

  DBENTRY  *pentry;
  pentry=dbAllocEntry(pdb);
Most static access to a database is via a DBENTRY structure. As many structures as desired can be allocated, each associated with a particular database. The user should NEVER access the fields of DBENTRY directly.

Most access routines accept an argument which contains the address of a DBENTRY. Each routine uses this structure to locate the information it needs and gives values to as many fields in this structure as possible. All other fields are set to NULL.

Database Configuration Field Types

Each database field has a type as defined in the previous chapter. For static database access a new and simpler set of field types are defined. This allows a simpler interface definition. In addition some database fields can be arrays. For DCT, however, all fields are scalars.

The DCT field types are:

A DCT_STRING field contains the address of a NULL terminated ASCII string. The field types DCT_INTEGER and DCT_REAL are used for numeric fields. A field that has any of these types can be accessed via the dbGetString, dbPutString, dbVerify, and dbGetRange routines.

The field type DCT_MENU has an associated set of ASCII strings defining the choices. Routines are available for accessing menu fields. A menu field can also be accessed via the dbGetString, dbPutString, dbVerify, and dbGetRange routines.

The field type DCT_MENUFORM is like DCT_MENU but in addition the field has an associated link field. The information for the link field can be entered via a set of form manipulation fields.

DCT_INLINK (input), DCT_OUTLINK (output), and DCT_FWDLINK (forward) specify that the field is a link structure as defined in dbStaticLib.h. Link fields, which have an associated set of static access routines, are described in more detail in the next subsection. A field that has any of these types can also be accessed via the dbGetString, dbPutString, dbVerify, and dbGetRange routines.

Link Types

Links are the most complicated types of fields. A link can be a constant, reference a field in another record, or can refer to a hardware device. Two additional complications arise for hardware links. The first is that field DTYP, which is a menu field, determines if the INP or OUT field is a device link. The second is that the information that must be specified for a device link is bus dependent. In order to shelter database configuration tools from these complications the following is done for static database access.

Each link is one of the following types:

Database configuration tools can change any link between being a constant and a process variable link. Routines are provided to accomplish these tasks. A device link can be given values via the form routines described below.

The routines dbGetString, dbPutString, and dbVerify can be used for link fields but should not be used to prompt the user for PV or DEVICE links. They are meant to be used for constant links or to save and restore ASCII versions of the database.

3. EXAMPLE - Dump All Records

The following example demonstrate how to use the database access routines. If this is the first time you are reading this manual just quickly look at the example and then come back to it after reading the rest of the chapter. The example shows how to locate each record and display each field.

void dbDumpRecords(DBBASE *pdbbase)
{
  DBENTRY  *pdbentry;
  long  status;
  pdbentry = dbAllocEntry(pdbbase);
  status = dbFirstRecdes(pdbentry);
  if(status) {printf("No record descriptions\n");return;}
  while(!status) {
    printf("record type: %s",dbGetRecdesName
      (pdbentry));
    status = dbFirstRecord(pdbentry);
    if(status) printf("  No Records\n"); 
    else printf("\n  Record: %s\n",
      dbGetRecordName(pdbentry));
    while(!status) {
      status = dbFirstFielddes(pdbentry,TRUE);
      if(status) printf("    No Fields\n");
      while(!status) {
        printf("    %s:%s",dbGetFieldName(pdbentry),
          dbGetString(pdbentry));
        status=dbNextFielddes(pdbentry,TRUE);
      }
      status = dbNextRecord(pdbentry);
    }
    status = dbNextRecdes(pdbentry);
  }
  printf("End of all Records\n");
  dbFreeEntry(pdbentry);
}

4. Allocating and Freeing Structures

  DBBASE *dbAllocBase(void);
  void dbFreeBase(DBBASE *pdbbase);
  DBENTRY *dbAllocEntry(DBBASE *pdbbase);
  void dbFreeEntry(DBENTRY *pdbentry);
  void dbInitEntry(DBBASE *pdbbase,DBENTRY *pdbentry);
  void dbFinishEntry(DBENTRY *pdbentry);
  DBENTRY *dbCopyEntry(DBENTRY *pdbentry);
These routines allocate and free DBBASE and DBENTRY structures. dbAllocBase allocates and initializes a memory resident database. dbFreebase frees all memory used by the database. Note that it is possible to work with more then one memory resident database at the same time.

The user can allocate and free DBENTRY structures as necessary. Each DBENTRY is, however, tied to a particular database.

The routines dbInitEntry and dbFinishEntry are provided in case the user wants to allocate a DBENTRY structure on the stack.

The routine dbCopyEntry allocates a new entry, via a call to dbAllocEntry, copies the information from the original entry, and returns the result.

5. Read and Write Database

  long dbRead(DBBASE *pdbbase,FILE *fp);
  long dbWrite(DBBASE *pdbbase,FILE *fpdctsdr,FILE *fp);
dbRead reads a file containing any combination of self defining records and adds the information to the memory resident database. dbWrite writes the memory resident database into a file. dbWrite requires two file pointers. The first is a file containing record description information. The second references the output database file.

Although an arbitrary number of database files can be read each must contain the same set of record descriptions. If any mismatch occurs dbRead will return an error. If dbRead returns a non zero value do not call any of the other routines described in this chapter.

6. Manipulating Record Descriptions

  long dbFindRecdes(DBENTRY *pdbentry,char *recdesname);
  long dbFirstRecdes(DBENTRY *pdbentry);
  long dbNextRecdes(DBENTRY *pdbentry);
  char *dbGetRecdesName(DBENTRY *pdbentry);
  int  dbGetNRecdes(DBENTRY *pdbentry);
  long dbCopyRecdes(DBENTRY *from,DBENTRY *to);
These routines manipulate the record description. An EPICS database consists of an arbitrary number of record descriptions. The above routines provide access to the following information:

7. Manipulating Record Instances

  long dbCreateRecord(DBENTRY *pdbentry,char *precordName);
  long dbDeleteRecord(DBENTRY *pdbentry);
  long dbFindRecord(DBENTRY *pdbentry,char *precordName);
  long dbFirstRecord(DBENTRY *pdbentry); /*first of record type*/
  long dbNextRecord(DBENTRY *pdbentry);
  int  dbGetNRecords(DBENTRY *pdbentry);
  char *dbGetRecordName(DBENTRY *pdbentry);
  long dbRenameRecord(DBENTRY *pdbentry, char *newname)
  long dbCopyRecord(DBENTRY *from,DBENTRY *to);
These routines are used to create, delete, locate, etc. record instances. Note that other than dbFindRecord all routines assume that one of the record description routines has been used to locate a record type. dbFindRecord also calls dbFindField if the record name includes an extension.

Routines are provided for accessing the following information:

8. Manipulating Field Descriptions

  long dbFindField(DBENTRY *pdbentry,char *pfieldName);
  long dbFirstFielddes(DBENTRY *pdbentry,int dctonly);
  long dbNextFielddes(DBENTRY *pdbentry,int dctonly);
  int  dbGetFieldType(DBENTRY *pdbentry);
  int  dbGetNFields(DBENTRY *pdbentry,int dctonly);
  char *dbGetFieldName(DBENTRY *pdbentry);
  char *dbGetPrompt(DBENTRY *pdbentry);
  int   dbGetPromptGroup(DBENTRY *pdbentry);
These routines manipulate the field descriptions for a particular record type. Note that if a record instance has previously been located they also update the location of the field itself.

9. Manipulating Field Values

  char *dbGetString(DBENTRY *pdbentry);
  long dbPutString(DBENTRY *pdbentry,char *pstring);
  char *dbVerify(DBENTRY *pdbentry,char *pstring);
  char *dbGetRange(DBENTRY *pdbentry);
  int   dbIsDefaultValue(DBENTRY *pdbentry);
These routines are used to get or change field values. They work on all the database field types except DCT_NOACCESS but should NOT be used to prompt the user for information for DCT_MENU, DCT_MENUFORM, or DCT_LINK_xxx fields. dbVerify returns (NULL, a message) if the string is (valid, invalid). Please note that the strings returned are volatile, i.e. the next call to a routines that returns a string will overwrite the value returned by a previous call. Thus it is the caller's responsibility to copy the strings if the value must be kept.

DCT_MENU, DCT_MENUFORM and DCT_LINK_xxx fields can be manipulated via routines described in the following sections. If, however dbGetString and dbPutString are used they do work correctly. For these field types they are intended to be used only for creating and restoring ASCII versions of a database.

10. Manipulating Menu Fields

MENU and MENUFORM Fields

  char **dbGetChoices(DBENTRY *pdbentry);
  int  dbGetMenuIndex(DBENTRY *pdbentry);
  long dbPutMenuIndex(DBENTRY *pdbentry,int index);
  int  dbGetNMenuChoices(DBENTRY *pdbentry);
  long dbCopyMenu(DBENTRY *from,DBENTRY *to);
These are the routines that should be used for DCT_MENU and DCT_MENUFORM fields.

MENUFORM Fields

These routines are used with a DCT_MENUFORM field (a DTYP field) but actually manipulate an associated field DCT_INLINK or DCT_OUTLINK field.

  int dbAllocForm(DBENTRY *pdbentry)
  long dbFreeForm(DBENTRY *pdbentry)
  char **dbGetFormPrompt(DBENTRY *pdbentry)
  char **dbGetFormValue(DBENTRY *pdbentry)
  long dbPutForm(DBENTRY *pdbentry, char **value)
  char **dbVerifyForm(DBENTRY *pdbentry,char **value)
dbAllocForm allocates storage needed to manipulate forms. The return value is the number of elements in the form. dbGetFormPrompt returns a pointer to an array of pointers to character strings specifying the prompt string. dbGetFormValue returns the current values. dbPutForm, which can use the same array of values returned by dbGetForm, sets new values. dbVerifyForm can be called to verify user input. It returns NULL if no errors are present. If errors are present it returns a pointer to an array of character strings containing error messages. Lines in error have a message and correct lines have a NULL string. The following is skeleton code showing use of these routines:

  char  **value;
  char  **prompt;
  char  **error;
  int  n;
  ...
  n = dbAllocForm(pdbentry);
  if(n<=0) {<Error>}
  prompt = dbGetFormPrompt(pdbentry);
  value = dbGetFormValue(pdbentry);
  for(i=0; i<n; i++) {
    printf("%s (%s) : \n",prompt[i],value[i]);
    scanf("%s",value[i]);
  }
  if(dbPutForm(pdbentry,value)) {
    error = dbVerifyForm(pdbentry,value);
    if(error) for(i=0; i<n; i++) {
      if(error[i]) printf("Error: %s (%s) %s\n", prompt[i], value[i],
         error[i]);
    }
  }
  dbFreeForm(pdbentry);
All value strings are MAX_STRING_SIZE in length.

A set of form calls for a particular DBENTRY, MUST begin with a call to dbAllocForm and end with a call to dbFreeForm. The values returned by dbGetFormPrompt, dbGetFormValue, and dbVerifyForm are valid only between the calls to dbAllocForm and dbFreeForm.

11. Manipulating Link Fields

All Link Fields

  int  dbGetNLinks(DBENTRY *pdbentry);
  long dbGetLinkField(DBENTRY *pdbentry,int index)
  int  dbGetLinkType(DBENTRY *pdbentry);
These are routines for manipulating DCT_xxxLINK fields. dbGetNLinks and dbGetLinkField are used to walk through all the link fields of a record. dbGetLinkType returns one of the values: DCT_LINK_CONSTANT, DCT_LINK_FORM, DCT_LINK_PV, or DCT_LINK_DEVICE.

Constant and Process Variable Links

  long dbCvtLinkToConstant(DBENTRY *pdbentry);
  long dbCvtLinkToPvlink(DBENTRY *pdbentry);
  long dbPutPvlink(DBENTRY *pdbentry,int pp,int ms,char *pvname);
  long dbGetPvlink(DBENTRY *pdbentry,int *pp,int *ms,char *pvname);
These routines should be used for modifying DCT_LINK_CONSTANT or DCT_LINK_PV links. They should not be used for DCT_LINK_FORM or DCT_LINK_DEVICE links, which should be processed via the associated DCT_MENUFORM field described above.

12. Dump Routines

  void dbDumpRecords(DBBASE *pdbbase, char *recdesname,int modOnly);
  void dbDumpPvd(DBBASE *pdbbase);
  void dbReportDeviceConfig(DBBASE *pdbbase,FILE *report)

13. Utility Programs

NOTE: The routines described in this section are provided to translate between the old DCT short form reports and .database files. Everyone is STRONGLY encouraged to start using the GDCT format and use dbLoadRecords and/or dbLoadTemplate to load databases into IOCs. Thus the atdb and dbta should be considered temporary commands. See the GDCT document for details.

atdb

ASCII to Database, format:

  atdb  <dctsdr> <database>
e.g.

  atdb default.dctsdr example.database < example.rpt
This program. which accepts its input from stdin, creates a new database file and populates it with records defined in the ASCII file. The ASCII file is a file in the old DCT short form format or a file generated by the dbta utility with the -s option.

It should also be noted that instead of terminating records with a ^L (the old short form report), it is also permissible to terminate records with $$end. Thus, in addition to short form reports, the following is valid input to atdb:

  PV: <record name>   Type: <record type>
  <field name> <value>
  ...   <as many fields as desired>
  $$end
  PV: <record name>   Type: <record type>
  <field name> <value>
  ...    <as many fields as desired>
  $$end
...
Use dbta, described next, on an existing database without the -s option to see an example.

dbta

Database to ASCII, format:

  dbta [-v] [-s] <filename>
e.g.

  dbta  -s example.database > newexample.rpt
This utility generates an ASCII file from a database file. If -v is specified then all prompt fields are generated, otherwise only fields with non-default values are displayed. If -s is specified, then the generated file can be used as input to the old DCT or to atdb.

 
Table of Contents Next Chapter