Difference between revisions of "V4 Design: dbdInterfaces"
MartyKraimer (talk | contribs) |
MartyKraimer (talk | contribs) |
||
Line 109: | Line 109: | ||
==== interface ==== | ==== interface ==== | ||
An example of an interface definition is: | An example of an interface definition is: | ||
( | |||
interface DbfBool extends Dbf { | interface DbfBool extends Dbf { | ||
bool get(); | bool get(); | ||
Line 345: | Line 345: | ||
... | ... | ||
field(fbool,bool) | field(fbool,bool) | ||
field( | field(octet,octet) | ||
field(fint,int16) | field(fint,int16) | ||
... | ... | ||
Line 368: | Line 368: | ||
public: | public: | ||
DbfBool *fbool; | DbfBool *fbool; | ||
DbfOctet * | DbfOctet *octet; | ||
DbfInt16 *fint; | DbfInt16 *fint; | ||
... | ... | ||
Line 393: | Line 393: | ||
class ExampleRecord extends IocRecord { | class ExampleRecord extends IocRecord { | ||
public DbfBool fbool; | public DbfBool fbool; | ||
public DbfOctet | public DbfOctet ctet; | ||
public DbfInt16 fint; | public DbfInt16 fint; | ||
... | ... | ||
Line 715: | Line 715: | ||
LinkDir getDir(); | LinkDir getDir(); | ||
DbdLink getDbdLink(); | DbdLink getDbdLink(); | ||
LinkSupport | LinkSupport getSupport(); | ||
} | } | ||
Line 849: | Line 849: | ||
interface RecordAccess { | interface RecordAccess { | ||
RecordSupport getRecordSupport(); | RecordSupport getRecordSupport(); | ||
Dbf &getField( | Dbf &getField(string name); | ||
} | } | ||
Line 925: | Line 925: | ||
interface LinkSupport extends Support { | interface LinkSupport extends Support { | ||
void get(Dbf field); | void get(Dbf field); | ||
void getWait(Dbf field | void getWait(Dbf field, double timeout,Callback callback); | ||
void put(Dbf field | void put(Dbf field); | ||
void putWait(Dbf field | void putWait(Dbf field, double timeout,Callback callback); | ||
void process(); | void process(); | ||
void processWait(double timeout,Callback callback); | void processWait(double timeout,Callback callback); | ||
Line 970: | Line 970: | ||
interface RecordSupport { | interface RecordSupport { | ||
void destroy(); | void destroy(); | ||
void initialize(); | void initialize(int32 pass); | ||
processState process(processState state); | processState process(processState state); | ||
// if special returns false when after is false put fails | |||
bool special(bool after,Dbf[] field); | |||
} | } | ||
Revision as of 19:27, 29 August 2005
EPICS: dbdInterfaces - IOC Database Description
August 29 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 C++ syntax.
Primitive Types
- bool - 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:
- bool => bool
- octet => byte BUT no arithmetic is implied
- int16 => short
- int32 => int
- int64 => long
- float32 => float
- float64 => double
In C++ these types become:
- bool => 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, 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 DbfBool extends Dbf { bool get(); void put(bool val); }
In C++ would be:
class DbfBool : public Dbf { public: virtual bool get() = 0; virtual void put(bool val) = 0; };
In Java would be:
interface DbfBool extends Dbf { bool get(); void put(bool val) }
string
All string arguments are assumned to be UTF-8 compatible.
For C++ a string will be a <bold>char *</bold> array containing UTF-8 compatible characters not necessarily terminated with a null character. Wherever a <bold>string</bold> argument appears, the C++ definition will have <bold>len</bold> and <bold>char *</bold> arguments/
For Java string will just be a <bold>String</bold>. 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, float64 data[]);
In Java this would be:
void get(float64[] 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
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 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 DbfBool 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 DbdLinkSupport describes a dbd link 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 { BasicTypeBool, // DbfBool BasicTypeOctet, // DbfOctet BasicTypeInt16, // DbfInt16 BasicTypeInt32, // DbfInt32 BasicTypeInt64, // DbfInt64 BasicTypeFloat32, // DbfFloat32 BasicTypeFloat64, // DbfFloat64 BasicTypeString, // DbfString BasicTypeArray, // DbfArray BasicTypeStruct, // DbfStruct }
enum DbfType { DbfTypeBasic, // DbfBool,...,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
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(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)) }
Then the generated C++ header file will be
class IocRecord { public: RecordInstance *base; };
class ExampleRecord : public IocRecord { public: DbfBool *fbool; DbfOctet *octet; 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 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 baseIndex = 1; public static final int16 fboolIndex = 2; ... public static final int16 lastIndex = displayLimitIndex; }
Database Fields
Each database field is accessed via an interface which all extend the following interface:
interface Dbf{ bool isPrimitive(); // BasicTypeBool,...,BasicTypeFloat64 bool 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 { 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. 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 DbfBool extends Dbf { bool get(); void put(bool 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 *precord; 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
char *name = "recordname.value"; int32 len = strlen(name); DbdAddr *pDbdAddr; DbfFloat64 *pfield; ... 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.
pDbfConvert->put(pDbdAddr->getAddr,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); }
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.
ExampleRecord *precord; epicsInt32 len = precord->string->getLength(); MutableString *pstring = pMutableStringFactory->create(len); ... 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 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
interface DbfStruct extends Dbf { int16 getNfields(); Dbf getInterface(int16 index); DbdField getDescription(int16 index); }
The following traverses the fields of a DbfStruct
DbfStruct *pDbfStruct; MutableString *pMutableString; for(i=0; t< pDbfStruct->getNfields(); i++) { DbdField *pDbdField = pDbfStruct->getDescription(i); int len = pDbdField->getNameLength(); MutableString *pname = pMutableStringFactory->create(len); pDbdField->getName(len,pname); DbfType type = pDbdField->getType(); printf("field %s type %d\n",pname->data,type); pname->destroy(); }
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 *precord; DisplayLimitData limits; ... pDisplayLimitAccess->get(precord->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 DbfBoolArray extends DbfArray { int32 get(int32 offset, int32 len, bool[] pto); int32 put(int32 offset, int32 len, bool[] 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 *precord; float64 *pdata; // assume this has been allocated somehow int32 nelements; // this is number of elements DbfFloat64Array *parray = dynamic_cast<DbfFloat64Array *>precord->array; ... parray->put(0,nelements,pdata);
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); bool isReadonly(); bool isDesign(); bool 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(); }
interface DbdLocate DbdMenu getMenu(string name); DbdLink getLink(string name); DbdDevice getDevice(string name); DbdStruct getStruct(string name); DbdRecord getRecord(string 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 DbdAddr { void destroy(); // call this when done with DbAddr DbfType getType(); RecordInstance getInstance(); int16 getIndex(); 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); }
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); }
RegisterSupport 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
interface Support { // base for Link and Device support void report(int16 level); 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
- 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.
Link Support
interface Callback { void done(); void timedout(); }
interface LinkSupport extends Support { void get(Dbf field); void getWait(Dbf field, double timeout,Callback callback); void put(Dbf field); void putWait(Dbf field, double timeout,Callback callback); void process(); void 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
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(int32 pass); processState process(processState state); // if special returns false when after is false put fails bool special(bool after,Dbf[] field); }
Database Access
class DbAccess { void process(RecordInstance instance); }