Difference between revisions of "V4 Design: dbdInterfaces"

From EPICSWIKI
 
(13 intermediate revisions by the same user not shown)
Line 1: Line 1:
= EPICS: dbdInterfaces - IOC Database Description =
EPICS: dbdInterfaces - IOC Database Description November 04 2005  
August 29 2005  


----
----
<center>
= Overview =
 
This document describes  definitions for code that accessses
IOC records, i.e. the records created from Database Definitions:
menu, struct, record, link, device, and record instances.
 
The interfaces support introspection of everything created from Database
Definitions. The interfaces can be used by tools such as VDCT or on
a running IOC database.


== Overview ==
The definitions can be used by support code, e.g. record support,
or by code that uses only introspection.


</center>
Code is automatically generated from the following definitions:
* <tt>record</tt> - Used by record support.
* <tt>struct</tt> - Used by code that understands the struct.
* <tt>menu</tt> - Used by code that understands the menu.


This document describes  definitions for code that accessses
----
IOC records, i.e. the records created from Database Definitions.
<center>
The definitions are intended for code that:
* includes header files generated from dbd definitions.  Header files are generated from the following dbd definitions:
** <tt>record</tt> - Should only be included by record support.
** <tt>struct</tt> - Included by code that understands the struct.
** <tt>menu</tt> - Included by code that understands the menu.
*  does not include the header files.


== Syntax ==
= Syntax =
</center>


The syntax is defined so that it is easy to provide C++ and Java definitions.
The syntax is defined so that either C++ and Java implementations can be created.


NOTE: Most of the code fragments use C++ syntax.
NOTE: Most of the code fragments use Java syntax.


=== Primitive Types ===
== Primitive Types ==
* bool - a value that takes the values true or false
* octet - 8 bit byte
* octet - an 8 bit byte
* boolean - a value that takes the values <tt>true</tt> or <tt>false</tt>
* int16 - a 16 bits signed integer
* int16 - 16 bit signed integer
* int32 - a 32 bit signed integer
* int32 - 32 bit signed integer
* int64 - a 64 bit signed integer
* int64 - 64 bit signed integer
* float32 - a 32 bit IEEE float
* float32 - 32 bit IEEE float
* float64 - a 64 bit IEEE float
* float64 - 64 bit IEEE float


In Java these types become:
In Java these types become:


* bool => bool
* octet => byte  BUT no arithmetic is implied
* octet => byte  BUT no arithmetic is implied
* boolean => boolean
* int16 => short
* int16 => short
* int32 => int
* int32 => int
Line 45: Line 51:
In C++ these types become:
In C++ these types become:


* bool => bool
* octet => char  BUT no arithmetic is implied
* octet => char  BUT no arithmetic is implied
* int16 => short
* boolean => bool BUT no arithmetic or conversion to/from int is implied
* int32 => int
* int16 => int16_t
* int64 => long long
* int32 => int32_t
* int64 => int64_t
* float32 => float
* float32 => float
* float64 => double
* float64 => double


NOTES:
NOTE:
* C++ may require something else to handle the integer types
* The C++ integer types are from C99 stdint.h
* Exceptions must be defined. TBD
* Should exceptions be defined?


=== enum, struct, interface,  string, array ===
== enum, struct, interface, class,  string, array ==


In adition to the primitive types the syntax
In adition to the primitive types the syntax
uses the terms enum, struct, interface, and array.
uses the terms enum, struct, interface, and array.


==== enum ====
=== enum ===


An example of an enum is:
An example of an enum is:
Line 84: Line 90:
     };
     };


==== struct ====
=== struct ===
An example of a struct definition is:
An example of a struct definition is:


Line 107: Line 113:
     }
     }


==== interface ====
=== interface ===
An example of an interface definition is:
An example of an interface definition is:


     interface DbfBool extends Dbf {
     interface DbfBoolean extends Dbf {
         bool get();
         boolean get();
         void put(bool val);
         void   put(boolean val);
     }
     }


In C++ would be:
In C++ would be:
     class DbfBool : public Dbf {
     class DbfBoolean : public Dbf {
     public:
     public:
         virtual bool get() = 0;
         virtual bool get() = 0;
Line 123: Line 129:


In Java would be:
In Java would be:
     interface DbfBool extends Dbf {
     interface DbfBoolean extends Dbf {
         bool get();
         boolean get();
         void put(bool val)
         void   put(boolean val)
     }
     }
==== string ====
=== class ===
 
Any class definitions are language specific.


All string arguments are assumned to be UTF-8 compatible.
=== string ===


For C++ a string will be a <bold>char *</bold> array containing UTF-8
For C++ a string will be a <tt>char *</tt> array containing UTF-8
compatible characters not necessarily terminated with a null character.
compatible characters not necessarily terminated with a null character.
Wherever a <bold>string</bold> argument appears, the C++ definition
Wherever a <tt>string</tt> argument appears, the C++ definition
will have <bold>len</bold> and <bold>char *</bold> arguments/
will have <tt>len</tt> and <tt>char *</tt> arguments.


For Java string will just be a <bold>String</bold>.
For Java string will just be a <tt>String</tt>.
It is assumed that Java Strings will be converted to/from UTF-8 byte streams
It is assumed that Java Strings will be converted to/from UTF-8 byte streams
when the data is transfered to/from the network.
when the data is transfered to/from the network.


An example of a definition that includes a string argument is
If a string is being passed to a method the syntax is:
     void get(string name);
     void put(string value);
In C++ this becomes
    void put(int valueLength, char *value);
In Java it becomes
    void put(String value);


In C++ this will become
If a method is returning a string the syntax is
     void get(int lenName, char *name);
    string get();
In Java this will become
In C++ this becomes
     void get(String name);
     int getValueLength();
==== array ====
    int get(int valueLength, char *value);
    // valueLength is number of octets in value
    // return value is the number of octets written
In Java it becomes
     String get();
=== array ===


An example of a definition that includes an array is:
An example of a definition that includes an array is:
Line 153: Line 170:
     void get(float64[] data);
     void get(float64[] data);


In C++ ,this would become:
In C++ this would become:


     void get(int lenData, float64 data[]);
     void get(int lenData, double data[]);


In Java this would be:
In Java this would be:


     void get(float64[] data);
     void get(double[] data);
 
----
<center>
 
== C++ support for strings and arrays ==
</center>
 
Note that the support described here is not needed for Java since
Java already provides facilities
 
=== string support ===
 
 
The following interfaces are for allocating tempory storage for strings:
 
    class NonmutableString {
    public:
        void destroy(); // Call this when done with string
        int32 getLength();
        char data[];
    }
   
    class NonmutableStringFactory {
    public:
        static NonmutableString *create(int32 len,char data[]);
    }
 
    class MutableString : public NonmutableString {
    public:
        int32 getCapacity();
        void setLength(int32 len);
    }
   
    class MutableStringFactory {
        static MutableString *create(int32 int32 capacity);
    }
 
These will use free lists to manage the storage for the octet arrays.
 
=== array support ===
 
This is only needed for C++. Java already provides an analogous facility.
 
    // ArrayCopy only works on primitive types
    class ArrayCopy {
    public:
        void copy(DbfArray from,DbfArray to);
    }
 
This only supports primitive types, i.e. DbfBool,...,DbfFloat64.
 
=== Arithmetic Type Conversions ===
 
    class DbfConvertScalar {
    public:
        static int16 get(Dbf to);
        static int32 get(Dbf to);
        static int64 get(Dbf to);
        static float32 get(Dbf to);
        static float64 get(Dbf to);
        static get(Dbf to, int32 lenValue,const *char value);
        static get(DbfBool to, int32 lenValue,const *char value);
        static put(Dbf from, int16 value);
        static put(Dbf from, int32 value);
        static put(Dbf from, int64 value);
        static put(Dbf from, float32 value);
        static put(Dbf from, float64 value);
        static put(Dbf from,int32 lenValue,const *char value);
        static put(DbfBool from, int32 lenValue,const *char value);
 
        static get(DbfArray to, int16 value[]);
        static get(DbfArray to, int32 value[]);
        static get(DbfArray to, int64 value[]);
        static get(DbfArray to, float32 value[]);
        static get(DbfArray to, float64 value[]);
        static put(DbfArray from, int16 value[]);
        static put(DbfArray from, int32 value[]);
        static put(DbfArray from, int64 value[]);
        static put(DbfArray from, float32 value[]);
        static put(DbfArray from, float64 value[]);
    }
 
NOTES:
* For Dbf <tt>to</tt> and <tt>from</tt> must be one of <tt>DbfInt16</tt>, ..., <tt>DbfFloat64</tt>
* The get to a string uses printf semantics
* The put from a string value just uses scanf semantics.
* The <tt>DbfBool</tt> method supports all the choices specified in the DBD Record Instance Specification
* For DbfArray tt>to</tt> and <tt>from</tt> must be an array of one of <tt>DbfInt16</tt>, ..., <tt>DbfFloat64</tt>


----
----
<center>
<center>


== DbfTypes ==
= DbfTypes =
  </center>
  </center>
The following naming conventions are used:
The following naming conventions are used:
; Dbf
; Dbf
: any class starting with Dbf describes a field in a generated header file. For example DbfArray describes a field generated from <tt>field(name,array(float64[])</tt>.
: any class starting with Dbf describes a field in a header file generated from a <tt>struct</tt> or <tt>record</tt> definition. For example DbfArray describes a field generated from <tt>field(name,array(float64[])</tt>.
; Dbd
; Dbd
: A class name starting with Dbd describes something related to dbd definitions. For example DbdLinkSupport describes a dbd link definition.
: A class name starting with Dbd describes something related to dbd definitions. For example DbdMenu describes a dbd menu definition.


All Dbf and Dbd definitions are interfaces. Thus all access to data is via
All Dbf and Dbd definitions are interfaces. Thus all access to data is via
interfaces. The IOC database implements the interfaces, with help from
interfaces.
record, link, and device support. After initialization data can only
be accessed via the interfaces. This allows the database to handle
actions like posting database monitors without any help from
record, link, or device support.


=== <tt>DbfTypes.h</tt> ===
== <tt>DbfTypes.h</tt> ==


The following <tt>enum</tt> definitions describe  
The following <tt>enum</tt> definitions describe  
Line 274: Line 199:


     enum BasicType {
     enum BasicType {
         BasicTypeBool,     // DbfBool
         basicTypeOctet,   // DbfOctet
         BasicTypeOctet,   // DbfOctet
         basicTypeBoolean, // DbfBoolean
         BasicTypeInt16,    // DbfInt16
         basicTypeInt16,    // DbfInt16
         BasicTypeInt32,    // DbfInt32
         basicTypeInt32,    // DbfInt32
         BasicTypeInt64,    // DbfInt64
         basicTypeInt64,    // DbfInt64
         BasicTypeFloat32,  // DbfFloat32
         basicTypeFloat32,  // DbfFloat32
         BasicTypeFloat64,  // DbfFloat64
         basicTypeFloat64,  // DbfFloat64
         BasicTypeString,  // DbfString
         basicTypeString,  // DbfString
         BasicTypeArray,    // DbfArray
         basicTypeArray,    // DbfArray
         BasicTypeStruct,  // DbfStruct
         basicTypeStruct,  // DbfStruct
     }
     }


     enum DbfType {
     enum DbfType {
         DbfTypeBasic,      // DbfBool,...,DbfStruct
         dbfTypeBasic,      // DbfOctet,...,DbfStruct
         DbfTypeMenu,        // DbfMenu
         dbfTypeMenu,        // DbfMenu
         DbfTypeEnum,        // DbfEnum
         dbfTypeEnum,        // DbfEnum
         DbfTypeLink,        // DbfLink
         dbfTypeLink,        // DbfLink
         DbfTypeDevice,      // DbfDevice
         dbfTypeMDArray,    // DbfMDArray
        DbfTypeMDArray,    // DbfMDArray
         dbfTypeTimeStamp   // DbfTimeStamp
         DbfTypeTimeStamp   // DbfTimeStamp
    }
 
 
=== Discussion of DbfTypes ===
 
The following shows the code generated from DBD files:
 
==== structure definitions ====
If a structure is defined as:
    struct(DisplayLimit) {
        field(low,double)
        field(high,double)
    }
 
Then the generated C++ header file will be
 
    class DisplayLimit {
    public:
        DbfFloat64 *low;
        DbfFloat64 *high;
        static int16 lowIndex = 1;
        static int16 highIndex = 2;
        static int16 lastIndex = highIndex;
    };
 
The generated Java file is
 
    class DisplayLimit {
        public DbfFloat64 low;
        public DbfFloat64 high;
        public static final int16 lowIndex = 1;
        public static final int16 highIndex = 2;
        public static final int16 lastIndex = indexHigh;
    }
 
In addition code will be generated that implements the following:
 
    struct DisplayLimitData {
        float64 low;
        float64 high;
    }
    interface DisplayLimitAccess{
        void get(DbfStruct, DisplayLimitData data);
        void put(DbfStruct, DisplayLimitData data);
    }
 
==== record definitions ====
If a record is defined as:
    record(Example) extends IocRecord {
        ...
        field(fbool,bool)
        field(foctet,octet)
        field(fint,int16)
        ...
        field(ffloat,float64)
        field(string,string)
        field(array,array(double[])
        field(mdarray,array(double[,])
        field(menu,menu(name))
        field(fenum,enum)
        field(link,link(in))
        field(device,link(in,analogIO))
        field(displayLimit,struct(DisplayLimit))
    }
 
Then the generated C++ header file will be
    class IocRecord {
    public:
        RecordInstance *base;
    };
 
    class ExampleRecord : public IocRecord {
    public:
        DbfBool      *fbool;
        DbfOctet    *foctet;
        DbfInt16    *fint;
        ...
        DbfFloat64  *ffloat;
        DbfString    *string;
        DbfArray    *array;
        DbfMDArray  *mdarray;
        DbfMenu      *menu;
        DbfEnum      *fenum;
        DbfLink      *link;
        DbfDevice    *device;
        DbfStruct    *displayLimit;
        static int16 baseIndex = 1;
        static int16 fboolIndex = 2;
        ...
        static int16 lastIndex = displayLimitIndex;
    };
 
The generated Java file is
 
    class IocRecord {
        public RecordInstance base;
    }
    class ExampleRecord extends IocRecord {
        public DbfBool      fbool;
        public DbfOctet    foctet;
        public DbfInt16    fint;
        ...
        public DbfFloat64  ffloat;
        public DbfString    string;
        public DbfArray    array;
        public DbfMDArray  mdarray;
        public DbfMenu      menu;
        public DbfEnum      fenum;
        public DbfLink      link;
        public DbfDevice    device;
        public DbfStruct    displayLimit;
        public static final int16 baseIndex = 1;
        public static final int16 fboolIndex = 2;
        ...
        public static final int16 lastIndex = displayLimitIndex;
     }
     }


Line 414: Line 223:
<center>
<center>


== Database Fields ==
= Database Fields =
  </center>
  </center>


Line 421: Line 230:


     interface Dbf{
     interface Dbf{
         bool isPrimitive(); // BasicTypeBool,...,BasicTypeFloat64
         DbfType getType();
         bool isBasic();
        boolean isPrimitive(); // BasicTypeBoolean,...,BasicTypeFloat64
         boolean isBasic();
         BasicType getBasicType();
         BasicType getBasicType();
         RecordInstance getRecord();
         RecordInstance getRecord();
Line 428: Line 238:
     }
     }


The interfaces are designed as follows:
The interfaces are designed so that a field can be accessded
* DbAccess provides storage for fields and never exposes the address of any field.
without exposing the address of its data.
* DbAccess posts monitors when a field is modified.
 
The fact the each field is an object means that additional storage is required.
DbAccess will probably have something like the following:
 
    class Field {
    public:
        RecordInstance &instance;
        short            index;
    };
    ...
    class IntField : public Field {
    public:
        int data;
    }
    ...
 
This each field has the overhead of
* instance - a reference to RecordInstance
* index - a 16 bit integer
* vtbl - a reference to the class implementation


An additional pointer field will be needed for things like monitors.
== Primitive Types ==
This field can start out null and only have additional storage as needed.
 
 
=== Primitive Types ===


DbfOctet, ..., DbfFloat64 are all interfaces with methods get and put.
DbfOctet, ..., DbfFloat64 are all interfaces with methods get and put.
    interface DbfBool extends Dbf {
        bool get();
        void put(bool val);
    }
      
      
     interface DbfOctet extends Dbf {
     interface DbfOctet extends Dbf {
         octet get();
         octet get();
         void put(octet val);
         void put(octet val);
    }
    interface DbfBoolean extends Dbf {
        boolean get();
        void put(boolean val);
     }
     }
      
      
Line 499: Line 284:
file. Some examples are:
file. Some examples are:


     ExampleRecord *precord;
     ExampleRecord record;
    short myint;
     ...
     ...
     precord->ffloat->put(10.0);
     precord.ffloat.put(10.0);
     ...
     ...
     myint = precord->fint->get();
     short myint = record.fint.get();


Code that does not include the generated header file can access these fields
Code that does not include the generated header file can access these fields
via the introspecion interfaces described later in this document.
via the introspection interfaces described later in this document.
For example code that expects a float64 field can access it via
For example code that expects a float64 field can access it via


     char *name = "recordname.value";
     Dbf dbf = RecordInstanceLocate.getField("recordname.value");
     int32 len = strlen(name);
     if(dbf==null
    DbdAddr *pDbdAddr;
     || !dbf.isPrimitive()
    DbfFloat64 *pfield;
     || (dbf.getBasicType() != basicTypeFloat64) ) // do something
     ...
     DbfFloat64 dbfdouble = (DbfFloat64)dbf;
   
     dbfdouble.put(10.0);
    pDbdAddr = pLocateInstance->getPV(len,name);
     if(!pDbdAddr || (pDbdAddr->getType != DbfTypeFloat64)) // do SOMETHING
     pfield = dynamic_cast<DbfFloat64 *>pDbdAddr->getAddr();
     pfield->put(10.0);


Instead of the last three lines, DbfConvert could be called.
or more concisely (but exception may be thrown)
    DbfConvertPrimitive(RecordInstanceLocate.getField("recordname.value"),10.0);


    pDbfConvert->put(pDbdAddr->getAddr,10.0);
== String fields ==
   
 
=== String fields ===


The interface for a string field is:
The interface for a string field is:


     interface DbfString extends Dbf {
     interface DbfString extends Dbf {
         int32 getLength();
         string get();
        void get(string value);
        void setPutSize(int32 size);
         void put(string value);
         void put(string value);
     }
     }


In C++ remember that this interface becomes:
    class DbfString : public Dbf {
        int getLength();
        void get(int lenValue,char value[]);
        void setPutSize(int size);
        void put(int lenValue,char value[]);
    };
The following code prints a string.
The following code prints a string.


     ExampleRecord *precord;
     ExampleRecord record;
     epicsInt32 len = precord->string->getLength();
     String string = record.fstring.get();
    MutableString *pstring = pMutableStringFactory->create(len);
     printf("%s\n",string);
    ...
or more concisely
    precord->string->get(len,pstring->data);
     printf("%s\n",record.fstring.get());
     printf("%.*s\n",len,pstring->data);
     pstring->destroy();


Code that does not include the header file can use the introspection methods
Code that does not include the header file can use the introspection methods
to locate the DbfString that provides access to the field.
to locate the DbfString that provides access to the field.


NOTES:
== Structure Fields ==
* Does record support have to have any say in how DbAccess manages storage for strings? I don't think so.
 
=== Structure Fields ===


     interface DbfStruct extends Dbf {
     interface DbfStruct extends Dbf {
Line 570: Line 334:
The following traverses the fields of a DbfStruct
The following traverses the fields of a DbfStruct


     DbfStruct *pDbfStruct;
     DbfStruct dbfStruct;
    MutableString *pMutableString;
      
      
     for(i=0; t< pDbfStruct->getNfields(); i++) {
     for(i=0; i < dbfStruct.getNfields(); i++) {
         DbdField *pDbdField = pDbfStruct->getDescription(i);
         DbdField dbdField = dbfStruct.getDescription[i];
         int len = pDbdField->getNameLength();
         String name = dbdField.getName();
         MutableString *pname = pMutableStringFactory->create(len);
         printf("field %s\n",name);
        pDbdField->getName(len,pname);
         // or just
         DbfType type = pDbdField->getType();
         printf("field %s\n",dbfStruct.getDescription[i].dbdField.getName());
         printf("field %s type %d\n",pname->data,type);
 
        pname->destroy();
     }
     }


Line 587: Line 349:
However, for each structure, code is generated that does the introspection.
However, for each structure, code is generated that does the introspection.
For example DisplayLimitData can be obtained via the statements:
For example DisplayLimitData can be obtained via the statements:
     ExampleRecord *precord;
     ExampleRecord record;
     DisplayLimitData limits;
     DisplayLimitData limit = new DisplayLimitData();
      
      
     ...
     ...
     pDisplayLimitAccess->get(precord->displayLimit,limits);
     DisplayLimitSupport.get(record.displayLimit,limit);
     printf("low %f high %f\n",limits.low,limits.high);
     printf("low %f high %f\n",limit.low,limit.high);
 
== Array Fields ==


=== Array Fields ===
The generated header file will have a type that extends <tt>DbfArray</tt>


     interface DbfArray extends Dbf {
     interface DbfArray extends Dbf {
Line 600: Line 364:
         int32 getNelements();
         int32 getNelements();
         void setNelements(int32 len);
         void setNelements(int32 len);
    }
    interface DbfBooleanArray extends DbfArray {
        int32 get(int32 offset, int32 len, boolean[] pto);
        int32 put(int32 offset, int32 len, boolean[] pfrom);
     }
     }


Line 605: Line 374:
         int32 get(int32 offset, int32 len, octet[] pto);
         int32 get(int32 offset, int32 len, octet[] pto);
         int32 put(int32 offset, int32 len, octet[] pfrom);
         int32 put(int32 offset, int32 len, octet[] pfrom);
    }
    interface DbfBoolArray extends DbfArray {
        int32 get(int32 offset, int32 len, bool[] pto);
        int32 put(int32 offset, int32 len, bool[] pfrom);
     }
     }


Line 647: Line 411:
     interface DbfStructArray extends DbfArray {
     interface DbfStructArray extends DbfArray {
         DbfStruct getInterface(int32 index);
         DbfStruct getInterface(int32 index);
    }
    interface DbfMenuArray extends DbfArray {
        DbfMenu getInterface(int32 index);
    }
    interface DbfEnumArray extends DbfArray {
        DbfEnum getInterface(int32 index);
    }
    interface DbfLinkArray extends DbfArray {
        DbfLink getInterface(int32 index);
     }
     }


The following puts data into a float64 field.
The following puts data into a float64 field.


     ExampleRecord *precord;
     ExampleRecord record;
     float64 *pdata; // assume this has been allocated somehow
 
     int32  nelements; // this is number of elements
     double[] data = new double[] {1.0,2.0,3.0};;
     DbfFloat64Array *parray = dynamic_cast<DbfFloat64Array *>precord->array;
    if(!record.ffloat.isPrimitive()
    || record.ffloat.getBasicType()!=basicTypeFloat64)  // DO SOMTHING!!!!
     ...
     array.put(0,nelements,precord->data);
 
or more concisely
 
    ExampleRecord record;
     ...
     ...
     parray->put(0,nelements,pdata);
     DbfConvertPrimitive.put(precord.array,new double[] {1.0,2.0,3.0});
For the primitive data types both get and put return an int32,
which is the number of array elements transfered.
The caller <b>must</b> be prepared to make multiple calls
in order to get or put an entire array.


   
For example
    DbfDoubleArray dbfarray;
    ...
    double[] data = double [dbfArray.getNelements];
    int offset=0;
    int len = data.length;
    int n;
    while(offset < len) {
        n = dbfarray.get(offset,len);
        if(n==len) break;
        len -= n;
        offset += n;
    }


NOTES:
* It must be possible for support to provide an implementation of get and put. It must be possible to do this in segments. For example
** A circular buffer requires two segments
** If the memory is in hardware, the support can read/write the data in segments. Transient recorders are an example.
* If support implements get and put then DbAccess does NOT manage storage but just calls support to get/put data.


=== DbfMDArray ===
== DbfMDArray ==
NOT YET DEFINED
NOT YET DEFINED
      
      


=== <tt>DbfMenu</tt> ===
== <tt>DbfMenu</tt> ==


<tt>DbfMenu</tt> is described as:
<tt>DbfMenu</tt> is described as:
Line 686: Line 480:




=== DbfEnum ===
== DbfEnum ==


<tt>DbfEnum</tt> is described as:
<tt>DbfEnum</tt> is described as:
Line 699: Line 493:
and also provides access to the The DbfArray field that contains the choices.
and also provides access to the The DbfArray field that contains the choices.


=== DbfLink and DbfDevice ===
== DbfLink ==
   
   
   
   
<tt>DbfLink</tt> is described as
<tt>DbfLink</tt> is described as
   
   
    enum LinkDir {
        LinkDirNone,
        LinkDirProcess,
        LinkDirIn,
        LinkDirOut,
        LinkDirInOut
    }
   
     interface DbfLink extends Dbf {
     interface DbfLink extends Dbf {
        LinkDir getDir();
         DbdLink getDbdLink();
         DbdLink getDbdLink();
         LinkSupport support;
         DbfStruct getSupportStruct();
    }
   
    interface DbfDevice extends Dbf {
        LinkDir getDir();
        DbdDevice getDbdDevice();
        Support support; // Support is base class for device support
     }
     }
Where
* <tt>getDbdLink</tt> gets the link definition for the field
* <tt>getSupportStruct</tt> get a struct instance for the support structure. Thius is NOT a field in the record but appears like it is.


See below for the description of support, which included record, link,
== DbfTimeStamp ==
and device support.
 
=== DbfTimeStamp ===


     struct TimeStamp {
     struct TimeStamp {
Line 742: Line 521:
<center>
<center>


== DbdStatements ==
= DbdStatements =


  </center>
  </center>
These describe everything defined in database definition files.
These describe everything defined in database definition files.


In the definitions:
== <tt>DbdMenu</tt> ==
 
 
=== <tt>DbdMenu</tt> ===


     interface DbdMenu {
     interface DbdMenu {
         int32 getNameLength();
         string getName();
        void getName(string name);
         int16 getNchoices();
         int16 getNchoices();
         int32 getChoiceLength(int16 index);
         string getChoice(int16 index);
        void getChoice(int16 index,string choice);
     }
     }




=== <tt>DbdLink</tt> and <tt>DbdDevice</tt> ===
 
== <tt>DbdSupport</tt> ==


     interface DbdSupport {
     interface DbdSupport {
         LinkDir getDir();
         string getChoiceName();
        int32 getChoiceNameLength();
         string getSupportStructName(string name);
        void getChoiceName(string name);
         int32 getSupportNameLength();
        void getSupportName(string name);
        int32 getDataStructNameLength();
        void getDataStructName(string name);
    }
   
    interface DbdLink extends DbdSupport{
        LinkSupport create(DbfLink link);
    }
   
    interface DbdDevice extends DbdSupport{
        int32 getInterfaceNameLength();
        void getInterfaceName(string name);
        Support  create(DbfDevice device);
     }
     }
      
      
=== <tt>DbdStruct</tt> and <tt>DbdRecord</tt> ===
== <tt>DbdStruct</tt> and <tt>DbdRecord</tt> ==


     interface DbdAttribute {
     interface DbdAttribute {
         int32 getDefaultLength();
         string getDefault();
        void getDefault(string value);
         boolean isReadonly();
         bool isReadonly();
         boolean isDesign();
         bool isDesign();
         boolean isSpecial();
         bool isSpecial();
         int16 getAsl();
         int16 getAsl();
     }
     }
Line 806: Line 565:
     interface DbdField {
     interface DbdField {
         int32 getNameLength();
         int32 getNameLength();
         void getName(string name);
         string getName();
         DbfType getType();
         DbfType getType();
         DbdDefaults getDefaults();
         DbdDefaults getDefaults();
Line 813: Line 572:


     interface DbdStruct {
     interface DbdStruct {
         int32 getNameLength();
         string getName();
        void getName(string name);
         int16 getNumberFields();
         int16 getNumberFields();
         DbdField getFieldDescription(int16 index);
         DbdField getFieldDescription(int16 index);
        DbdStructLifetime getLifetime();
     }
     }


     interrface DbdRecord {
     interface DbdRecord {
         // Need list of instances
         string getName();
        int32 getNameLength();
        void getName(string name);
         int16 getNumberFields();
         int16 getNumberFields();
         DbdField getFieldDescription(int16 index);
         DbdField getFieldDescription(int16 index);
        DbdRecordSupport getSupport();
        DbdRecordLifetime getLifetime();
     }
     }


     interface DbdLocate
== Record Instance ==
         DbdMenu getMenu(string name);
 
         DbdLink getLink(string name);
     interface RecordInstance {
         DbdDevice getDevice(string name);
         string getName();
         DbdStruct getStruct(string name);
         DbdRecord getDescription();
         DbdRecord getRecord(string name);
         DbfStruct getInstance();
         Dbf getField(string fieldName);
         Dbf getField(int16 index)
     }
     }


; <tt>getName</tt>
: get the record name
; <tt>getDescription</tt>
: get the description of the record specific portion of the record
; <tt>getInstance</tt>
: get instance for the record specific portion of the record
; <tt>getField</tt>
: get field instance.


----
----
<center>
<center>


== Run Time Interfaces ==
= Locate Interfaces =


  </center>
  </center>
=== Record Instance ===


    interface RecordAccess {
Classes are available to find and traverse the various Dbd definitons
        RecordSupport getRecordSupport();
and record instances.
        Dbf &getField(int16 index);
The implementation will be language specific. For Java they will be something
    }
like the following:
 
In addition the following is defined:


     interface RecordInstance {
     public class DbdLocate {
         int32 getNameLength();
         public DbdMenu getMenu(String name);
         void getName(string name);
         public DbdLink getLink(String name);
         DbdRecord getDescription();
         public DbdStruct getStruct(String name);
         RecordAccess getRecordAccess();
         public DbdRecord getRecord(String name);
         DbdRecord getIocRecordDescription();
         public LinkedList<DbdMenu> menuList;
         RecordAccess getIocRecordAccess();
        public LinkedList<DbdLink> linkList;
        public LinkedList<DbdStruct> structList;
         public LinkedList<DbdRecord> recordList;
     }
     }


     interface DbdAddr {
     public class RecordInstanceLocate {
         void    destroy(); // call this when done with DbAddr
         public RecordInstance getRecord(string name);
         DbfType getType();
         public Dbf getField(string name);
         RecordInstance getInstance();
         public Dbf getField(string recordName, string fieldName);
         int16 getIndex();
         public LinkedList<RecordInstance> instanceList;
         void  getField(string name);
         public LinkedList<RecordInstance> instanceList(string recordTypeName);
     }
     }


     class LocateInstance{
<b>Question</b> Are the above methods static or should there be something like
         DbdAddr getPV(string name);
     public class FindLocator {
        DbdAddr getPV(string recordName, string fieldName);
         public static DbdLocate findDbdLocate();
        DbdAddr getPV(RecordInstance instance, string fieldName);
         public static RecordInstance findRecordInstance();
        DbdAddr getPV(RecordInstance instance,int16 field);
         DbdAddr getIocRecordPV(RecordInstance instance, string fieldName);
        DbdAddr getIocRecordPV(RecordInstance instance,int16 field);
     }
     }
      
      
=== Record, Link, Device Support ===
==== Registration and Instance Creation ====
    interface RegisterSupport {
        link(SupportFactory support,string name);
        device(SupportFactory support,string name);
        record(SupportFactory support,string name);
    }
    interface SupportFactory {
        Support create(Dbf field);
    }
<tt>RegisterSupport</tt> is implemented by iocCore. Each support
implementation must register a "factory" for creating new instances
to attach to database records (record support) or to database
fields (link and device support). During database initialization,
iocCore cxalls the factory methods.


==== Link/Device Base ====
The following locates a specific menu.
    interface Support { // base for Link and Device support
        void report(int16 level);
        void destroy();
        void initialize();
        void connect();
        void disconnect();
    }


<tt>Support is the base class for all link and device support.
    DbdMenu menu = DbdLocate.get("DisplayLimit");
An instance of this is connected to each DbfLink or DbfDevice field.
    if(menu!=null) printf("found menu %s\n","DisplayLimit");
The methods are:
* <tt>report</tt> - report
* <tt>destroy</tt> - This is called if the field is being changed after initialization or if the record is being removed.
* <tt>initialize</tt> - Called to initialize a link.
* <tt>connect</tt> - Called to connect. Note that this is different than initilization.
* <tt>disconnect</tt> - disconnect.


==== Link Support ====
The following locates a field of a record instance.
    interface Callback {
        void done();
        void timedout();
    }


     interface LinkSupport extends Support {
     Dbf field = RecordInstanceLocate.getField("example.value");
        void get(Dbf field);
     if(addr!=null) printf("found %s\n","example.value");
        void getWait(Dbf field,bool process, double timeout,Callback callback);
        void put(Dbf field, bool process);
        void putWait(Dbf field,bool process, double timeout,Callback callback);
        void process();
        void processWait(double timeout,Callback callback);
    }
 
<tt>LinkSupport</tt> supports the following semantics:
* Input Link from another record
**  get current value independent of processing state.
** Ask the record to process and wait for completion before fetching value.
** wait until next time record processes then get value. No request is made to process the record.
* Output Link to another record
** put value without requesting that record be processed.
** put value then process record but don't wait for processing to complete.
** put value, process record, and wait for completion
* process link to another record
** request processing but do not wait for completion
** request processing and wait for completion
 
 
==== Device Support ====
 
Other than that it must extend Support, nothing is defined for Device support
in this document. It will be modeled after the V3 asynDriver support.
 
==== Record Support ====
 
    // Each record type implements RecordFactory
    interface RecordFactory {
        RecordAccess create(RecordInstance instance);
        void destroy(RecordAccess access);
    }
 
    enum processState {
        processIdle,
        processInputActive,
        processActive,
        processOutputActive,
        processDone
     }
 
 
    interface RecordSupport {
        void destroy();
        void initialize();
        processState process(processState state);
        void special(bool after,Dbf field);
    }
 
==== Database Access ====
 
    class DbAccess {
        void process(RecordInstance instance);
    }


----
----

Latest revision as of 15:25, 4 November 2005

EPICS: dbdInterfaces - IOC Database Description November 04 2005


Overview

This document describes definitions for code that accessses IOC records, i.e. the records created from Database Definitions: menu, struct, record, link, device, and record instances.

The interfaces support introspection of everything created from Database Definitions. The interfaces can be used by tools such as VDCT or on a running IOC database.

The definitions can be used by support code, e.g. record support, or by code that uses only introspection.

Code is automatically generated from the following definitions:

  • record - Used by record support.
  • struct - Used by code that understands the struct.
  • menu - Used by code that understands the menu.

Syntax

The syntax is defined so that either C++ and Java implementations can be created.

NOTE: Most of the code fragments use Java syntax.

Primitive Types

  • octet - 8 bit byte
  • boolean - a value that takes the values true or false
  • int16 - 16 bit signed integer
  • int32 - 32 bit signed integer
  • int64 - 64 bit signed integer
  • float32 - 32 bit IEEE float
  • float64 - 64 bit IEEE float

In Java these types become:

  • octet => byte BUT no arithmetic is implied
  • boolean => boolean
  • int16 => short
  • int32 => int
  • int64 => long
  • float32 => float
  • float64 => double

In C++ these types become:

  • octet => char BUT no arithmetic is implied
  • boolean => bool BUT no arithmetic or conversion to/from int is implied
  • int16 => int16_t
  • int32 => int32_t
  • int64 => int64_t
  • float32 => float
  • float64 => double

NOTE:

  • The C++ integer types are from C99 stdint.h
  • Should exceptions be defined?

enum, struct, interface, class, string, array

In adition to the primitive types the syntax uses the terms enum, struct, interface, and array.

enum

An example of an enum is:

    enum LinkDir {
        LinkDirNone,
        LinkDirProcess,
        LinkDirIn,
        LinkDirOut,
        LinkDirInOut
    }

The C++ definition is identical. In Java 5 the definition would be:

    public enum LinkDir {
        LinkDirNone,
        LinkDirProcess,
        LinkDirIn,
        LinkDirOut,
        LinkDirInOut
    };

struct

An example of a struct definition is:

    struct DisplayLimitData {
        float64 low;
        float64 high;
    }

In C++ this would be:

   class DisplayLimitData {
   public:
        double low;
        double high;
    };

In Java this would be:

    class DisplayLimitData {
        public double low;
        public double high;
    }

interface

An example of an interface definition is:

    interface DbfBoolean extends Dbf {
        boolean get();
        void    put(boolean val);
    }

In C++ would be:

    class DbfBoolean : public Dbf {
    public:
        virtual bool get() = 0;
        virtual void put(bool val) = 0;
    };

In Java would be:

    interface DbfBoolean extends Dbf {
        boolean get();
        void    put(boolean val)
    }

class

Any class definitions are language specific.

string

For C++ a string will be a char * array containing UTF-8 compatible characters not necessarily terminated with a null character. Wherever a string argument appears, the C++ definition will have len and char * arguments.

For Java string will just be a String. It is assumed that Java Strings will be converted to/from UTF-8 byte streams when the data is transfered to/from the network.

If a string is being passed to a method the syntax is:

    void put(string value);

In C++ this becomes

    void put(int valueLength, char *value);

In Java it becomes

    void put(String value);

If a method is returning a string the syntax is

    string get();

In C++ this becomes

    int getValueLength();
    int get(int valueLength, char *value);
    // valueLength is number of octets in value
    // return value is the number of octets written

In Java it becomes

    String get();

array

An example of a definition that includes an array is:

    void get(float64[] data);

In C++ this would become:

    void get(int lenData, double data[]);

In Java this would be:

    void get(double[] data);

DbfTypes

The following naming conventions are used:

Dbf
any class starting with Dbf describes a field in a header file generated from a struct or record definition. For example DbfArray describes a field generated from field(name,array(float64[]).
Dbd
A class name starting with Dbd describes something related to dbd definitions. For example DbdMenu describes a dbd menu definition.

All Dbf and Dbd definitions are interfaces. Thus all access to data is via interfaces.

DbfTypes.h

The following enum definitions describe each field in the header files generated from DBD struct and record definitions.

    enum BasicType {
        basicTypeOctet,    // DbfOctet
        basicTypeBoolean,  // DbfBoolean
        basicTypeInt16,    // DbfInt16
        basicTypeInt32,    // DbfInt32
        basicTypeInt64,    // DbfInt64
        basicTypeFloat32,  // DbfFloat32
        basicTypeFloat64,  // DbfFloat64
        basicTypeString,   // DbfString
        basicTypeArray,    // DbfArray
        basicTypeStruct,   // DbfStruct
    }
    enum DbfType {
        dbfTypeBasic,       // DbfOctet,...,DbfStruct
        dbfTypeMenu,        // DbfMenu
        dbfTypeEnum,        // DbfEnum
        dbfTypeLink,        // DbfLink
        dbfTypeMDArray,     // DbfMDArray
        dbfTypeTimeStamp    // DbfTimeStamp
    }

Database Fields

Each database field is accessed via an interface which all extend the following interface:

    interface Dbf{
        DbfType getType();
        boolean isPrimitive(); // BasicTypeBoolean,...,BasicTypeFloat64
        boolean isBasic();
        BasicType getBasicType();
        RecordInstance getRecord();
        int16 getIndex();
    }

The interfaces are designed so that a field can be accessded without exposing the address of its data.

Primitive Types

DbfOctet, ..., DbfFloat64 are all interfaces with methods get and put.

    interface DbfOctet extends Dbf {
        octet get();
        void put(octet val);
    }
    interface DbfBoolean extends Dbf {
        boolean get();
        void put(boolean val);
    }
   
    interface DbfInt16 extends Dbf {
        int16 get();
        void put(int16 val);
    }
   
    interface DbfInt32 extends Dbf {
        int32 get();
        void put(int32 val);
    }
   
    interface DbfInt64 extends Dbf {
        int64 get();
        void put(int64 val);
    }
   
    interface DbfFloat32 extends Dbf {
        float32 get();
        void put(float32 val);
    }
   
    interface DbfFloat64 extends Dbf {
        float64 get();
        void put(float64 val);
    }


Record support code can access such fields via the generated header file. Some examples are:

    ExampleRecord record;
    ...
    precord.ffloat.put(10.0);
    ...
    short myint = record.fint.get();

Code that does not include the generated header file can access these fields via the introspection interfaces described later in this document. For example code that expects a float64 field can access it via

    Dbf dbf =  RecordInstanceLocate.getField("recordname.value");
    if(dbf==null
    || !dbf.isPrimitive()
    || (dbf.getBasicType() != basicTypeFloat64) ) // do something
    DbfFloat64 dbfdouble = (DbfFloat64)dbf;
    dbfdouble.put(10.0);

or more concisely (but exception may be thrown)

    DbfConvertPrimitive(RecordInstanceLocate.getField("recordname.value"),10.0);

String fields

The interface for a string field is:

    interface DbfString extends Dbf {
       string get();
       void put(string value);
    }

The following code prints a string.

    ExampleRecord record;
    String string = record.fstring.get();
    printf("%s\n",string);

or more concisely

    printf("%s\n",record.fstring.get());

Code that does not include the header file can use the introspection methods to locate the DbfString that provides access to the field.

Structure Fields

    interface DbfStruct extends Dbf {
        int16 getNfields();
        Dbf getInterface(int16 index);
        DbdField getDescription(int16 index);
    }

The following traverses the fields of a DbfStruct

    DbfStruct dbfStruct;
    
    for(i=0; i < dbfStruct.getNfields(); i++) {
        DbdField dbdField = dbfStruct.getDescription[i];
        String name = dbdField.getName();
        printf("field %s\n",name);
        // or just
        printf("field %s\n",dbfStruct.getDescription[i].dbdField.getName());
    }


Structure fields can only be accessed via introspection. However, for each structure, code is generated that does the introspection. For example DisplayLimitData can be obtained via the statements:

    ExampleRecord record;
    DisplayLimitData limit = new DisplayLimitData();
    
    ...
    DisplayLimitSupport.get(record.displayLimit,limit);
    printf("low %f high %f\n",limit.low,limit.high);

Array Fields

The generated header file will have a type that extends DbfArray

    interface DbfArray extends Dbf {
       DbfType getType();
       int32 getNelements();
       void setNelements(int32 len);
    }
    interface DbfBooleanArray extends DbfArray {
       int32 get(int32 offset, int32 len, boolean[] pto);
       int32 put(int32 offset, int32 len, boolean[] pfrom);
    }
    interface DbfOctetArray extends DbfArray {
       int32 get(int32 offset, int32 len, octet[] pto);
       int32 put(int32 offset, int32 len, octet[] pfrom);
    }
    interface DbfInt16Array extends DbfArray {
       int32 get(int32 offset, int32 len, int16[] pto);
       int32 put(int32 offset, int32 len, int16[] pfrom);
    }
    interface DbfInt32Array extends DbfArray {
       int32 get(int32 offset, int32 len, int32[] pto);
       int32 put(int32 offset, int32 len, int32[] pfrom);
    }
    interface DbfInt64Array extends DbfArray {
       int32 get(int32 offset, int32 len, int64[] pto);
       int32 put(int32 offset, int32 len, int64[] pfrom);
    }
    interface DbfFloat32Array extends DbfArray {
       int32 get(int32 offset, int32 len, float32[] pto);
       int32 put(int32 offset, int32 len, float32[] pfrom);
    }
    interface DbfFloat64Array extends DbfArray {
       int32 get(int32 offset, int32 len, float64[] pto);
       int32 put(int32 offset, int32 len, float64[] pfrom);
    }
    interface DbfStringArray extends DbfArray {
        DbfString getInterface(int32 index);
    }
    interface DbfArrayArray extends DbfArray {
        DbfArray getInterface(int32 index);
    }
    interface DbfStructArray extends DbfArray {
        DbfStruct getInterface(int32 index);
    }
    interface DbfMenuArray extends DbfArray {
        DbfMenu getInterface(int32 index);
    }
    interface DbfEnumArray extends DbfArray {
        DbfEnum getInterface(int32 index);
    }
    interface DbfLinkArray extends DbfArray {
        DbfLink getInterface(int32 index);
    }

The following puts data into a float64 field.

    ExampleRecord record;
    double[] data = new double[] {1.0,2.0,3.0};;
    if(!record.ffloat.isPrimitive()
    || record.ffloat.getBasicType()!=basicTypeFloat64)  // DO SOMTHING!!!!
    ...
    array.put(0,nelements,precord->data);

or more concisely

    ExampleRecord record;
    ...
    DbfConvertPrimitive.put(precord.array,new double[] {1.0,2.0,3.0});

For the primitive data types both get and put return an int32, which is the number of array elements transfered. The caller must be prepared to make multiple calls in order to get or put an entire array.

For example

    DbfDoubleArray dbfarray;
    ...
    double[] data = double [dbfArray.getNelements];
    int offset=0;
    int len = data.length;
    int n;
    while(offset < len) {
        n = dbfarray.get(offset,len);
        if(n==len) break;
        len -= n;
        offset += n;
    }


DbfMDArray

NOT YET DEFINED


DbfMenu

DbfMenu is described as:

    interface DbfMenu extends Dbf {
        int16 getIndex();
        void putIndex(int16 val);
        DbdMenu getDbdMenu();
    }

DbfMenu allows the menu index to be set and retrieved and also provides access to the DbdMenu.

A DbfMenu field can be accessed via the generated header file or via the introspection methods.


DbfEnum

DbfEnum is described as:

    interface DbfEnum extends Dbf {
        int16 getIndex();
        void putIndex(int16 val);
        DbfStringArray getChoiceArray();
    }


DbfEnum allows the enum index to be set and retrieved and also provides access to the The DbfArray field that contains the choices.

DbfLink

DbfLink is described as

    interface DbfLink extends Dbf {
        DbdLink getDbdLink();
        DbfStruct getSupportStruct();
    }

Where

  • getDbdLink gets the link definition for the field
  • getSupportStruct get a struct instance for the support structure. Thius is NOT a field in the record but appears like it is.

DbfTimeStamp

    struct TimeStamp {
        int64 secondsSinceEpoch;
        int32 nanoSeconds;
    }
    interface DbfTimeStamp extends Dbf {
        void get(TimeStamp timeStamp);
        void put(TimeStamp timeStamp);
    }

DbdStatements

These describe everything defined in database definition files.

DbdMenu

    interface DbdMenu {
        string getName();
        int16 getNchoices();
        string getChoice(int16 index);
    }


DbdSupport

    interface DbdSupport {
        string getChoiceName();
        string getSupportStructName(string name);
    }
   

DbdStruct and DbdRecord

    interface DbdAttribute {
        string getDefault();
        boolean isReadonly();
        boolean isDesign();
        boolean isSpecial();
        int16 getAsl();
    }
   
    interface DbdDefaults {}
   
    interface DbdArrayDefaults extends DbdDefaults{
        DbfType getType();
        int32 getLength();
    interface DbdStructDefaults extends DbdDefaults{
        DbdStruct getDescription();
    }
    interface DbdField {
        int32 getNameLength();
        string getName();
        DbfType getType();
        DbdDefaults getDefaults();
        DbdAttribute getAttributes();
    }
    interface DbdStruct {
        string getName();
        int16 getNumberFields();
        DbdField getFieldDescription(int16 index);
    }
    interface DbdRecord {
        string getName();
        int16 getNumberFields();
        DbdField getFieldDescription(int16 index);
    }

Record Instance

    interface RecordInstance {
        string getName();
        DbdRecord getDescription();
        DbfStruct getInstance();
        Dbf getField(string fieldName);
        Dbf getField(int16 index)
    }
getName
get the record name
getDescription
get the description of the record specific portion of the record
getInstance
get instance for the record specific portion of the record
getField
get field instance.

Locate Interfaces

Classes are available to find and traverse the various Dbd definitons and record instances. The implementation will be language specific. For Java they will be something like the following:

In addition the following is defined:

    public class DbdLocate {
        public DbdMenu getMenu(String name);
        public DbdLink getLink(String name);
        public DbdStruct getStruct(String name);
        public DbdRecord getRecord(String name);
        public LinkedList<DbdMenu> menuList;
        public LinkedList<DbdLink> linkList;
        public LinkedList<DbdStruct> structList;
        public LinkedList<DbdRecord> recordList;
    }
    public class RecordInstanceLocate {
        public RecordInstance getRecord(string name);
        public Dbf getField(string name);
        public Dbf getField(string recordName, string fieldName);
        public LinkedList<RecordInstance> instanceList;
        public LinkedList<RecordInstance> instanceList(string recordTypeName);
    }

Question Are the above methods static or should there be something like

    public class FindLocator {
        public static DbdLocate findDbdLocate();
        public static RecordInstance findRecordInstance();
    }
   

The following locates a specific menu.

    DbdMenu menu = DbdLocate.get("DisplayLimit");
    if(menu!=null) printf("found menu %s\n","DisplayLimit");

The following locates a field of a record instance.

    Dbf field = RecordInstanceLocate.getField("example.value");
    if(addr!=null) printf("found %s\n","example.value");