Difference between revisions of "V4 Design: dbdInterfaces"

From EPICSWIKI
Line 1: Line 1:
= EPICS: dbdInterfaces - IOC Database Description =
= EPICS: dbdInterfaces - IOC Database Description =
August 31 2005  
September 1 2005  


----
----
Line 22: Line 22:
The syntax is defined so that it is easy to provide C++ and Java definitions.
The syntax is defined so that it is easy to provide C++ and Java definitions.


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
* boolean - a value that takes the values true or false
* octet - an 8 bit byte
* octet - an 8 bit byte
* int16 - a 16 bits signed integer
* int16 - a 16 bits signed integer
Line 35: Line 35:
In Java these types become:
In Java these types become:


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


* bool => bool
* boolean => bool
* octet => char  BUT no arithmetic is implied
* octet => char  BUT no arithmetic is implied
* int16 => short
* int16 => short
Line 110: Line 110:
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;
         virtual void put(bool val) = 0;
         virtual void put(boolean val) = 0;
     };
     };


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)
     }
     }
==== class ====
==== class ====


For now only <tt>static</tt> class definitions are required.
For now only static class definitions are required.
An example of a static class definition is:
An example of a static class definition is:
     static class RegisterSupport {
     static class RegisterSupport {
Line 153: Line 153:
compatible characters not necessarily terminated with a null character.
compatible characters not necessarily terminated with a null character.
Wherever a <tt>string</tt> argument appears, the C++ definition
Wherever a <tt>string</tt> argument appears, the C++ definition
will have <tt>len</tt> and <tt>char *</tt> arguments/
will have <tt>len</tt> and <tt>char *</tt> arguments.


For Java string will just be a <tt>String</tt>.
For Java string will just be a <tt>String</tt>.
Line 174: Line 174:
In C++ ,this would become:
In C++ ,this would become:


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


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


     void get(float64[] data);
     void get(doible[] data);


----
----
Line 226: Line 226:
     }
     }


This only supports primitive types, i.e. DbfBool,...,DbfFloat64.
This only supports primitive types, i.e. DbfBoolean,...,DbfFloat64.


=== Arithmetic Type Conversions ===
=== Arithmetic Type Conversions ===
Line 232: Line 232:
     class DbfConvertScalar {
     class DbfConvertScalar {
     public:
     public:
         static int16 get(Dbf to);
         static int16 get(Dbf from);
         static int32 get(Dbf to);
         static int32 get(Dbf from);
         static int64 get(Dbf to);
         static int64 get(Dbf from);
         static float32 get(Dbf to);
         static float32 get(Dbf from);
         static float64 get(Dbf to);
         static float64 get(Dbf from);
         static get(Dbf to, int32 lenValue,const *char value);
         static get(Dbf from, int32 lenValue,const *char value);
         static get(DbfBool to, int32 lenValue,const *char value);
         static get(DbfBoolean from, int32 lenValue,const *char value);
         static put(Dbf from, int16 value);
         static put(Dbf to, int16 value);
         static put(Dbf from, int32 value);
         static put(Dbf to, int32 value);
         static put(Dbf from, int64 value);
         static put(Dbf to, int64 value);
         static put(Dbf from, float32 value);
         static put(Dbf to, float32 value);
         static put(Dbf from, float64 value);
         static put(Dbf to, float64 value);
         static put(Dbf from,int32 lenValue,const *char value);  
         static put(Dbf to,int32 lenValue,const *char value);  
         static put(DbfBool from, int32 lenValue,const *char value);
         static put(DbfBoolean to, int32 lenValue,const *char value);


         static get(DbfArray to, int16 value[]);
         static get(DbfArray from, int16 value[]);
         static get(DbfArray to, int32 value[]);
         static get(DbfArray from, int32 value[]);
         static get(DbfArray to, int64 value[]);
         static get(DbfArray from, int64 value[]);
         static get(DbfArray to, float32 value[]);
         static get(DbfArray from, float32 value[]);
         static get(DbfArray to, float64 value[]);
         static get(DbfArray from, float64 value[]);
         static put(DbfArray from, int16 value[]);
         static put(DbfArray to, int16 value[]);
         static put(DbfArray from, int32 value[]);
         static put(DbfArray to, int32 value[]);
         static put(DbfArray from, int64 value[]);
         static put(DbfArray to, int64 value[]);
         static put(DbfArray from, float32 value[]);
         static put(DbfArray to, float32 value[]);
         static put(DbfArray from, float64 value[]);
         static put(DbfArray to, float64 value[]);
     }
     }


Line 263: Line 263:
* The get to a string uses printf semantics
* The get to a string uses printf semantics
* The put from a string value just uses scanf 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
* The <tt>DbfBoolean</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>
* For DbfArray tt>to</tt> and <tt>from</tt> must be an array of one of <tt>DbfInt16</tt>, ..., <tt>DbfFloat64</tt>


Line 275: Line 275:
: 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 generated header file. 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
Line 291: Line 291:


     enum BasicType {
     enum BasicType {
         BasicTypeBool,     // DbfBool
         basicTypeBoolean, // DbfBoolean
         BasicTypeOctet,    // DbfOctet
         basicTypeOctet,    // DbfOctet
         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,      // DbfBoolean,...,DbfStruct
         DbfTypeMenu,        // DbfMenu
         dbfTypeMenu,        // DbfMenu
         DbfTypeEnum,        // DbfEnum
         dbfTypeEnum,        // DbfEnum
         DbfTypeLink,        // DbfLink
         dbfTypeLink,        // DbfLink
         DbfTypeDevice,      // DbfDevice
         dbfTypeDevice,      // DbfDevice
         DbfTypeMDArray,    // DbfMDArray
         dbfTypeMDArray,    // DbfMDArray
         DbfTypeTimeStamp   // DbfTimeStamp
         dbfTypeTimeStamp   // DbfTimeStamp
     }
     }


Line 336: Line 336:
     }
     }


The generated Java file is
A generated file <tt>DisplayLimit.java</tt> contains:


     puiblic class DisplayLimit implements Struct{
     puiblic class DisplayLimit implements Struct{
Line 354: Line 354:
     }
     }


The generated C++ header file will be
A generated file <tt>DisplayLimitSupport.java</tt> contains:
 
    class DisplayLimit : public Struct {
    public:
        DbfFloat64 *low;
        DbfFloat64 *high;
        static int16 lowIndex = 1;
        static int16 highIndex = 2;
        static int16 lastIndex = highIndex;
        Dbf *getField(short index) {
            switch(index) {
                case lowIndex: return(low);
                case highIndex: return(high);
                default: throw IllegalStateException;
            }
            return null;
        }
    };
 


For <tt>struct(displayLimit)</tt> the following code is generated for Java
For <tt>struct(displayLimit)</tt> the following code is generated for Java
Line 384: Line 366:
         public static final void get(DbfStruct from,DisplayLimitData data) {
         public static final void get(DbfStruct from,DisplayLimitData data) {
             DbfFloat64 dbf = from.getInterface(1);
             DbfFloat64 dbf = from.getInterface(1);
             data.low = dbf->get();
             data.low = dbf.get();
             DbfFloat64 dbf = from.getInterface(2);
             DbfFloat64 dbf = from.getInterface(2);
             data.high = dbf->get();
             data.high = dbf.get();
         }
         }
         public static final void put(DbfStruct to, DisplayLimitData data) {
         public static final void put(DbfStruct to, DisplayLimitData data) {
             DbfFloat64 dbf = to.getInterface(1);
             DbfFloat64 dbf = to.getInterface(1);
             dbf->put(data.low);
             dbf.put(data.low);
             DbfFloat64 dbf = to.getInterface(2);
             DbfFloat64 dbf = to.getInterface(2);
             dbf->put(data.high);
             dbf.put(data.high);
         }
         }
     }
     }
Line 403: Line 385:
     }
     }


<b>Question</b> Who calls DisplayLimitSupportRegister.createAndRegister()?


<bold>Question</bold> Who calls DisplayLimitSupportRegister.createAndRegister()?
Similar files are generated for C++.


For C++ similar code is generated.
For C++ similar code is generated.
Line 412: Line 395:
     record(Example) extends IocRecord {
     record(Example) extends IocRecord {
         ...
         ...
         field(fbool,bool)
         field(fboolean,boolean)
         field(octet,octet)
         field(octet,octet)
         field(fint,int16)
         field(fint,int16)
Line 432: Line 415:


     public class ExampleRecord implements Struct {
     public class ExampleRecord implements Struct {
         public DbfBool      fbool;
         public DbfBoolean  fboolean;
         public DbfOctet    ctet;
         public DbfOctet    ctet;
         public DbfInt16    fint;
         public DbfInt16    fint;
Line 445: Line 428:
         public DbfDevice    device;
         public DbfDevice    device;
         public DbfStruct    displayLimit;
         public DbfStruct    displayLimit;
         public static final int16 fboolIndex = 1;
         public static final int16 fbooleanIndex = 1;
         ...
         ...
         public static final int16 lastIndex = displayLimitIndex;
         public static final int16 lastIndex = displayLimitIndex;
         Dbf getField(short index) {
         Dbf getField(short index) {
             switch(index) {
             switch(index) {
                 case fboolIndex : return(fbool);
                 case fbooleanIndex : return(fboolean);
                 ...
                 ...
                 case displayLimitIndex: return(displayLimit);
                 case displayLimitIndex: return(displayLimit);
Line 469: Line 452:
     }
     }


<bold>Question</bold> Who calls ExampleRecordFactoryRegister.createAndRegister?
<b>Question</b> Who calls ExampleRecordFactoryRegister.createAndRegister?




Line 484: Line 467:


     interface Dbf{
     interface Dbf{
         bool isPrimitive(); // BasicTypeBool,...,BasicTypeFloat64
         boolean isPrimitive(); // BasicTypeBoolean,...,BasicTypeFloat64
         bool isBasic();
         boolean isBasic();
         BasicType getBasicType();
         BasicType getBasicType();
         RecordInstance getRecord();
         RecordInstance getRecord();
Line 499: Line 482:


     class Field {
     class Field {
    public:
         RecordInstance instance;
         RecordInstance &instance;
         short         index;
         short             index;
     };
     };
     ...
     ...
     class IntField : public Field {
     class IntField extends Field {
    public:
         int data;
         int data;
     }
     }
Line 513: Line 494:
* instance - a reference to RecordInstance
* instance - a reference to RecordInstance
* index - a 16 bit integer
* index - a 16 bit integer
* vtbl - a reference to the class implementation
* vtbl - a reference to the object implementation


An additional pointer field will be needed for things like monitors.
An additional pointer field will be needed for things like monitors.
Line 523: Line 504:
DbfOctet, ..., DbfFloat64 are all interfaces with methods get and put.
DbfOctet, ..., DbfFloat64 are all interfaces with methods get and put.


     interface DbfBool extends Dbf {
     interface DbfBoolean extends Dbf {
         bool get();
         boolean get();
         void put(bool val);
         void put(boolean val);
     }
     }
      
      
Line 562: Line 543:
file. Some examples are:
file. Some examples are:


     ExampleRecord *precord;
     ExampleRecord record;
     short myint;
     short myint;
     ...
     ...
     precord->ffloat->put(10.0);
     precord.ffloat.put(10.0);
     ...
     ...
     myint = precord->fint->get();
     myint = precord.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
Line 573: Line 554:
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";
     DbAddr dbdAddr = LocateInstance.getPV("recordname.value");
     int32 len = strlen(name);
     if(dbdAddr==null) // do something
    DbdAddr *pDbdAddr;
     Dbf dbf = dbdAddr.getField();
    DbfFloat64 *pfield;
     if(!dbf.isPrimitive()
     ...
    || (dbf.getBasicType() != basicTypeFloat64) ) // do something
   
     DbfFloat64 dbfdouble = (DbfFloat64)dbf;
    pDbdAddr = pLocateInstance->getPV(len,name);
     dbfdouble.put(10.0);
     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.
 
    pDbfConvert->put(pDbdAddr->getAddr,10.0);
   


=== String fields ===
=== String fields ===
Line 600: Line 573:
     }
     }


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;
    MutableString *pstring = pMutableStringFactory->create(len);
     record.fstring.get(string);
     ...
     printf("%s\n",string);
    precord->string->get(len,pstring->data);
     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:
* Does record support have to have any say in how DbAccess manages storage for strings? I don't think so.


=== Structure Fields ===
=== Structure Fields ===
Line 633: Line 593:
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;
        MutableString *pname = pMutableStringFactory->create(len);
         dbdField.getName(name);
         pDbdField->getName(len,pname);
         printf("field %s\n",name);
        DbfType type = pDbdField->getType();
         printf("field %s type %d\n",pname->data,type);
        pname->destroy();
     }
     }


Line 650: Line 606:
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 limits;
      
      
     ...
     ...
     pDisplayLimitAccess->get(precord->displayLimit,limits);
     DisplayLimitSupport.get(record.displayLimit,limits);
     printf("low %f high %f\n",limits.low,limits.high);
     printf("low %f high %f\n",limits.low,limits.high);


Line 670: Line 626:
     }
     }


     interface DbfBoolArray extends DbfArray {
     interface DbfBooleanArray extends DbfArray {
         int32 get(int32 offset, int32 len, bool[] pto);
         int32 get(int32 offset, int32 len, boolean[] pto);
         int32 put(int32 offset, int32 len, bool[] pfrom);
         int32 put(int32 offset, int32 len, boolean[] pfrom);
     }
     }


Line 714: Line 670:
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
     double[] data;
     int32   nelements; // this is number of elements
     int   nelements; // this is number of elements
     DbfFloat64Array *parray = dynamic_cast<DbfFloat64Array *>precord->array;
     DbfFloat64Array array = precord->array;
     ...
     ...
     parray->put(0,nelements,pdata);
     arrayputput(0,nelements,data);


      
      
Line 851: Line 807:
         int32 getDefaultLength();
         int32 getDefaultLength();
         void getDefault(string value);
         void getDefault(string value);
         bool isReadonly();
         boolean isReadonly();
         bool isDesign();
         boolean isDesign();
         bool isSpecial();
         boolean isSpecial();
         int16 getAsl();
         int16 getAsl();
     }
     }
Line 893: Line 849:
     }
     }


     interface DbdLocate
----
         DbdMenu getMenu(string name);
<center>
         DbdLink getLink(string name);
 
         DbdDevice getDevice(string name);
== Locate Interfaces ==
         DbdStruct getStruct(string name);
 
         DbdRecord getRecord(string name);
</center>
 
=== Database Definitions ===
 
Classes are available to find and traverse the various Dbd definitons.
The implementation will be language specific. For Java they will be something
like.
 
    class Dbd<T> implements java.lang.Iterable<T> {
        public T get(string name);
    }
     class DbdLocate {
         public static Dbd<DbdMenu> getMenu();
         public static Dbd<DbdLink> getLink();
         public static Dbd<DbdDevice> getDevice();
         public static Dbd<DbdStruct> getStruct();
         public static Dbd<DbdRecord> getRecord();
    }
 
Then the following can be done
 
    Dbd<DbdMenu> dbdMenu = DbdLocate.getMenu;
    DbdMenu menu = dbdMenu.get("DisplayLimit");
    if(menu!=null) printf("found menu %s\n","DisplayLimit");
    Iterator<DbdMenu> iterator = dbdMenu.iterator();
    while(iterator.hasNext()) {
        DbdMenu menu = iterator.next();
        String name;
        menu.getName(name);
        printf("name is%s\n",name);
     }
     }


=== Record Instances ===
Classes are available to find and traverse record instances.
The implementation will be language specific. For Java it will be something
like.
    class DbInstance implements java.lang.Iterable<RecordInstance> {
        DbAddr getPV(string name);
        DbAddr getPV(string recordName, string fieldName);
        DbAddr getPV(RecordInstance instance, string fieldName);
        DbAddr getPV(RecordInstance instance,int16 field);
        DbAddr getIocRecordPV(RecordInstance instance, string fieldName);
        DbAddr getIocRecordPV(RecordInstance instance,int16 field);
    }
    class DbInstanceLocate{
        public static DbInstanceLocate get();
    }
The following can be done.
    DbInstance dbdInstance = DbInstanceLocate.get();
    DbAddr dbAddr = dbdInstance.getPV("example.value");
    if(dbAddr!=null) printf("found %s\n","example.value");
    Iterator<RecordInstance> iterator = dbdInstance.iterator();
    while(iterator.hasNext()) {
        RecordInstance record= iterator.next();
        String name;
        record.getName(name);
        printf("name is%s\n",name);
    }


----
----
Line 924: Line 939:
     }
     }


     interface DbdAddr {
     interface DbAddr {
        void    destroy();  // call this when done with DbAddr
        DbfType getType();
         RecordInstance getInstance();
         RecordInstance getInstance();
         int16 getIndex();
         Dbf getField();
        void getField(string name);
    }
 
    class LocateInstance{
        DbdAddr getPV(string name);
        DbdAddr getPV(string recordName, string fieldName);
        DbdAddr getPV(RecordInstance instance, string fieldName);
        DbdAddr getPV(RecordInstance instance,int16 field);
        DbdAddr getIocRecordPV(RecordInstance instance, string fieldName);
        DbdAddr getIocRecordPV(RecordInstance instance,int16 field);
     }
     }


Line 1,059: Line 1,062:
         ProcessState process(ProcessState state);
         ProcessState process(ProcessState state);
         // if special returns false when after is false put fails
         // if special returns false when after is false put fails
         bool special(bool after,Dbf[] field);
         boolean special(boolean after,Dbf[] field);
     }
     }



Revision as of 15:36, 1 September 2005

EPICS: dbdInterfaces - IOC Database Description

September 1 2005


Overview

This document describes definitions for code that accessses IOC records, i.e. the records created from Database Definitions. The definitions are intended for code that:

  • includes header files generated from dbd definitions. Header files are generated from the following dbd definitions:
    • record - Should only be included by record support.
    • struct - Included by code that understands the struct.
    • menu - Included by code that understands the menu.
  • does not include the header files.

Syntax

The syntax is defined so that it is easy to provide C++ and Java definitions.

NOTE: Most of the code fragments use Java syntax.

Primitive Types

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

In Java these types become:

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

In C++ these types become:

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

NOTES:

  • C++ may require something else to handle the integer types
  • Exceptions must be defined. TBD

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(boolean val) = 0;
    };

In Java would be:

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

class

For now only static class definitions are required. An example of a static class definition is:

    static class RegisterSupport {
        link(SupportFactory support, string name);
        ...
    }

In C++ would be:

    class RegisterSupport {
    public:
        static link(SupportFactory &support, int nameLength, char name[]);
        ...
    };

On Java would be:

    public final class RegisterSupport {
        public static final link(SupportFactory support, string name);
        ...
    }

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.

An example of a definition that includes a string argument is

    void get(string name);

In C++ this will become

    void get(int lenName, char *name);

In Java this will become

    void get(String name);

array

An example of a definition that includes an array is:

    void get(float64[] data);

In C++ ,this would become:

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

In Java this would be:

    void get(doible[] data);

C++ support for strings and arrays

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

    // ArrayCopy only works on primitive types
    class ArrayCopy {
    public:
        void copy(DbfArray from,DbfArray to);
    }

This only supports primitive types, i.e. DbfBoolean,...,DbfFloat64.

Arithmetic Type Conversions

    class DbfConvertScalar {
    public:
        static int16 get(Dbf from);
        static int32 get(Dbf from);
        static int64 get(Dbf from);
        static float32 get(Dbf from);
        static float64 get(Dbf from);
        static get(Dbf from, int32 lenValue,const *char value);
        static get(DbfBoolean from, int32 lenValue,const *char value);
        static put(Dbf to, int16 value);
        static put(Dbf to, int32 value);
        static put(Dbf to, int64 value);
        static put(Dbf to, float32 value);
        static put(Dbf to, float64 value);
        static put(Dbf to,int32 lenValue,const *char value); 
        static put(DbfBoolean to, int32 lenValue,const *char value);
        static get(DbfArray from, int16 value[]);
        static get(DbfArray from, int32 value[]);
        static get(DbfArray from, int64 value[]);
        static get(DbfArray from, float32 value[]);
        static get(DbfArray from, float64 value[]);
        static put(DbfArray to, int16 value[]);
        static put(DbfArray to, int32 value[]);
        static put(DbfArray to, int64 value[]);
        static put(DbfArray to, float32 value[]);
        static put(DbfArray to, float64 value[]);
    }

NOTES:

  • For Dbf to and from must be one of DbfInt16, ..., DbfFloat64
  • The get to a string uses printf semantics
  • The put from a string value just uses scanf semantics.
  • The DbfBoolean method supports all the choices specified in the DBD Record Instance Specification
  • For DbfArray tt>to and from must be an array of one of DbfInt16, ..., DbfFloat64

DbfTypes

The following naming conventions are used:

Dbf
any class starting with Dbf describes a field in a generated header file. 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. The IOC database implements the interfaces, with help from 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.

DbfTypes.h

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

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


Discussion of DbfTypes

The following shows the code generated from DBD files:

structure definitions

Two class implementations are generated from struct(name) definitions.

  1. A class named name
  2. A class named nameSupport

The class named name extends:

    interface Struct {
        Dbf getField(int16 index);
    }


If a structure is defined as:

    struct(DisplayLimit) {
        field(low,double)
        field(high,double)
    }

A generated file DisplayLimit.java contains:

    puiblic class DisplayLimit implements Struct{
        public DbfFloat64 low;
        public DbfFloat64 high;
        public static final int16 lowIndex = 1;
        public static final int16 highIndex = 2;
        public static final int16 lastIndex = indexHigh;
        Dbf getField(short index) {
            switch(index) {
                case lowIndex: return(low);
                case highIndex: return(high);
                default: throw java.lang.IllegalStateException;
            }
            return null;
        }
    }

A generated file DisplayLimitSupport.java contains:

For struct(displayLimit) the following code is generated for Java

    public class DisplayLimitData {
        public double low;
        public double high;
    }
    public final class DisplayLimitSupport implements StructFactory{
        public Struct create() { return new DisplayLimit; }
        public static final void get(DbfStruct from,DisplayLimitData data) {
            DbfFloat64 dbf = from.getInterface(1);
            data.low = dbf.get();
            DbfFloat64 dbf = from.getInterface(2);
            data.high = dbf.get();
        }
        public static final void put(DbfStruct to, DisplayLimitData data) {
            DbfFloat64 dbf = to.getInterface(1);
            dbf.put(data.low);
            DbfFloat64 dbf = to.getInterface(2);
            dbf.put(data.high);
        }
    }
    public final class DisplayLimitSupportRegister {
        static public createAndRegister() {
            DisplayLimitSupport support = new DisplayLimitSupport;
            RegisterSupport.structure(support,"DisplayLimit");
        }
    }

Question Who calls DisplayLimitSupportRegister.createAndRegister()?

Similar files are generated for C++.

For C++ similar code is generated.

record definitions

If a record is defined as:

    record(Example) extends IocRecord {
        ...
        field(fboolean,boolean)
        field(octet,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))
    }


The generated Java file is

    public class ExampleRecord implements Struct {
        public DbfBoolean   fboolean;
        public DbfOctet     ctet;
        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 fbooleanIndex = 1;
        ...
        public static final int16 lastIndex = displayLimitIndex;
        Dbf getField(short index) {
            switch(index) {
                case fbooleanIndex : return(fboolean);
                ...
                case displayLimitIndex: return(displayLimit);
                default: throw java.lang.IllegalStateException;
            }
        }
    }
    public final class ExampleRecordFactory implements StructFactory{
        public static final Struct create() { return new ExampleRecord; }
    }
    public final class ExampleRecordFactoryRegister {
        static public createAndRegister() {
            ExampleRecordFactory  factory = new ExampleRecordFactory;
            RegisterSupport.record(factory,"ExampleRecord");
        }
    }

Question Who calls ExampleRecordFactoryRegister.createAndRegister?


Similar code is generated for C++.


Database Fields

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

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

The interfaces are designed as follows:

  • DbAccess provides storage for fields and never exposes the address of any field.
  • 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 {
       RecordInstance instance;
       short          index;
   };
   ...
   class IntField extends Field {
       int data;
   }
   ...

This each field has the overhead of

  • instance - a reference to RecordInstance
  • index - a 16 bit integer
  • vtbl - a reference to the object implementation

An additional pointer field will be needed for things like monitors. 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.

    interface DbfBoolean extends Dbf {
        boolean get();
        void put(boolean val);
    }
   
    interface DbfOctet extends Dbf {
        octet get();
        void put(octet 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;
    short myint;
    ...
    precord.ffloat.put(10.0);
    ...
    myint = precord.fint.get();

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

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

String fields

The interface for a string field is:

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

The following code prints a string.

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

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(name);
        printf("field %s\n",name);
    }


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 limits;
    
    ...
    DisplayLimitSupport.get(record.displayLimit,limits);
    printf("low %f high %f\n",limits.low,limits.high);

Array Fields

    interface DbfArray extends Dbf {
       DbfType getType();
       int32 getNelements();
       void setNelements(int32 len);
    }
    interface DbfOctetArray extends DbfArray {
       int32 get(int32 offset, int32 len, octet[] pto);
       int32 put(int32 offset, int32 len, octet[] pfrom);
    }
    interface DbfBooleanArray extends DbfArray {
       int32 get(int32 offset, int32 len, boolean[] pto);
       int32 put(int32 offset, int32 len, boolean[] 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);
    }

The following puts data into a float64 field.

    ExampleRecord record;
    double[] data;
    int   nelements; // this is number of elements
    DbfFloat64Array array = precord->array;
    ...
    arrayputput(0,nelements,data);


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

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 and DbfDevice

DbfLink is described as

    enum LinkDir {
        LinkDirNone,
        LinkDirProcess,
        LinkDirIn,
        LinkDirOut,
        LinkDirInOut
    }
   
    interface DbfLink extends Dbf {
        LinkDir getDir();
        DbdLink getDbdLink();
        LinkSupport getSupport();
    }
   
    interface DbfDevice extends Dbf {
        LinkDir getDir();
        DbdDevice getDbdDevice();
        Support support; // Support is base class for device support
    }

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

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.

In the definitions:


DbdMenu

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


DbdLink and DbdDevice

    interface DbdSupport {
        LinkDir getDir();
        int32 getChoiceNameLength();
        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);
    }
   

DbdStruct and DbdRecord

    interface DbdAttribute {
        int32 getDefaultLength();
        void getDefault(string value);
        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();
        void getName(string name);
        DbfType getType();
        DbdDefaults getDefaults();
        DbdAttribute getAttributes();
    }
    interface DbdStruct {
        int32 getNameLength();
        void getName(string name);
        int16 getNumberFields();
        DbdField getFieldDescription(int16 index);
        DbdStructLifetime getLifetime();
    }
    interrface DbdRecord {
        // Need list of instances
        int32 getNameLength();
        void getName(string name);
        int16 getNumberFields();
        DbdField getFieldDescription(int16 index);
        DbdRecordSupport getSupport();
        DbdRecordLifetime getLifetime();
    }

Locate Interfaces

Database Definitions

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

    class Dbd<T> implements java.lang.Iterable<T> {
        public T get(string name);
    }
    class DbdLocate {
        public static Dbd<DbdMenu> getMenu();
        public static Dbd<DbdLink> getLink();
        public static Dbd<DbdDevice> getDevice();
        public static Dbd<DbdStruct> getStruct();
        public static Dbd<DbdRecord> getRecord();
    }

Then the following can be done

    Dbd<DbdMenu> dbdMenu = DbdLocate.getMenu;
    DbdMenu menu = dbdMenu.get("DisplayLimit");
    if(menu!=null) printf("found menu %s\n","DisplayLimit");
    Iterator<DbdMenu> iterator = dbdMenu.iterator();
    while(iterator.hasNext()) {
        DbdMenu menu = iterator.next();
        String name;
        menu.getName(name);
        printf("name is%s\n",name);
    }

Record Instances

Classes are available to find and traverse record instances. The implementation will be language specific. For Java it will be something like.

    class DbInstance implements java.lang.Iterable<RecordInstance> {
        DbAddr getPV(string name);
        DbAddr getPV(string recordName, string fieldName);
        DbAddr getPV(RecordInstance instance, string fieldName);
        DbAddr getPV(RecordInstance instance,int16 field);
        DbAddr getIocRecordPV(RecordInstance instance, string fieldName);
        DbAddr getIocRecordPV(RecordInstance instance,int16 field);
    }
    class DbInstanceLocate{
        public static DbInstanceLocate get();
    }

The following can be done.

    DbInstance dbdInstance = DbInstanceLocate.get();
    DbAddr dbAddr = dbdInstance.getPV("example.value");
    if(dbAddr!=null) printf("found %s\n","example.value");
    Iterator<RecordInstance> iterator = dbdInstance.iterator();
    while(iterator.hasNext()) {
        RecordInstance record= iterator.next();
        String name;
        record.getName(name);
        printf("name is%s\n",name);
    }

Run Time Interfaces

Record Instance

    interface RecordAccess {
        RecordSupport getRecordSupport();
        Dbf getField(string name);
    }
    interface RecordInstance {
        int32 getNameLength();
        void getName(string name);
        DbdRecord getDescription();
        RecordAccess getRecordAccess();
        DbdRecord getIocRecordDescription();
        RecordAccess getIocRecordAccess();
    }
    interface DbAddr {
        RecordInstance getInstance();
        Dbf  getField();
    }


Record, Link, Device Support

Registration and Instance Creation

    interface StructFactory {
        DbfStruct create();
    }
    interface RecordSupportFactory {
        RecordSupport create(RecordInstance instance);
        void destroy(RecordSupport support);
    }
    interface SupportFactory {
        Support create(Dbf field);
    }
    class RegisterSupport {
        structure(StructFactory create, string name);
        record(StructFactory create, string name);
        link(SupportFactory support,string name);
        device(SupportFactory support,string name);
        record(RecordSupportFactory support,string name);
    }

RegisterSupport is implemented by iocCore. Each struct DBD definition must register a StructFactory to create instances of the structure or record.

Each link, device, and record support must register a SupportFactory to create instances of the support to attach to the link, device, or record instance. During database initialization, iocCore calls the factory methods.

Link/Device Base

    interface Support { // base for Link and Device support
        void report(int16 level);
        void cancel();
        void destroy();
        void initialize();
        void connect();
        void disconnect();
    }

Support is the base class for all link and device support. An instance of this is connected to each DbfLink or DbfDevice field. The methods are:

  • report - report
  • cancel - Cancel any outstanding I/O
  • destroy - This is called if the field is being changed after initialization or if the record is being removed.
  • initialize - Called to initialize a link.
  • connect - Called to connect. Note that this is different than initilization.
  • disconnect - disconnect.

Normally record support does not need to call any of the Support methods since Database Access does this automatically. For example if a link or device field is modified via a channel access put, database access will call destroy before modifying the link and initialize and connect after the link is modidied.

Link Support

    enum LinkWaitResult {
        linkNoop,           // Nothing was done, e.g. link is null link
        linkDone,           // field was modified. No wait is necessary
        linkWaitSequential, // waiting. must processes links sequentially
        linkWaitParallel,   // waiting. parallel processing OK
    }
    interface Callback {
        void done();
        void timedout();
    }
    interface LinkSupport extends Support {
        void get(Dbf field);
        LinkWaitResult getWait(Dbf field, double timeout,Callback callback);
        void put(Dbf field);
        LinkWaitResult putWait(Dbf field, double timeout,Callback callback);
        void process();
        LinkWaitResult processWait(double timeout,Callback callback);
    }

LinkSupport 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

The Wait methods all return LinkWaitResult.


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

    enum ProcessState {
        processCancel,
        processIdle,
        processInputActive,
        processActive,
        processOutputActive,
        processDone
    }
    interface RecordSupport {
        void destroy();
        void initialize(int32 pass);
        ProcessState process(ProcessState state);
        // if special returns false when after is false put fails
        boolean special(boolean after,Dbf[] field);
    }

ProcessState supports the following semantics:

cancel
If the record is active terminate

Database Access

    class DbAccess {
        void process(RecordInstance instance);
    }