Difference between revisions of "V4 Design: Runtime Interfaces"

From EPICSWIKI
 
(6 intermediate revisions by the same user not shown)
Line 1: Line 1:
= EPICS: IOC Runtime Interfaces =
EPICS: IOC Runtime Interfaces November 03 2005


----
= Overview =
This document describes  definitions for IOC runtime,
i.e. interfaces implemented or used by database access, record support,
link support, and device support.
It assumes knowledge of the interfaces described in dbdInterfaces.


September 15
<b>NOTES</b>
* IOC runtime needs lots more work
* methods, arguments, and return values are likely to undergo many changes


The example code presented in this document assume the following database
definitions:
    struct(DisplayLimit) {
        field(low,double)
        field(high,double)
    }
    record(Example) extends RecordCommon {
        field(common,struct(RecordCommon))
        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))
    }
----
----
<center>
<center>
= Record Support =
</center>
    enum ProcessState {
        processCancel,
        processStart,
        processContinue,
        processFinish,
    }


== Overview ==
    enum ProcessReturn {
        processDone,
        processAbort,
        processActive
    }


</center>
    interface RecordSupport {
        void destroy();
        void initialize(int16 pass);
        ProcessReturn process(ProcessState state,Callback done);
        // if special returns false when after is false put fails
        boolean special(boolean after,Dbf[] field);
    }


This document describes  definitions for IOC runtime,
<tt>ProcessState</tt> supports the following semantics:
i.e. interfaces implemented or used by database access, record support,
* cancel - If the record is active terminate
link support, and device support.
* For all other states see the companion document "V4 Design: Record Processing"


It assumes knowledge of the interfaces described in dbdInterfaces.
The methods of <tt>RecordSupport</tt> are:
* <tt>destroy</tt> - Called when a record instance is deleted.
* <tt>initialize</tt> - Called twice (pass = 0,1) when a record is initialized.
** pass 0 - Can do all local initialization but can not attach to anything external
** pass 1 - Can attach to external things
* <tt>process</tt> - Called to process a record. See "V4 Design: Record Processing" for semantics.
* <tt>special</tt> - Called when a field declared special in a DBD file is modified. <tt>field</tt> is an array of Dbfs showing which field is being modified.


<b>NOTE</b> IOC runtime needs lots more work
== Discussion of special ==


Special is passed a Dbf array that identifies the field being modified.
For example <tt>ExampleRecord.dbd</tt> has a field:
    field(displayLimit,struct(displayLimit))
<tt>ExampleRecordSupport.java</tt> might implement special as follows:
    boolean special(boolean after, Dbf[] field)
    {
        switch(field[0].getIndex()) {
        ...
        case ExampleRecord.displayLimitIndex:
            if(field.length==1) {
                // displayLimits itself is being modified. Do something
            } else {
                // a field of displayLimits is being modified
                switch(field[1].getIndex) {
                    case DisplayLimit.lowIndex:
                          // low being modified. do something
                    case DisplayLimit.highIndex:
                          // high being modified. do something
                }
            }
        ...
        }
    }
----
----
<center>
<center>
== Support Code ==
= Support for link and struct fields =
  </center>
  </center>


This section described interfaces for
Support can be provided for link and struct fields.
strings, arrays, and conversions.  
An arbitrary number of Support implementations can exist.
They are intended for use by Database Access, Record Support, etc.
An implementation can be either soft support or support that communicates with
hardware.


=== C++ support for strings ===
Recall that the support DBD definition is:


This subsection describes interfaces for C++ code that needs tempory storage
    support(choiceName, supportStructName)
for strings. For Java the String class is used.


The following interfaces are for allocating tempory storage for strings:
<tt>choiceName</tt> is the name that identifies the implementation.
<tt>supportStructName</tt> is the name of a structure used by support for
configuration.


     class NonmutableString {
The syntax for interface definitions is similar to Java syntax
since it is more concise than C++.
It can easily be translated to C++ syntax. For example the
definition:
    interface LinkInt16 {
        LinkResult get(Callback callback,Int16 data);
        LinkResult put(Callback callback,int16 data);
        boolean getBounds(Int16 low, Int16 high);
    }
In C++ would be:
     class LinkInt16 {
     public:
     public:
        void destroy(); // Call this when done with string
        virtual LinkResult get(Callback &callback, int16_t &data) = 0;
        int32_t getLength();
        virtual LinkResult put(Callback &callback, int16_t data) = 0;
        char data[];
        virtual bool getBounds(int16_t &low, int16_t &high) = 0;
    };
 
<b>NOTE:</b> Int16 becomes int16_t &data and int16 data becomes int16_t data.
 
Support and SupportMonitor are interfaces that are common to
the support for both link and struct fields.
All support <b>must</b> register interface <tt>Support</tt>
and if it supports monitors interface <tt>SupportMonitor</tt>.
 
<b>NOTE: arguments and return types ignored for now</b>
    interface Support {
        void report(int16 level);
        void destroy();
        void initialize(int16 pass);
        void connect();
        void disconnect();
        void cancel();
     }
     }
   
 
     class NonmutableStringFactory {
     interface SupportMonitor {
     public:
        void addMonitor(Callback callback);
         static NonmutableString *create(int32_t len,char data[]);
        void removeMonitor();
     }
 
<tt>Support</tt> is an interface that must be implemented by every
support module.
An instance of this is connected to each DbfLink or DbfStruct field that is configured to use support.
The <tt>Support</tt> methods are:
* <tt>report</tt> - report
* <tt>cancel</tt> - Cancel any outstanding I/O
* <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.
 
<tt>SupportMonitor</tt> is an interface that is implemented by support that
supports monitors. An example is support for hardware interrupts.
The methods are:
* <tt>addMonitor</tt> - Add a monitor callback
* <tt>removeMonitor</tt> - Remove monitor
 
Normally recordSupport does not need to call any of the Support
or SupportMonitor methods since database access does this automatically.
For example if a link 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 modified.
---
<center>
=  Link Support =
</center>
 
== Overview ==
 
This section describes the standard interfaces
used by the record types supplied with epics base.
These interfaces are appropriate for the soft support supplied with EPICS
base and should also be sufficient for a wide variety of hardware support.
In particular they sufficient for communication with asynDriver.
 
Soft support should try to implement the same interfaces implemented by
the Channel/Database access support supplied with base. The support
can define support structures for it's own use.
Implementing these interfaces means that the support will work for
the same set of record links as the Channel/Database access support.
 
Hardware support should, if possible, also implement the same interfaces
implemented by the Channel/Database access support.
Message based support may also have to implement the AsynOctet interface
but this interface is only used by a few record types.
 
This section describes the interfaces implemented by all link support
and then the standard interfaces used by the records supplied with base.
 
 
== Definitions that apply to Process,Monitor,Input,and Output support ==
These are the definitions used by the Channel/Database access support
supplied with base.
 
    enum LinkResult {
        linkNoop,          // Nothing was done, e.g. link is null link
        linkNoop,          // field was modified. No wait is necessary
        linkWait,          // waiting for completion
    }
 
    interface Callback {
        void done();
        void failure();
    }
 
== Octet Support ==
 
    interface LinkOctet {
        LinkResult get(Callback callback, Octet data);
        LinkResult put(Callback callback, octet data);
    }
    interface LinkArrayOctet {
        LinkResult get(Callback callback, octet[] data);
        LinkResult put(Callback callback, octet[] data);
    }
 
 
== asynOctet Support ==
 
This still needs more work. It attempts to reproduce the functionality
of V3 asynDriver.
 
    interface AsynOctet {
        LinkResult write(Callback callback,
          octet[] data, int32 numchars, Int32 nbytesTransfered);
        LinkResult writeRaw(Callback callback,
          octet[] data, int32 numchars, Int32 nbytesTransfered);
        LinkResult read(Callback callback,
          octet[] data, Int32 nbytesTransfered);
        LinkResult readRaw(Callback callback,
          octet[] data, Int32 nbytesTransfered);
        void flush();
        void setInputEos(octet[] eos);
        void getInputEos(octet[] eos);
        void setOutputEos(octet[] eos);
        void getOutputEos(octet[] eos);
    }
 
== asynDigital Support ==
 
    enum interruptReason {
        interruptOnZeroToOne, interruptOnOneToZero, interruptOnBoth
    }
    struct asynDigitalInterrupt {
        octet[] mask;
        int32 addr;
         Callback callback
    }
 
    interface AsynDigital {
          LinkResult write(Callback callback,octet[] value, octet[] mask);
          LinkResult read(Callback callback,octet[] value, octet[] mask);
          void setInterrupt(octet[] mask, interruptReason reason);
          void clearInterrupt(octet[] mask);
          void getInterrupt(octet[] mask, interruptReason reason);
          void registerInterruptUser(interruptCallbackUInt32Digital callback,
              octet[] mask);
          void cancelInterruptUser();
    }
 
== Boolean Support ==
    interface LinkBoolean {
        LinkResult get(Callback callback,Boolean data);
        LinkResult put(Callback callback,boolean data);
    }
    interface LinkArrayBoolean {
        LinkResult get(Callback callback,boolean[] data);
        LinkResult put(Callback callback,boolean[] data);
    }
 
 
The data source must be a boolean or a string that contains a valid
boolean value.
 
== Integer Support ==
 
Support is available. for int16, int32, and int64.
 
    interface LinkInt16 {
        LinkResult get(Callback callback,Int16 data);
        LinkResult put(Callback callback,int16 data);
        boolean getBounds(Int16 low, Int16 high);
    }
    interface LinkArrayInt16 {
        LinkResult get(Callback callback,int16[] data);
        LinkResult put(Callback callback,int16[] data);
    }
    interface LinkInt32 {
        LinkResult get(Callback callback,Int32 data);
        LinkResult put(Callback callback,int32 data);
        boolean getBounds(Int32 low, Int32 high);
    }
    interface LinkArrayInt32 {
        LinkResult get(Callback callback,int32[] data);
        LinkResult put(Callback callback,int32[] data);
    }
    interface LinkInt64 {
        LinkResult get(Callback callback,Int64 data);
        LinkResult put(Callback callback,int64 data);
        boolean getBounds(Int64 low, Int64 high);
    }
    interface LinkArrayInt64 {
        LinkResult get(Callback callback,int64[] data);
        LinkResult put(Callback callback,int64[] data);
    }
 
       
== Float Support ==
 
Support is available. for float32 and float64.
 
    interface LinkFloat32 {
        LinkResult get(Callback callback,Float32 data);
        LinkResult put(Callback callback,float32 data);
    }
    interface LinkArrayFloat32 {
        LinkResult get(Callback callback,float32[] data);
        LinkResult put(Callback callback,float32[] data);
    }
    interface LinkFloat64 {
        LinkResult get(Callback callback,Float64 data);
        LinkResult put(Callback callback,float64 data);
    }
    interface LinkArrayFloat64 {
        LinkResult get(Callback callback,float64[] data);
        LinkResult put(Callback callback,float64[] data);
     }
     }


     class MutableString : public NonmutableString {
== String Support ==
    public:
     interface LinkString {
        int32_t getCapacity();
        LinkResult get(Callback callback,string data);
        void setLength(int32_t len);
        LinkResult put(Callback callback,string data);
     }
     }
   
     interface LinkArrayString {
     class MutableStringFactory {
         LinkResult get(Callback callback,string[] data);
         static MutableString *create(int32_t capacity);
        LinkResult put(Callback callback,string[] data);
     }
     }


These will use free lists to manage the storage for the octet arrays.


=== array copy ===
The data source must be a string.
---
<center>
= Struct Support =
  </center>
  </center>
== support for RecordCommon ==
This implements an interface, not yet defined, that is similar to the
interface record support implements.
== support for LinearConvert ==
This implements the interface
    interface SupportLinearConvert{
          void convert(int32 raw,Float64 value) // from raw to eng
          void convert(float64 value,Int32 raw) // from eng to raw
    }


ArrayCopy copies arrays an array performing conversions if necessary.
In addition it provides a factory
    interface FactoryLinearConvert{
        SupportLinearConvert create(string choiceName,
            DbfStruct linearConvert,DbfLink inlink)
    }
== support for simulation ==
This needs to be defined.
== support for breakpoint tables ==
This needs to be defined.


    interface ArrayCopy {
----
        void copy(DbfArray from,DbfArray to);
<center>
    }


This only supports primitive types, i.e. DbfBoolean,...,DbfFloat64.
= Runtime Database Access =
It does NOT convert between
* DbfBoolean and another type.
* DbfOctet and another type.
Thus for DbfBoolean and DbfOctet it is willing to copy
but not convert to/from other types.


=== Arithmetic Conversions ===
  </center>
  </center>


== Overview ==
Database Access, with help from record, link, and device support
implements the dbdInterfaces.
Other than Database Access, fields of a record can only
be accessed via the interfaces described in dbdInterfaces.
This allows the database to handle
actions like posting database monitors without any help from
record, link, or device support.
Database access by default allocates the actual storage for each field
but allows support code to register itself to manage storage for field
instances.
Two examples, both involving arrays, are:
* The compress record  registers to provide storage for the value.
** This allows it to implement a circular buffer.
** Code that accesses the value field may have to issue two get requests.
* Device support for a transient recorder registers to provide storage for the array
** This allows device support to read data from hardware in segments
** Code that accesses the array may have to issue many get requests.
The fact that each field of a record instance is an object means that additional storage is required.
Database Access  will probably have something like the following:
    class Field {
        DbRecord instance;
        short          index;
    };
    ...
    class IntField extends Field {
        int data;
    }
    ...


    static class DbfConvertPrimitive {
This each field has the overhead of
    public:
* instance - a reference to DbRecord, i.e. record instance
          int16 get(Dbf from);
* index - a 16 bit integer
          int32 get(Dbf from);
* vtbl - a reference to the object implementation
          int64 get(Dbf from);
 
          float32 get(Dbf from);
Additional fields will be needed for things like monitors.
          float64 get(Dbf from);
These fields can start out null and only allocate storage as needed.
          get(Dbf from, String value);
 
          get(DbfOctet from, String value);
== Database Access ==
          get(DbfBoolean from, String value);
          put(Dbf to, int16 value);
          put(Dbf to, int32 value);
          put(Dbf to, int64 value);
          put(Dbf to, float32 value);
          put(Dbf to, float64 value);
          put(Dbf to,int32 String value);
          put(DbfOctet to, String value);
          put(DbfBoolean to, String value);


          get(DbfArray from, int16[] value);
    interface DbAccess {
          get(DbfArray from, int32[] value);
        void lock(RecordInstance instance);
          get(DbfArray from, int64[] value);
        void unlock(RecordInstance instance);
          get(DbfArray from, float32[] value);
        void process(RecordInstance instance, Callback callback);
          get(DbfArray from, float64[] value);
        void processCancel(RecordInstance instance, Callback callback);
          put(DbfArray to, int16[] value);
        void processContinue(RecordInstance instance, Callback callback);
          put(DbfArray to, int32[] value);
        void processRequestAccept(Dbf field, boolean yesNo);
          put(DbfArray to, int64[] value);
        void registerDbfSource(Dbf field, Dbf source);
          put(DbfArray to, float32[] value);
        LinkSupport getLinkSupport(Dbf field);
          put(DbfArray to, float64[] value);
        Support getSupport(Dbf field);
        RecordSupport getRecordSupport(RecordInstance instance);
        RecordSupport getRecordCommonSupport(RecordInstance instance);
     }
     }


NOTES:
The methods are:
* For Dbf <tt>to</tt> and <tt>from</tt> must be one of <tt>DbfInt16</tt>, ..., <tt>DbfFloat64</tt>
* <tt>lock</tt> - lock the record.
* The get to a string implements printf semantics
* <tt>unlock</tt> - unlock the record.
* The put from a string value just scanf semantics.
* <tt>process</tt> - Request processing. Record must be idle or active or request fails.
* The <tt>DbfOctet</tt> methods convert to/from a string of the form "0xXX"
** <tt>instance</tt> - The record to process
* The <tt>DbfBoolean</tt> get method supports all the choices specified in the DBD Record Instance Specification
** <tt>callback</tt> - A callback to call when processing completes or for failure.
* For DbfArray <tt>to</tt> and <tt>from</tt> must be an array of one of <tt>DbfInt16</tt>, ..., <tt>DbfFloat64</tt>
* <tt>processRequestAccept</tt> - Should a process request from channel access be accepted,
* <tt>registerDbfSource</tt> - Register code that will implement storage for the field.
* <tt>getLinkSupport</tt> - Find the link support for a link field
* <tt>getSupport</tt> - Get the device support for a device field
* <tt>getRecordSupport</tt> - Get the record support for the record
* <tt>getRecordCommonSupport</tt> - Get the RecordCommon support for the record.
 
<b>NOTES</b> This is just the beginning of defining the methods in DbAccess
 


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


== Code generated from Database Definition Files ==
= Code generated from Database Definition Files =
  </center>
  </center>


Line 127: Line 471:
The following shows the Java code generated from DBD files:
The following shows the Java code generated from DBD files:


=== structure definitions ===
== structure definitions ==


Two files are generated from <tt>struct(name)</tt> definitions.
Two files are generated from <tt>struct(name)</tt> definitions.
Line 163: Line 507:
     }
     }


A generated file <tt>DisplayLimitSupport.java</tt> contains:
A generated file <tt>DisplayLimitAccess.java</tt> contains:


     public class DisplayLimitData {
     public class DisplayLimitData {
Line 169: Line 513:
         public double high;
         public double high;
     }
     }
     public final class DisplayLimitSupport implements StructFactory{
     public final class DisplayLimitAccess implements StructFactory{
         public Struct create() { return new DisplayLimit; }
         public Struct create() { return new DisplayLimit; }
         public static final void get(DbfStruct from,DisplayLimitData data) {
         public static final void get(DbfStruct from,DisplayLimitData data) {
Line 185: Line 529:
     }
     }


     public final class DisplayLimitSupportRegister {
     public final class DisplayLimitAccessRegister {
         static public createAndRegister() {
         static public createAndRegister() {
             DisplayLimitSupport support = new DisplayLimitSupport;
             DisplayLimitAccess access = new DisplayLimitAccess;
             RegisterSupport.structure(support,"DisplayLimit");
             RegisterSupport.structure(access,"DisplayLimit");
         }
         }
     }
     }


<b>NOTE</b> The V4 replacement for registerRecordDeviceDriver must
<b>NOTES</b>
call DisplayLimitSupportRegister.createAndRegister.
* The V4 replacement for registerRecordDeviceDriver must call DisplayLimitAccessRegister.createAndRegister.
* For non primitive fields the generated field is the Dbf type for the field.


Similar files are generated for C++.
Similar files are generated for C++.


=== record definitions ===
== record definitions ==
If a record is defined as:
If a record is defined as:
     record(Example) extends IocRecord {
     record(Example) {
        field(common,struct(RecordCommon))
         ...
         ...
         field(fboolean,boolean)
         field(fboolean,boolean)
Line 219: Line 565:


     public class ExampleRecord implements Struct {
     public class ExampleRecord implements Struct {
        ... // all the fields from menuStructBase
         public DbfBoolean  fboolean;
         public DbfBoolean  fboolean;
         public DbfOctet    ctet;
         public DbfOctet    ctet;
Line 262: Line 609:
Similar code is generated for C++.
Similar code is generated for C++.


----
<center>
== Record Support ==
</center>
    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);
    }
<tt>ProcessState</tt> supports the following semantics:
* cancel - If the record is active terminate
* For all other states see the companion document "V4 Design: Record Processing"
The methods of <tt>RecordSupport</tt> are:
* <tt>destroy</tt> - Called when a record instance is deleted.
* <tt>initialize</tt> - Called twice (pass = 0,1) when a record is initialized.
* <tt>process</tt> - Called to process a record. See "V4 Design: Record Processing" for semantics.
* <tt>special</tt> - Called when a field declared special in a DBD file is modified. <tt>field</tt> is an array of Dbfs showing which field is being modified.
=== Discussion of special ===


Special is passed a Dbf array that identifies the field being modified.
For example <tt>ExampleRecord.dbd</tt> has a field:
    field(displayLimit,struct(displayLimit))
<tt>ExampleRecordSupport.java</tt> might implement special as follows:
    boolean special(boolean after, Dbf[] field)
    {
        switch(field[0].getIndex()) {
        ...
        case ExampleRecord.displayLimitIndex:
            if(field.length==1) {
                // displayLimits itself is being modified. Do something
            } else {
                // a field of displayLimits is being modified
                switch(field[1].getIndex) {
                    case DisplayLimit.lowIndex:
                          // low being modified. do something
                    case DisplayLimit.highIndex:
                          // high being modified. do something
                }
            }
        ...
        }
    }
----
----
<center>
<center>
   
= Support Code =
==  Link and Device Support ==
  </center>
  </center>


=== Link/Device Base ===
This section described interfaces for
    interface Support { // base for Link and Device support
strings, arrays, and conversions.  
        void report(int16 level);
They are intended for use by Database Access, Record Support, etc.
        void cancel();
        void destroy();
        void initialize();
        void connect();
        void disconnect();
    }
 
<tt>Support</tt> 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:
* <tt>report</tt> - report
* <tt>cancel</tt> - Cancel any outstanding I/O
* <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.
 
Normally record support does not need to call any of the Base 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 failure();
    }


    interface LinkSupport extends Support {
== C++ support for strings ==
        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);
    }


<tt>LinkSupport</tt> supports the following semantics:
For most strings all the database needs is a nonmutable string.
* 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.
Perhaps the only type of string connected with an IOC record
that changes frequently is a field in a record type
like stringin and stringout.


This what to do for strings?


=== Device Support ===


Other than that it must extend <tt>Support</tt>,
== array copy ==
nothing is defined for Device support in this document.
It will be modeled after the V3 asynDriver support.
----


<center>
ArrayCopy copies arrays an array performing conversions if necessary.
== Registration and Instance Creation ==
</center>


     interface StructFactory {
     interface ArrayCopy {
         DbfStruct create();
         void copy(DbfArray from,DbfArray to);
     }
     }


    interface RecordSupportFactory {
This only supports primitive types, i.e. DbfBoolean,...,DbfFloat64.
        RecordSupport create(RecordInstance instance);
It does NOT convert between
    }
* DbfBoolean and another type.
* DbfOctet and another type.
Thus for DbfBoolean and DbfOctet it is willing to copy
but not convert to/from other types.


    interface SupportFactory {
== Arithmetic Conversions ==
        Support create(Dbf field);
This is a static class that converts between primitie types and arrays of primitive types.
    }


     interface RegisterSupport {
     static class DbfConvertPrimitive {
        structure(StructFactory create, string name);
    public:
        record(StructFactory create, string name);
          int16 get(Dbf from);
        link(SupportFactory support,string name);
          int32 get(Dbf from);
        device(SupportFactory support,string name);
          int64 get(Dbf from);
        record(RecordSupportFactory support,string name);
          float32 get(Dbf from);
    }
          float64 get(Dbf from);
          get(Dbf from, String value);
          get(DbfOctet from, String value);
          get(DbfBoolean from, String value);
          put(Dbf to, int16 value);
          put(Dbf to, int32 value);
          put(Dbf to, int64 value);
          put(Dbf to, float32 value);
          put(Dbf to, float64 value);
          put(Dbf to,int32 String value);
          put(DbfOctet to, String value);
          put(DbfBoolean to, String value);


<tt>RegisterSupport</tt> is implemented by iocCore.
          get(DbfArray from, int16[] value);
Each <tt>struct</tt> DBD definition must register a StructFactory to
          get(DbfArray from, int32[] value);
create instances of the structure or record.
          get(DbfArray from, int64[] value);
 
          get(DbfArray from, float32[] value);
Each link, device, and record support must register a SupportFactory to create
          get(DbfArray from, float64[] value);
instances of the support to attach to the link, device, or record instance.
          put(DbfArray to, int16[] value);
During database initialization, iocCore calls the factory methods.
          put(DbfArray to, int32[] value);
 
          put(DbfArray to, int64[] value);
----
          put(DbfArray to, float32[] value);
<center>
          put(DbfArray to, float64[] value);
 
== Runtime Database Access ==
 
</center>
 
=== Overview ===
 
Database Access, with help from record, link, and device support
implements the dbdInterfaces.
Other than Database Access, fields of a record can only
be accessed via the interfaces described in dbdInterfaces.
This allows the database to handle
actions like posting database monitors without any help from
record, link, or device support.
 
Database access by default allocates the actual storage for each field
but allows support code to register itself to manage storage for field
instances.
Two examples, both involving arrays, are:
* The compress record  registers to provide storage for the value.
** This allows it to implement a circular buffer.
** Code that accesses the value field may have to issue two get requests.
* Device support for a transient recorder registers to provide storage for the array
** This allows device support to read data from hardware in segments
** Code that accesses the array may have to issue many get requests.
 
The fact that each field of a record instance is an object means that additional storage is required.
Database Access  will probably have something like the following:
 
    class Field {
        DbRecord instance;
        short          index;
    };
    ...
    class IntField extends Field {
        int data;
    }
    ...
 
This each field has the overhead of
* instance - a reference to DbRecord, i.e. record instance
* index - a 16 bit integer
* vtbl - a reference to the object implementation
 
Additional fields will be needed for things like monitors.
These fields can start out null and only allocate storage as needed.
 
=== Database Access ===
 
    interface DbAccess {
        void process(RecordInstance instance, Callback callback);
        void processRequestAccept(Dbf field, boolean yesNo);
        void registerDbfSource(Dbf field, Dbf source);
        LinkSupport getLinkSupport(Dbf field);
        Support getSupport(Dbf field);
        RecordSupport getRecordSupport(DbInstance instance);
        RecordSupport getIocRecordSupport(DbInstance instance);
     }
     }


The methods are:
NOTES:
* <tt>process</tt> - Request processing. Record must be idle or active or request fails.
* For Dbf <tt>to</tt> and <tt>from</tt> must be one of <tt>DbfInt16</tt>, ..., <tt>DbfFloat64</tt>
** <tt>instance</tt> - The record to process
* The get to a string implements printf semantics
** <tt>callback</tt> - A callback to call when processing completes or for failure.
* The put from a string value just scanf semantics.
* <tt>processRequestAccept</tt> - Should a process request from channel access be accepted,
* The <tt>DbfOctet</tt> methods convert to/from a string of the form "0xXX"
* <tt>registerDbfSource</tt> - Register code that will implement storage for the field.
* The <tt>DbfBoolean</tt> get method supports all the choices specified in the DBD Record Instance Specification
* <tt>getLinkSupport</tt> - Find the link support for a link field
* For DbfArray <tt>to</tt> and <tt>from</tt> must be an array of one of <tt>DbfInt16</tt>, ..., <tt>DbfFloat64</tt>
* <tt>getSupport</tt> - Get the device support for a device field
* <tt>getRecordSupport</tt> - Get the record support for the record
* <tt>getIocRecordSupport</tt> - Get the IocRecord support for the record.
 
<b>NOTES</b> This is just the beginning of defining the methods in DbAccess
 
 
----

Latest revision as of 19:47, 3 November 2005

EPICS: IOC Runtime Interfaces November 03 2005


Overview

This document describes definitions for IOC runtime, i.e. interfaces implemented or used by database access, record support, link support, and device support.

It assumes knowledge of the interfaces described in dbdInterfaces.

NOTES

  • IOC runtime needs lots more work
  • methods, arguments, and return values are likely to undergo many changes

The example code presented in this document assume the following database definitions:

    struct(DisplayLimit) {
        field(low,double)
        field(high,double)
    }
    record(Example) extends RecordCommon {
        field(common,struct(RecordCommon))
        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))
    }

Record Support

    enum ProcessState {
        processCancel,
        processStart,
        processContinue,
        processFinish,
    }
    enum ProcessReturn {
        processDone,
        processAbort,
        processActive
    }
    interface RecordSupport {
        void destroy();
        void initialize(int16 pass);
        ProcessReturn process(ProcessState state,Callback done);
        // 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
  • For all other states see the companion document "V4 Design: Record Processing"

The methods of RecordSupport are:

  • destroy - Called when a record instance is deleted.
  • initialize - Called twice (pass = 0,1) when a record is initialized.
    • pass 0 - Can do all local initialization but can not attach to anything external
    • pass 1 - Can attach to external things
  • process - Called to process a record. See "V4 Design: Record Processing" for semantics.
  • special - Called when a field declared special in a DBD file is modified. field is an array of Dbfs showing which field is being modified.

Discussion of special

Special is passed a Dbf array that identifies the field being modified. For example ExampleRecord.dbd has a field:

    field(displayLimit,struct(displayLimit))

ExampleRecordSupport.java might implement special as follows:

    boolean special(boolean after, Dbf[] field)
    {
        switch(field[0].getIndex()) {
        ...
        case ExampleRecord.displayLimitIndex:
            if(field.length==1) {
                // displayLimits itself is being modified. Do something
            } else {
               // a field of displayLimits is being modified
                switch(field[1].getIndex) {
                    case DisplayLimit.lowIndex:
                         // low being modified. do something
                    case DisplayLimit.highIndex:
                         // high being modified. do something
                }
            }
        ...
        }
    }

Support for link and struct fields

Support can be provided for link and struct fields. An arbitrary number of Support implementations can exist. An implementation can be either soft support or support that communicates with hardware.

Recall that the support DBD definition is:

   support(choiceName, supportStructName)

choiceName is the name that identifies the implementation. supportStructName is the name of a structure used by support for configuration.

The syntax for interface definitions is similar to Java syntax since it is more concise than C++. It can easily be translated to C++ syntax. For example the definition:

    interface LinkInt16 {
        LinkResult get(Callback callback,Int16 data);
        LinkResult put(Callback callback,int16 data);
        boolean getBounds(Int16 low, Int16 high);
    }

In C++ would be:

    class LinkInt16 {
    public:
        virtual LinkResult get(Callback &callback, int16_t &data) = 0;
        virtual LinkResult put(Callback &callback, int16_t data) = 0;
        virtual bool getBounds(int16_t &low, int16_t &high) = 0;
    };

NOTE: Int16 becomes int16_t &data and int16 data becomes int16_t data.

Support and SupportMonitor are interfaces that are common to the support for both link and struct fields. All support must register interface Support and if it supports monitors interface SupportMonitor.

NOTE: arguments and return types ignored for now

    interface Support {
        void report(int16 level);
        void destroy();
        void initialize(int16 pass);
        void connect();
        void disconnect();
        void cancel();
    }
    interface SupportMonitor {
        void addMonitor(Callback callback);
        void removeMonitor();
    }

Support is an interface that must be implemented by every support module. An instance of this is connected to each DbfLink or DbfStruct field that is configured to use support. The Support 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.

SupportMonitor is an interface that is implemented by support that supports monitors. An example is support for hardware interrupts. The methods are:

  • addMonitor - Add a monitor callback
  • removeMonitor - Remove monitor

Normally recordSupport does not need to call any of the Support or SupportMonitor methods since database access does this automatically. For example if a link 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 modified. ---

Link Support

Overview

This section describes the standard interfaces used by the record types supplied with epics base. These interfaces are appropriate for the soft support supplied with EPICS base and should also be sufficient for a wide variety of hardware support. In particular they sufficient for communication with asynDriver.

Soft support should try to implement the same interfaces implemented by the Channel/Database access support supplied with base. The support can define support structures for it's own use. Implementing these interfaces means that the support will work for the same set of record links as the Channel/Database access support.

Hardware support should, if possible, also implement the same interfaces implemented by the Channel/Database access support. Message based support may also have to implement the AsynOctet interface but this interface is only used by a few record types.

This section describes the interfaces implemented by all link support and then the standard interfaces used by the records supplied with base.


Definitions that apply to Process,Monitor,Input,and Output support

These are the definitions used by the Channel/Database access support supplied with base.

    enum LinkResult {
        linkNoop,           // Nothing was done, e.g. link is null link
        linkNoop,           // field was modified. No wait is necessary
        linkWait,           // waiting for completion 
    }
    interface Callback {
        void done();
        void failure();
    }

Octet Support

    interface LinkOctet {
        LinkResult get(Callback callback, Octet data);
        LinkResult put(Callback callback, octet data);
    }
    interface LinkArrayOctet {
        LinkResult get(Callback callback, octet[] data);
        LinkResult put(Callback callback, octet[] data);
    }


asynOctet Support

This still needs more work. It attempts to reproduce the functionality of V3 asynDriver.

    interface AsynOctet {
        LinkResult write(Callback callback,
          octet[] data, int32 numchars, Int32 nbytesTransfered);
        LinkResult writeRaw(Callback callback,
          octet[] data, int32 numchars, Int32 nbytesTransfered);
        LinkResult read(Callback callback,
          octet[] data, Int32 nbytesTransfered);
        LinkResult readRaw(Callback callback,
          octet[] data, Int32 nbytesTransfered);
        void flush();
        void setInputEos(octet[] eos);
        void getInputEos(octet[] eos);
        void setOutputEos(octet[] eos);
        void getOutputEos(octet[] eos);
    }

asynDigital Support

    enum interruptReason {
        interruptOnZeroToOne, interruptOnOneToZero, interruptOnBoth
    }
    struct asynDigitalInterrupt {
        octet[] mask;
        int32 addr;
        Callback callback
    }
    interface AsynDigital {
         LinkResult write(Callback callback,octet[] value, octet[] mask);
         LinkResult read(Callback callback,octet[] value, octet[] mask);
         void setInterrupt(octet[] mask, interruptReason reason);
         void clearInterrupt(octet[] mask);
         void getInterrupt(octet[] mask, interruptReason reason);
         void registerInterruptUser(interruptCallbackUInt32Digital callback,
             octet[] mask);
         void cancelInterruptUser();
    }

Boolean Support

    interface LinkBoolean {
        LinkResult get(Callback callback,Boolean data);
        LinkResult put(Callback callback,boolean data);
    }
    interface LinkArrayBoolean {
        LinkResult get(Callback callback,boolean[] data);
        LinkResult put(Callback callback,boolean[] data);
    }


The data source must be a boolean or a string that contains a valid boolean value.

Integer Support

Support is available. for int16, int32, and int64.

    interface LinkInt16 {
        LinkResult get(Callback callback,Int16 data);
        LinkResult put(Callback callback,int16 data);
        boolean getBounds(Int16 low, Int16 high);
    }
    interface LinkArrayInt16 {
        LinkResult get(Callback callback,int16[] data);
        LinkResult put(Callback callback,int16[] data);
    }
    interface LinkInt32 {
        LinkResult get(Callback callback,Int32 data);
        LinkResult put(Callback callback,int32 data);
        boolean getBounds(Int32 low, Int32 high);
    }
    interface LinkArrayInt32 {
        LinkResult get(Callback callback,int32[] data);
        LinkResult put(Callback callback,int32[] data);
    }
    interface LinkInt64 {
        LinkResult get(Callback callback,Int64 data);
        LinkResult put(Callback callback,int64 data);
        boolean getBounds(Int64 low, Int64 high);
    }
    interface LinkArrayInt64 {
        LinkResult get(Callback callback,int64[] data);
        LinkResult put(Callback callback,int64[] data);
    }


Float Support

Support is available. for float32 and float64.

    interface LinkFloat32 {
        LinkResult get(Callback callback,Float32 data);
        LinkResult put(Callback callback,float32 data);
    }
    interface LinkArrayFloat32 {
        LinkResult get(Callback callback,float32[] data);
        LinkResult put(Callback callback,float32[] data);
    }
    interface LinkFloat64 {
        LinkResult get(Callback callback,Float64 data);
        LinkResult put(Callback callback,float64 data);
    }
    interface LinkArrayFloat64 {
        LinkResult get(Callback callback,float64[] data);
        LinkResult put(Callback callback,float64[] data);
    }

String Support

    interface LinkString {
        LinkResult get(Callback callback,string data);
        LinkResult put(Callback callback,string data);
    }
    interface LinkArrayString {
        LinkResult get(Callback callback,string[] data);
        LinkResult put(Callback callback,string[] data);
    }


The data source must be a string. ---

Struct Support

support for RecordCommon

This implements an interface, not yet defined, that is similar to the interface record support implements.

support for LinearConvert

This implements the interface

    interface SupportLinearConvert{
         void convert(int32 raw,Float64 value) // from raw to eng
         void convert(float64 value,Int32 raw) // from eng to raw
    }

In addition it provides a factory

    interface FactoryLinearConvert{
        SupportLinearConvert create(string choiceName,
            DbfStruct linearConvert,DbfLink inlink)
    }

support for simulation

This needs to be defined.

support for breakpoint tables

This needs to be defined.


Runtime Database Access

Overview

Database Access, with help from record, link, and device support implements the dbdInterfaces. Other than Database Access, fields of a record can only be accessed via the interfaces described in dbdInterfaces. This allows the database to handle actions like posting database monitors without any help from record, link, or device support.

Database access by default allocates the actual storage for each field but allows support code to register itself to manage storage for field instances. Two examples, both involving arrays, are:

  • The compress record registers to provide storage for the value.
    • This allows it to implement a circular buffer.
    • Code that accesses the value field may have to issue two get requests.
  • Device support for a transient recorder registers to provide storage for the array
    • This allows device support to read data from hardware in segments
    • Code that accesses the array may have to issue many get requests.

The fact that each field of a record instance is an object means that additional storage is required. Database Access will probably have something like the following:

   class Field {
       DbRecord instance;
       short          index;
   };
   ...
   class IntField extends Field {
       int data;
   }
   ...

This each field has the overhead of

  • instance - a reference to DbRecord, i.e. record instance
  • index - a 16 bit integer
  • vtbl - a reference to the object implementation

Additional fields will be needed for things like monitors. These fields can start out null and only allocate storage as needed.

Database Access

    interface DbAccess {
        void lock(RecordInstance instance);
        void unlock(RecordInstance instance);
        void process(RecordInstance instance, Callback callback);
        void processCancel(RecordInstance instance, Callback callback);
        void processContinue(RecordInstance instance, Callback callback);
        void processRequestAccept(Dbf field, boolean yesNo);
        void registerDbfSource(Dbf field, Dbf source);
        LinkSupport getLinkSupport(Dbf field);
        Support getSupport(Dbf field);
        RecordSupport getRecordSupport(RecordInstance instance);
        RecordSupport getRecordCommonSupport(RecordInstance instance);
    }

The methods are:

  • lock - lock the record.
  • unlock - unlock the record.
  • process - Request processing. Record must be idle or active or request fails.
    • instance - The record to process
    • callback - A callback to call when processing completes or for failure.
  • processRequestAccept - Should a process request from channel access be accepted,
  • registerDbfSource - Register code that will implement storage for the field.
  • getLinkSupport - Find the link support for a link field
  • getSupport - Get the device support for a device field
  • getRecordSupport - Get the record support for the record
  • getRecordCommonSupport - Get the RecordCommon support for the record.

NOTES This is just the beginning of defining the methods in DbAccess



Code generated from Database Definition Files


The following shows the Java code generated from DBD files:

structure definitions

Two files are generated from struct(name) definitions.

  1. name.java
  2. nameSupport.java

name.java implements:

    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:

    public class DisplayLimit implements Struct{
        public DbfFloat64 low;
        public DbfFloat64 high;
        public static final short lowIndex = 1;
        public static final short highIndex = 2;
        public static final short 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 DisplayLimitAccess.java contains:

    public class DisplayLimitData {
        public double low;
        public double high;
    }
    public final class DisplayLimitAccess 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 DisplayLimitAccessRegister {
        static public createAndRegister() {
            DisplayLimitAccess access = new DisplayLimitAccess;
            RegisterSupport.structure(access,"DisplayLimit");
        }
    }

NOTES

  • The V4 replacement for registerRecordDeviceDriver must call DisplayLimitAccessRegister.createAndRegister.
  • For non primitive fields the generated field is the Dbf type for the field.

Similar files are generated for C++.

record definitions

If a record is defined as:

    record(Example) {
        field(common,struct(RecordCommon))
        ...
        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 {
        ... // all the fields from menuStructBase
        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");
        }
    }

NOTE The V4 replacement for registerRecordDeviceDriver must call ExampleRecordFactoryRegister.createAndRegister


Similar code is generated for C++.



Support Code

This section described interfaces for strings, arrays, and conversions. They are intended for use by Database Access, Record Support, etc.

C++ support for strings

For most strings all the database needs is a nonmutable string.

Perhaps the only type of string connected with an IOC record that changes frequently is a field in a record type like stringin and stringout.

This what to do for strings?


array copy

ArrayCopy copies arrays an array performing conversions if necessary.

    interface ArrayCopy {
        void copy(DbfArray from,DbfArray to);
    }

This only supports primitive types, i.e. DbfBoolean,...,DbfFloat64. It does NOT convert between

  • DbfBoolean and another type.
  • DbfOctet and another type.

Thus for DbfBoolean and DbfOctet it is willing to copy but not convert to/from other types.

Arithmetic Conversions

This is a static class that converts between primitie types and arrays of primitive types.

    static class DbfConvertPrimitive {
    public:
         int16 get(Dbf from);
         int32 get(Dbf from);
         int64 get(Dbf from);
         float32 get(Dbf from);
         float64 get(Dbf from);
         get(Dbf from, String value);
         get(DbfOctet from, String value);
         get(DbfBoolean from, String value);
         put(Dbf to, int16 value);
         put(Dbf to, int32 value);
         put(Dbf to, int64 value);
         put(Dbf to, float32 value);
         put(Dbf to, float64 value);
         put(Dbf to,int32 String value);
         put(DbfOctet to, String value);
         put(DbfBoolean to, String value);
         get(DbfArray from, int16[] value);
         get(DbfArray from, int32[] value);
         get(DbfArray from, int64[] value);
         get(DbfArray from, float32[] value);
         get(DbfArray from, float64[] value);
         put(DbfArray to, int16[] value);
         put(DbfArray to, int32[] value);
         put(DbfArray to, int64[] value);
         put(DbfArray to, float32[] value);
         put(DbfArray to, float64[] value);
    }

NOTES:

  • For Dbf to and from must be one of DbfInt16, ..., DbfFloat64
  • The get to a string implements printf semantics
  • The put from a string value just scanf semantics.
  • The DbfOctet methods convert to/from a string of the form "0xXX"
  • The DbfBoolean get method supports all the choices specified in the DBD Record Instance Specification
  • For DbfArray to and from must be an array of one of DbfInt16, ..., DbfFloat64