Difference between revisions of "V4 Design: Runtime Interfaces"

From EPICSWIKI
Line 1: Line 1:
EPICS: IOC Runtime Interfaces September 27 2005
EPICS: IOC Runtime Interfaces September 28 2005


----
----
Line 312: Line 312:
=  Link Support =
=  Link Support =
  </center>
  </center>
An arbitrary number of Link Support implementations can exist.
An implementation can be either soft support or support that communicates with
hardware.
EPICS base supplies support that accesses data via channel access, i.e.
access to records in the same or other IOCS.
Recall that the link DBD definition is:
    link(dir, choiceName, interfaceName, dataStructName)
<tt>choiceName</tt> is the name that identifies the implementation.
<tt>interfaceName</tt> is the name of the interface that record support
uses and the support implements.
<tt>dataStructName</tt> is the name of a structure used by support for
configuration.
This section describes that standard <tt>interfaceNames</tt> that
are 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 it is sufficient for communication with asynDriver.


== Link  ==
== Link  ==
Line 321: Line 343:
         void connect();
         void connect();
         void disconnect();
         void disconnect();
    }
    interface MonitorLink {
        void addMonitor(Callback callback);
        void removeMonitor();
     }
     }


Line 326: Line 353:
support.
support.
An instance of this is connected to each DbfLink field.
An instance of this is connected to each DbfLink field.
The methods are:
<tt>MonitorLink</tt> is an interface that is implemented by link support that
supports monitors. Example are hardware interrupts.
The <tt>Link</tt> methods are:
* <tt>report</tt> - report  
* <tt>report</tt> - report  
* <tt>cancel</tt> - Cancel any outstanding I/O
* <tt>cancel</tt> - Cancel any outstanding I/O
Line 334: Line 363:
* <tt>disconnect</tt> - disconnect.
* <tt>disconnect</tt> - disconnect.


Normally record support does not need to call any of the Link methods
The <tt>MonitorLink</tt> methods are:
since database access does this automatically. For example if a link
* <tt>addMonitor</tt> - Add a monitor callback
field is modified via a channel access put, database access will call
* <tt>removeMonitor</tt> - Remove monitor
 
Normally record support does not need to call any of the Link  
or MonitorLink 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
destroy before modifying the link and initialize and connect after the
link is modidied.
link is modified.


== Definitions that apply to Process,Monitor,Input,and Output support ==
== Definitions that apply to Process,Monitor,Input,and Output support ==
Line 355: Line 389:
== Process Link Support ==
== Process Link Support ==
     interface ProcessLinkSupport<type> {
     interface ProcessLinkSupport<type> {
         void process();
         LinkWaitResult process(Callback callback);
         LinkWaitResult processWait(double timeout,Callback callback);
    }
 
== Octet Support ==
There is no support for octet but there is support for an array of octets.
 
 
    interface LinkArrayOctet {
        LinkWaitResult getWait(octet[] data,Callback callback);
         LinkWaitResult putWait(octet[] data,Callback callback);
     }
     }


== Monitor Link Support ==
The data source must be an array of octets or the connect request will fail.
There is support for the following types: octet, boolean, int16, int32,
int64, float32, float64, and arrays of each of these types.


     interface MonitorLink<type> {
 
         void get(<type> data);
== asynOctet Support ==
 
This still needs more work. It attempts to reproduce the functionality
of V3 asynDriver.
 
     interface AsynOctet {
        LinkWaitResult write(Callback callback,
          octet[] data, int32 numchars, Int32 nbytesTransfered);
        LinkWaitResult writeRaw(Callback callback,
          octet[] data, int32 numchars, Int32 nbytesTransfered);
        LinkWaitResult read(Callback callback,
          octet[] data, Int32 nbytesTransfered);
        LinkWaitResult 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);
     }
     }


== Input Link Support ==
    enum interruptReason {
There is support for the following types: octet, boolean, int16, int32,
        interruptOnZeroToOne, interruptOnOneToZero, interruptOnBoth
int64, float32, float64, and arrays of each of these types.
    }
    struct asynDigitalInterrupt {
        octet[] mask;
        int32 addr;
        Callback callback
    }


     interface InputLinkSupport<type> {
     interface AsynDigital {
        void get(<type> data);
          LinkWaitResult write(octet[] value, octet[] mask);
        LinkWaitResult getWait(<type> data, double timeout,Callback callback);
          LinkWaitResult read(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();
     }
     }
== Output Link Support ==
There is support for the following types: octet, boolean, int16, int32,
int64, float32, float64, and arrays of each of these types.


     interface OutputLinkSupport<type> {
== Boolean Support ==
         void put(Dbf field);
     interface LinkBoolean {
         LinkWaitResult putWait(Dbf field, double timeout,Callback callback);
         LinkWaitResult getWait(Boolean data,Callback callback);
         LinkWaitResult putWait(boolean data,Callback callback);
     }
     }
    interface LinkArrayBoolean {
        LinkWaitResult getWait(boolean[] data,Callback callback);
        LinkWaitResult putWait(boolean[] data,Callback callback);
    }
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.
This section uses the Java generic syntax, e.g. <type>. In this
section <type> must be int16, int32, or int64.
    interface Link<type> {
        LinkWaitResult getWait(<type> data,Callback callback);
        LinkWaitResult putWait(<type> data,Callback callback);
        void getBounds(<type> low, <type> high);
    }
    interface LinkArray<type> {
        LinkWaitResult getWait(<type>[] data,Callback callback);
        LinkWaitResult putWait(<type>[] data,Callback callback);
    }
    enum interruptReason {
        interruptOnZeroToOne, interruptOnOneToZero, interruptOnBoth
    }
       
       
== Float Support ==
Support is available. for float32 and float64.
This section uses the Java generic syntax, e.g. <type>. In this
section <type> must be float32 or float64.
    interface Link<type> {
        LinkWaitResult getWait(<type> data,Callback callback);
        LinkWaitResult putWait(<type> data,Callback callback);
    }
    interface LinkArray<type> {
        LinkWaitResult getWait(<type>[] data,Callback callback);
        LinkWaitResult putWait(<type>[] data,Callback callback);
    }
== String Support ==
    interface LinkString {
        LinkWaitResult getWait(string data,Callback callback);
        LinkWaitResult putWait(string data,Callback callback);
    }
    interface LinkArrayString {
        LinkWaitResult getWait(string[] data,Callback callback);
        LinkWaitResult putWait(string[] data,Callback callback);
    }
The data source must be a string.


----
----

Revision as of 19:32, 28 September 2005

EPICS: IOC Runtime Interfaces September 28 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.

NOTE IOC runtime needs lots more work


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. For these it wants to be able to pass a reference to it to arbitrary code. It needs this for things like record type names, field names, record instance names, menu choice names, etc. etc. Some of these may change but very infrequently. The string always contains UTF-8 encoded strings, which I believe can be printed with a "%s" format if the string is made available as a "const char *"

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. In this case the record support can do what ever it wants to handle the string. It can even just use C style strings!!!

Thus all the IOC database needs is a:

1) nonmutable reference counted continguous string. When the reference count goes to 0 the storage can be reclaimed 2) The ability to copy the value from a nonmutable string to a mutable string.


In Java String and StringBuffer are all that is needed.

In C++ all that is needed is

1) A class that holds UFT-8 encoded non-mutable strings and make the contents available as a "const char *" and it's length. The class keeps a reference count and frees the storage when done. 2) The ability to copy the nonmutable string to a "char *" array allocated by the requester.

That is all.

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

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");
        }
    }

NOTE The V4 replacement for registerRecordDeviceDriver must call DisplayLimitAccessRegister.createAndRegister.

Similar files are generated for C++.

record definitions

If a record is defined as:

    record(Example) extends 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 {
        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++.


Record Support

    enum ProcessState {
        processCancel,
        processStart,
        processContinue,
    }
    enum ProcessReturn {
        processDone,
        processAbort,
        processActive
    }
    interface RecordSupport {
        void destroy();
        void initialize(int32 pass);
        ProcessState 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.
  • 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
                }
            }
        ...
        }
    }

Link Support

An arbitrary number of Link Support implementations can exist. An implementation can be either soft support or support that communicates with hardware. EPICS base supplies support that accesses data via channel access, i.e. access to records in the same or other IOCS.

Recall that the link DBD definition is:

   link(dir, choiceName, interfaceName, dataStructName)

choiceName is the name that identifies the implementation. interfaceName is the name of the interface that record support uses and the support implements. dataStructName is the name of a structure used by support for configuration.

This section describes that standard interfaceNames that are 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 it is sufficient for communication with asynDriver.

Link

    interface Link {
        void report(int16 level);
        void cancel();
        void destroy();
        void initialize();
        void connect();
        void disconnect();
    }
    interface MonitorLink {
        void addMonitor(Callback callback);
        void removeMonitor();
    }

Link is an interface that must be implemented by every link support. An instance of this is connected to each DbfLink field. MonitorLink is an interface that is implemented by link support that supports monitors. Example are hardware interrupts. The Link 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.

The MonitorLink methods are:

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

Normally record support does not need to call any of the Link or MonitorLink 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.

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

    enum LinkWaitResult {
        linkNoop,           // Nothing was done, e.g. link is null link
        linkDone,           // field was modified. No wait is necessary
        linkWait,           // waiting. can do additional procsssing
        linkWaitBlock,      // waiting. dont do additional processing until
    }
    interface Callback {
        void done();
        void failure();
    }

Process Link Support

    interface ProcessLinkSupport<type> {
        LinkWaitResult process(Callback callback);
    }

Octet Support

There is no support for octet but there is support for an array of octets.


    interface LinkArrayOctet {
        LinkWaitResult getWait(octet[] data,Callback callback);
        LinkWaitResult putWait(octet[] data,Callback callback);
    }

The data source must be an array of octets or the connect request will fail.


asynOctet Support

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

    interface AsynOctet {
        LinkWaitResult write(Callback callback,
          octet[] data, int32 numchars, Int32 nbytesTransfered);
        LinkWaitResult writeRaw(Callback callback,
          octet[] data, int32 numchars, Int32 nbytesTransfered);
        LinkWaitResult read(Callback callback,
          octet[] data, Int32 nbytesTransfered);
        LinkWaitResult 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);
    }
    enum interruptReason {
        interruptOnZeroToOne, interruptOnOneToZero, interruptOnBoth
    }
    struct asynDigitalInterrupt {
        octet[] mask;
        int32 addr;
        Callback callback
    }
    interface AsynDigital {
         LinkWaitResult write(octet[] value, octet[] mask);
         LinkWaitResult read(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 {
        LinkWaitResult getWait(Boolean data,Callback callback);
        LinkWaitResult putWait(boolean data,Callback callback);
    }
    interface LinkArrayBoolean {
        LinkWaitResult getWait(boolean[] data,Callback callback);
        LinkWaitResult putWait(boolean[] data,Callback callback);
    }


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. This section uses the Java generic syntax, e.g. <type>. In this section <type> must be int16, int32, or int64.

    interface Link<type> {
        LinkWaitResult getWait(<type> data,Callback callback);
        LinkWaitResult putWait(<type> data,Callback callback);
        void getBounds(<type> low, <type> high);
    }
    interface LinkArray<type> {
        LinkWaitResult getWait(<type>[] data,Callback callback);
        LinkWaitResult putWait(<type>[] data,Callback callback);
    }
    enum interruptReason {
        interruptOnZeroToOne, interruptOnOneToZero, interruptOnBoth
    } 
        
        

Float Support

Support is available. for float32 and float64. This section uses the Java generic syntax, e.g. <type>. In this section <type> must be float32 or float64.

    interface Link<type> {
        LinkWaitResult getWait(<type> data,Callback callback);
        LinkWaitResult putWait(<type> data,Callback callback);
    }
    interface LinkArray<type> {
        LinkWaitResult getWait(<type>[] data,Callback callback);
        LinkWaitResult putWait(<type>[] data,Callback callback);
    }

String Support

    interface LinkString {
        LinkWaitResult getWait(string data,Callback callback);
        LinkWaitResult putWait(string data,Callback callback);
    }
    interface LinkArrayString {
        LinkWaitResult getWait(string[] data,Callback callback);
        LinkWaitResult putWait(string[] data,Callback callback);
    }


The data source must be a string.


Registration and Instance Creation

    interface StructFactory {
        DbfStruct create();
    }
    interface RecordSupportFactory {
        RecordSupport create(RecordInstance instance);
    }
    interface SupportFactory {
        Support create(Dbf field);
    }
    interface 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.


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 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(DbInstance instance);
        RecordSupport getRecordCommonSupport(DbInstance instance);
    }

The methods are:

  • 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