Table of Contents Previous Chapter Chapter 12: Database Structures

Chapter 12: Database Structures

1. Overview

This chapter describes the internal structures describing an IOC database. It is of interest to EPICS system developers but serious application developers may also find it useful. This chapter is intended to make it easier to understand the IOC source listings.

The database attributes defined in this chapter are fixed, i.e. they are common to all IOC databases. They are defined via C include files. Any changes to these include files can affect many IOC software components, which will have to be modified and recompiled. A serious reader of this chapter should obtain a listing of all the files in epics/share/epicsH.

In the IOC a single global variable (pdbBase) contains the address of the dbBase structures that defines the run time database. The various structures mentioned in dbBase are described in this chapter. Any IOC source module using the macros and other routines mentioned in this chapter must include a definition:

  extern dbBase *pdbBase;
Then a particular routine accessing a database structure can either reference it via pdbBase or create a local copy which must be initialized via pdbBase.

2. Macros for Accessing Database Structures

This section describes macros that make it easier to access the database structures.

Defined in dbRecType.h

GET_PRECNAME

  GET_PRECNAME (prectype, rec_type)
Typical Usage:

  char *pstr;
  if(!(pstr=GET_PRECNAME(precType,type)) {/*action if not found*/}
This macro returns a pointer to the record name.

Defined in dbRecords.h

GET_PRECLOC

  GET_PRECLOC (precheader, rec_type)
Typical Usage:

  struct recLoc *precLoc;
  if(!(precLoc=GET_PRECLOC(precHeader,type)) {/*action if not found*/}
This macro returns a pointer to the record location structure.

Defined in dbRecDes.h

GET_PFLDDES

  GET_PFLDDES (prectypdes, ind_fld)
Typical Usage:

  struct fldDes *pfldDes;
  if(!(pfldDes=GET_PFLDDES(precTypDes,ind)) {/*action if not found*/}
This macro returns a pointer to the field description structure for a particular field of the record type defined by precTypDes.

GET_PRECTYPDES

  GET_PRECTYPDES (precdes, ind_rec)
Typical Usage:

  struct recTypDes *precTypDes;
  if(!(precTypDes=GET_PRECTYPDES(precDes,ind)) {/*action if not found*/}
This macro returns a pointer to the record type description structure.

Defined in choice.h

GET_CHOICE

  GET_CHOICE (pchoice_set, ind_choice)
Typical Usage:

  char *pchoice;
  if(!(pchoice=GET_CHOICE(pchoiceSet,ind)) {/*action if not found*/}
This macro returns a pointer to the string defining a particular choice.

GET_PCHOICE_SET

  GET_PCHOICE_SET(parr_choice_set, ind_arr)
Typical Usage:

  struct choiceSet *pchoiceSet;
  if(!(pchoiceSet=GET_PCHOICE_SET(parrChoiceSet,ind)) {/*action if not found*/}
This macro returns a pointer to the structure defining a particular choice set.

GET_PARR_CHOICE_SET

  GET_PARR_CHOICE_SET (pchoice_rec, ind_arr)
Typical Usage:

  struct arrChoiceSet *parrChoiceSet;
  if(!(parrChoiceSet=GET_PARR_CHOICE_SET(pChoiceRec,ind)) {/*action*/}
This macro returns a pointer to the structure defining an array of choice sets.

GET_DEV_CHOICE

  GET_DEV_CHOICE (pdev_choice_set, ind_choice)
Typical Usage:

  struct devChoice *pdevChoice;
  if(!(pdevChoice=GET_DEV_CHOICE(pdevChoice,ind)) {/*action*/}
This macro returns a pointer to the structure defining a device choice.

GET_PDEV_
CHOICE_SET

  GET_PDEV_CHOICE_SET (pchoice_dev, ind_rec)
Typical Usage:

  struct devChoiceSet *pdevChoiceSet;
  if(!(pdevChoiceSet=GET_PDEV_CHOICE_SET(pchoiceDev,ind)) {/*action*/}
This macro returns a pointer to the structure defining the device choices for a particular record.

Defined in recSup.h

GET_PRSET

  GET_PRSET (precsup, rec_type)
Typical Usage:

  struct rset *prset;
  if(!(prset=GET_PRSET(precSup,type)) {/*action if not found*/}
This macro returns a pointer to a record support entry table.

Defined in devSup.h

GET_PDSET

  GET_PDSET (pdevsup,dtype)
Typical Usage:

  struct dset *pdset;
  if(!(pdset=GET_PDSET(pdevSup,type)) {/*action if not found*/}
This macro returns a pointer to a device support entry table.

GET_PDEVSUP

  GET_PDEVSUP (precdevsup, rec_type)
Typical Usage:

  struct devSup *pdevSup;
  if(!(pdevSup=GET_PDEVSET(precDevSup,rectype)) {/*action if not found*/}
This macro returns a pointer to a structure defining the ndevice support entry tables for a particular record type.

Defined in drvSup.h

GET_PDRVET

  GET_PDRVET (pdrvsup,type)
Typical Usage:

  struct drvet *pdrvet;
  if(!(pdrvet=GET_PDRVET(pdrvSup,type)) {/*action if not found*/}
This macro returns a pointer to a driver entry table.

GET_PDRVNAME

  GET_PDRVNAME (pdrvsup,type)
Typical Usage:

  char *pdrvName;
  if(!(pdrvName=GET_PDRVNAME(pdrvSup,type)) {/*action if not found*/}
This macro returns a pointer to the driver name.

3. Database Structures

The following is a partial description of various database related structures. The associated include files are located in epics/share/epicsH. The include files themselves should be consulted for a complete description. Each file describing database structures contains the following:

For the purposes of understanding this document it is sufficient to show the structure declarations and the associated memory layout. If you are going to study program listings you should first study the complete include files. In particular become familiar with the macros which access the structures. The actual IOC code almost always uses macros to access the structures.

dbRecType.h - Record Types

This file describes the possible record types. All include files which define structures containing components for multiple record types assume that the record type order is that specified by structue recType.

  struct recType {
    long   number;    /* number of types   */
    char   **papName;   /* ptr to arr of ptr to name   */
  };
Figure 12-1 shows the memory layout of the record type structures.

Figure 12-1: Record Types

This is among the simpler of the IOC structures thus let's discuss a few details. The external variable dbRecType points to structure recType. This structure contains two elements: number and papName. number specifies the number of record types. papName is a pointer to an array of pointers to record names. Notice in the figure the unnamed array of pointers. It is permissible for any pointer in the array to be NULL.This type of structure will be seen many times in the following subsections. Thus whenever a variable starts with "pap" it means "pointer to array of pointers".

dbRecords.h - Record Locations

These structures describe the location of the actual database records.

  typedef struct{
    ELLNODE   next;
    void   *precord;
  } RECNODE;
  
  struct recLoc{   /* record location */
    long   rec_size;   /* record size in bytes */
    long   record_type;   /* record type  */
    ELLLIST    *preclist   /* LIST head of sorted RECNODEs  */
  };
  struct recHeader{ /*record header*/
    long   number;   /*number of record types*/
    struct recLoc  **papRecLoc; /*ptr to arr of ptr to recLoc*/
  };
Figure 12-2 shows the memory layout of the database records.

Figure 12-2: Database Records

dbRecDes.h - Record Description

These structures describe each record type and each field of each record type.

/* conversion types*/
#define CT_DECIMAL 0
#define CT_HEX   1
/* lowfl, highfl */
#define CON   0
#define VAR   1
#define PROMPT_SZ   24
union fld_types{
  char   char_value;
  unsigned   char   uchar_value;
  short   short_value;
  unsigned   short   ushort_value;
  long   long_value;
  unsigned long   ulong_value;
  float   float_value;
  double   double_value;
  unsigned short   enum_value;
};
struct range {
  long   fldnum;
  unionfld_types  value;
};
struct  fldDes{  /* field description */
  char   prompt[PROMPT_SZ]; /*Prompt string for DCT */
  char   fldname[FLDNAME_SZ];/*field name */
  short  offset;   /* Offset in bytes from beginning of record */
  short  size;   /* length in bytes of a field element */
  short  special;   /* Special processing requirements */
  short  field_type;   /* Field type as defined in dbFldTypes.h */
  short  process_passive;   /*should dbPutField process passive records*/
  short  choice_set;   /* index of choiceSet GBLCHOICE & RECCHOICE*/
  short  cvt_type;   /* Conversion type for DCT */
  short  promptflag;   /* Does DCT display this field */
  short  lowfl;   /* Is range1 CON or VAR */
  short  highfl;   /* Is range2 CON or VAR */
  short  interest;   /* interest level for reporting */
  union  fld_types  initial;   /* initial value */
  struct range  range1; /* Low value for field (Used by DCT) */
  struct range  range2; /* High value for field (Used by DCT) */
};
struct recTypDes{  /* record type description */
  short   rec_size;  /* size of the record  */
  short   no_fields;  /* number of fields defined */
  short   no_prompt;  /* number of fields to configure */
  short   no_links;  /* number of links */
  short   *link_ind;  /* addr of array of ind in apFldDes */
  unsigned long  *sortFldName;  /* addr of array of sorted fldname */
  short   *sortFldInd;  /* addr of array of ind in apFldDes */
  struct fldDes  **papFldDes;  /* ptr to array of ptr to fldDes */
};
struct   recDes{  /* record description */
  long  number;   /*number of recTypDes*/
  struct recTypDes **papRecTypDes;/*ptr to arr of ptr to recTypDes*/
};
extern struct recDes *dbRecDes;
Figure 12-3 shows the memory layout of the record descriptions. Lets first discuss structure recTypDes and then fldDes.

Figure 12-3: Record Descriptions

Structure recTypDes describes the information specific to each record type. It contains the following information:

Structure fldDes contains a complete description of a field. The database access routines and various utilities such as DCT use these definitions to access the database (the fields common to all record types are also used). Thus, with the exception of record and device support routines, the software has no knowledge of particular record types. This makes it possible to add new record and device support and/or modify existing support without affecting most of the IOC and utility software. Lets discuss each field attribute:

choice.h - Choice Definitions

struct choiceSet { /* This defines one set of choices*/
  long   number;   /*number of choices */
  char   **papChoice;   /*ptr to arr of ptr to choice string   */
  };
struct arrChoiceSet{ /*An array of choice sets for particular record type*/
  long   number;   /*number of choice sets */
  struct choiceSet **papChoiceSet;  /*ptr to arr of ptr to choiceSet*/
  };
struct choiceRec{ /*define choices for each record type*/
  long   number;   /*number of arrChoiceSet */
  struct arrChoiceSet **papArrChoiceSet;  /*ptr to arr of ptr to arrChoiceSet*/
  };
/* device choices */
struct devChoice{
  long   link_type;  /*link type for this device*/
  char   *pchoice;   /*ptr to choice string */
  };
struct devChoiceSet { /* This defines one set of device choices*/
  long   number;  /*number of choices */
  struct devChoice **papDevChoice; /*ptr to arr of ptr to devChoice */
  char   **papChoice;   /*ptr to arr of ptr to choice string */
  };
struct devChoiceRec{ /*define device choices for each record type*/
  long  number;   /*number of devChoiceSet  */
  struct devChoiceSet **papDevChoiceSet; /*ptr to arr of ptr to devChoiceSet*/
  };
Figure 12-4 shows the memory layout of the choice definitions. In the database a choice field is stored as an unsigned short value. The meaning is determined via the associated choice structures. Four types of structures are referenced via the following pointers (stored in struct dbBase):

Figure 12-4: Choice Definitions

cvtTable.h

struct brkInt{ /* breakpoint interval */
  long   raw;    /*raw value for beginning of interval */
  float  slope;   /*slope for interval */
  float  eng;   /*converted value for beginning of interval*/
  };
struct brkTable { /* breakpoint table */
  char   *name;   /*breakpoint table name */
  long   number;   /*number of brkInt in this table */
  long   rawLow;   /*lowest raw data value allowed */
  long   rawHigh;   /*highest raw data value allowed */
  struct brkInt  **papBrkInt;  /* ptr to array of ptr to brkInt */
  };
struct arrBrkTable { /* array of brkTable */
  long   number;   /*number of break tables */
  struct brkTable  **papBrkTable;  /* ptr to array of ptr to brkTable */
  };

Figure 12-5: Conversion Tables

recSup.h - Record Support

typedef long (*RECSUPFUN) ();  /* ptr to record support function */
struct rset {  /* record support entry table */
  long   number;  /*number of support routines */
  RECSUPFUN   report;  /*print report */
  RECSUPFUN   init;  /*init support */
  RECSUPFUN   init_record;  /*init record */
  RECSUPFUN   process;  /*process record  */
  RECSUPFUN   special;  /*special processing */
  RECSUPFUN   get_value;  /*get value field */
  RECSUPFUN   cvt_dbaddr;  /*cvt  dbAddr */
  RECSUPFUN   get_array_info;
  RECSUPFUN   put_array_info;
  RECSUPFUN   get_units;
  RECSUPFUN   get_precision;
  RECSUPFUN   get_enum_str;   /*get string from enum item */
  RECSUPFUN   get_enum_strs;   /*get all enum strings */
  RECSUPFUN   put_enum_str;   /*put string to enum item */
  RECSUPFUN   get_graphic_double;
  RECSUPFUN   get_control_double;
  RECSUPFUN   get_alarm_double;
  };
struct recSup {
  long   number;  /*number of record types */
  struct rset  **papRset;  /*ptr to arr of ptr to rset */
  };
#define RSETNUMBER ( (sizeof(struct rset) - sizeof(long))/sizeof(RECSUPFUN) )
Figure 12-6 shows the memory layout of the record support definitions. Each record type must have an associated set of record support routines. Note that only the record and device support routines use the record structure declarations while accessing a record. The record support routines are intended to isolate the rest of the IOC software from details of record access and processing.

Figure 12-6: Record Support

devSup.h - Device Support

typedef long (*DEVSUPFUN) ();  /* ptr to device support function*/
struct dset {  /* device support entry table */
  long   number;   /*number of support routines*/
  DEVSUPFUN  report;  /*print report*/
  DEVSUPFUN  init;  /*init support*/
  DEVSUPFUN  init_record;  /*init support for particular record*/
  DEVSUPFUN  get_ioint_info;  /*get I/O interrupt information*/
  /*other functions are record dependent*/
  };
struct devSup {
  long   number;  /*number of dset */
  char   **papDsetName;  /*ptr of arr of ptr to name */
  struct dset **papDset;  /*ptr to arr of ptr to dset */
  };
struct recDevSup {
  long   number;  /*number of record types */
  struct devSup **papDevSup; /*ptr to arr of ptr to devSup */
  };
extern struct recDevSup *devSup;
Figure 12-7 shows the memory layout of the device support definitions. The device support routines are intended to isolate the record processing routines from device specific details.

Figure 12-7: Device Support

drvSup.h - Driver Support

typedef int (*DRVSUPFUN) ();  /* ptr to driver support function*/
struct drvet {   /* driver entry table */
  long  number;   /*number of support routines*/
  DRVSUPFUN   report;   /*print report*/
  DRVSUPFUN   init;   /*init support*/
  DERSUPFUN   reboot;  /*reboot entry*/
  /* other functions are device dependent*/
  };
struct drvSup {
  long   number;   /*number of dset */
  char   **papDrvName;   /*ptr to arr of ptr to drvetName*/
  struct drvet  **papDrvet;   /*ptr to arr ptr to drvet */
  };
#define DRVETNUMBER ( (sizeof(struct drvet) -sizeof(long))/sizeof(DRVSUPFUN) )
Figure 12-8 shows the memory layout of the driver support definitions.

Figure 12-8: Driver Support