Difference between revisions of "V4 Design: RecordCommon Support"

From EPICSWIKI
 
(One intermediate revision by the same user not shown)
Line 1: Line 1:
EPICS V4: RecordCommonSupport - September 22 2005  
EPICS V4: RecordCommonSupport - October 04 2005  
----
----
= Overview =
= Overview =
<b>THIS WIKI NEEDS LOTS OF WORK</b>


This document describes the implementation of the record support
This document describes the implementation of the record support
Line 24: Line 22:
# V4 Design: Runtime interfaces
# V4 Design: Runtime interfaces
# V4 Design: RecordCommon
# V4 Design: RecordCommon
Unless the reader is familiar with these most of this document will
be very difficult to understand.


Java is used as the example code for two reasons:
Java is used as the example code for two reasons:
Line 33: Line 28:


An implementaion of RecordCommonSupport is presented for two reasons:
An implementaion of RecordCommonSupport is presented for two reasons:
# It demonstrates how to implement code that uses or implements the interface described in "V4 Design: dbdInterfaces" and "V4 Design: Runtime interfaces".
# It demonstrates how to implement code that uses or implements the interfaces described in "V4 Design: dbdInterfaces" and "V4 Design: Runtime interfaces".
# It helps refine the interfaces described in them
# It helps refine the interfaces described in them


Line 43: Line 38:
See the description of <tt>dbCommon.dbd</tt> in "V4 Design: RecordCommon"
See the description of <tt>dbCommon.dbd</tt> in "V4 Design: RecordCommon"


Link support must register support each of the <tt>link</tt> definitions.
Link support must register support for each of the <tt>link</tt> definitions.
          
          
From <tt>dbCommon.dbd</tt> the following class implementations are generated:
From <tt>dbCommon.dbd</tt> the following class implementations are generated:
* For the link structures:
* For the link structures:
** <tt>MonitorLink</tt> and <tt>MonitorLinkData</tt>, and <tt>MonitorLinkSupport</tt>
** <tt>MonitorLink</tt>, <tt>MonitorLinkData</tt>, and <tt>MonitorLinkSupport</tt>
** <tt>InputLink</tt>, <tt>InputLinkData</tt>, and <tt>InputLinkSupport</tt>
** <tt>InputLink</tt>, <tt>InputLinkData</tt>, and <tt>InputLinkSupport</tt>
** <tt>OutputLink</tt>, <tt>OutputLinkData</tt>, and <tt>OutputLinkSupport</tt>
** <tt>OutputLink</tt>, <tt>OutputLinkData</tt>, and <tt>OutputLinkSupport</tt>
Line 90: Line 85:
: This is an array of process links, which replace the V3 <tt>FLNK</tt>
: This is an array of process links, which replace the V3 <tt>FLNK</tt>


The following subsection contains the complete code for RecordCommonSupport.java.
The following subsection describes the code for RecordCommonSupport.java.
The remaining subsections discuss each part of the code.
The remaining subsections describes the code for additional classes
that are part of the implementation.


== IocCommon.java ==
== RecordCommonSupport.java ==


This defines
interface Common {
    interface Common {
    boolean setSeverity(AlarmSeverity severity, string status);
        bool setSeverity(AlarmSeverity severity, string status);
}
public class RecordCommonSupport implements RecordSupport, Common
{
    private RecordInstance instance;
    private Input          input;
    private Output        output;
    // The following are for implementing Common.setSeverity
    private short severity;
    private String status;
    private DbfMenu alarmSeverity;
    private DbfString alarmStatus;
    private boolean modifyingAlarm;
    // Constructor
    public RecordCommonSupport(RecordInstance instance) {
        this.instance = instance;
     }
     }
 
 
    // Beginning of RecordSupport
==  RecordCommonSupport.java ==
     public destroy() {} // destroy not needed for Java
     enum Mode {modeNone,modeInput,modeOutput};
     public initialize(int pass) {
   
        if(pass==0) return;
     public class RecordCommonSupport implements RecordSupport,Callback,Common
         input = new Input(instance);
    {
         output = new Output(instance);
         private RecordInstance instance;
         alarmSeverity = instance.getField("alarmSeverity");
         private Mode          mode;
         alarmStatus = instance.getField("alarmStatus");
         private Input          input;
         severity = alarmSeverity.get();
         private Output        output;
         alarmStatus.get(status);
         private Alarm          alarm;
     }
         private bool          requestProcess;
      
    public ProcessReturn process(processState state,Callback callback) {
        // Constructor
         if(state==processCancel) {
         public RecordCommonSupport(RecordInstance instance) {
             input.process(state,callback);
             this.instance = instance;
            output.process(state,callback);
    return processDone;
         }
         }
   
         if(state==processStart) {
        // Beginning of RecordSupport
             ProcessReturn result = input.process(state,callback);
          public destroy() {} // destroy not needed for Java
             if(result!=processDone) return result;
        public destroy() {} // destroy not needed for Java
         public initialize(int pass) {
            if(pass==0) return;
             input.init(instance);
             output.init(instance);
            alarm.init(instance);
         }
         }
 
         if(state==processFinish) {
         public processState process(processState state) {
             ProcessReturn result = output.process(state,callback);
            switch(state) {
            if(result!=processDone) return result;
            case processCancel:
        //MUCH MORE NEEDS TO BE DONE
// MUST CANCAL ANY OUTSTANDING I/O
        mode = modeNone; return processDone;
            case processStart: {
                mode = modeInput;
                requestProcess = false;
                LinkWaitResult result = input.disableSupport.getWait(
                    input.disableInput,0.0,this);
                if(result==linkNoop || result==linkDone) return processDone;
                requestProcess = true;
            }
            // fall through to processInputActive
            case processInputActive: {
                if(requestProcess) return processInputActive;
                if(input.disable.disableValue == disable.disableInput)
                        return processDone;
                return processActive;
             }
            case processDone: {
                mode = modeOutput;
                output.nextLink = 0;
                output.numberLinksActive = 0;
                   
            }
            // fall through to processOutputActive
            case processOutputActive:
                LinkWaitResult result = linkWaitParallel;
                requestProcess = false;
                while(output.nextLink< output.link.length) {
                    if(result==linkWaitSequential) break;
                    result = output.link[nextLink].support.processWait(0.0,this);
                    if(result==linkWaitSequential || result==linkWaitParallel)
                        output.numberLinksActive++
                    output.nextLink++;
                }
                if(output.numberLinksActive==0) {
                      mode = modeNone;
                      return processDone;
                }
                requestProcess = true;
                return processOutputActive;
            default: throw java.lang.IllegalStateException;
            }
         }
         }
 
        return output.process(state,callback)
        public bool special(bool after,Dbf[] field) {
    }
            int nlevels = field.length;
            assert(nlevels>0);
    public boolean special(boolean after,Dbf[] field) {
            if(index==alarmStatusIndex || index==alarmSeverityIndex) {
        int nlevels = field.length;
                  if(!alarm.modifyingAlarm) return false;
        assert(nlevels>0);
                  return true;
        if(index==alarmStatusIndex || index==alarmSeverityIndex) {
            }
              if(!alarm.modifyingAlarm) return false;
            if(!after) return;
              return true;
            switch(index) {
                case disableIndex: {
                    if(nlevels==1) {
                        input.init(instance);
                    } else {
                        input.special(instance,Dbf[1]);
                    }
                    break;
                }
                case processLinkIndex : output.init(instance); break;
                default: printf("Why is special being called?\n");
            }
         }
         }
 
         if(!after) return;
         // Start of Callback methods
        switch(index) {
        public void done() {
             case disableIndex: {
            lock(instance);
                 if(nlevels==1) {
            switch(mode) {
                     input = new Input(instance);
             case modeNone:
                } else {
                  unlock(instance)
                     input.special(instance,Dbf[1]);
                  throw java.lang.IllegalStateException;
            case modeInput:
                 if(requestProcess) {
                     requestProcess = false;
                     DbAccess.process(instance);
                 }
                 }
                 break;
                 break;
            case modeOutput:
                output.numberLinksActive--;
                if(output.numberLinksActive==0 && requestProcess) {
                    requestProcess = false;
                    DbAccess.process(instance);
                }
                if(output.numberLinksActive==0 && requestProcess)
                break;
            }
            unlock(instance)
        }
        public void failure() { done(); }
        // Start of Common methods
        bool setSeverity(AlarmSeverity severity, string status) {
            Alarm alarm = this.alarm;
            if(severity<= alarm.severity) return false;
            alarm.severity = severity;
            alarm.status = status;
            alarm.modifyingAlarm = true;
            alarm.alarmSeverity.putIndex(severity);
            alarm.alarmStatus.put(status);
            alarm.modifyingAlarm = false;
        }
    }
   
    // Definitions for modeInput
    class Input {
        DisableData disable;
        LinkSupport disableSupport;
        DbfInt16    disableInput;
        DbfInt16    disableValue;
        DbfInt16    alarmSeverity;
   
        void init(RecordInstance instance) {
            RecordAccess recordAccess = instance.getRecordCommonAccess();
            DbfLink link = recordAccess.getField("disable.disableLink");
            disableSupport = link.getSupport();
            DisplayLimitAccess.get(
                recordAccess.getField("disable"),disable);
            recordAccess.getField("disable.disableInput",disableInput);
        }
        void special(RecordInstance instance,Dbf field) {
            int16 index = Dbf.getIndex();
            switch(index) {
                case disableValueIndex:
                    disableValue = ((DbfInt16)Dbf).get(); break;
                case disableInputIndex:
                    disableInput = ((DbfInt16)Dbf).get(); break;
                case disableAlarmSeverityIndex:
                    alarmSeverity = ((dbfMenu)Dbf).getIndex(); break;
                case disableLinkIndex:
                    init(instance); break;
                default:
                    throw java.lang.IllegalStateException;
            }
        }
    }
    // Definitions for modeOutput
    class OutputLink {
        Link        link;
        LinkSupport support;
        void init(RecordInstance instance,String linkFieldName) {
            RecordAccess recordAccess = instance.getRecordCommonAccess();
            DbfLink link = recordAccess.getField(linkFieldName);
            support = link.getSupport();
        }
    };
   
    class Output {
        int          nextLink;
        int          numberLinksActive;
        OutputLink[] link;
        void init(RecordInstance instance) {
            RecordAccess recordAccess = instance.getRecordCommonAccess();
            DbfArray array = recordAccess.getField("processLink");
            int n = array.getNelements();
            link = new OutputLink[n];
            for(i=0; i< n; i++) {
                String field = "processLink[" + "i" + "]";
                link[i].init(instance,field);
             }
             }
            case processLinkIndex : output = new Output(instance); break;
            default: printf("Why is special being called?\n");
         }
         }
     }
     }
 
     // Definitions for alarm handling
     // Start of Common methods
     class Alarm {
     boolean setSeverity(AlarmSeverity severity, string status) {
         short severity;
         if(severity<= this.severity) return false;
         String status;
         this.severity = severity;
         DbfMenu alarmSeverity;
         this.status = status;
         DbfString alarmStatus;
         this.modifyingAlarm = true;
        bool modifyingAlarm;
         this.alarmSeverity.putIndex(severity);
         void init(RecordInstance instance) {
        this.alarmStatus.put(status);
            RecordAccess recordAccess = instance.getRecordCommonAccess();
         this.modifyingAlarm = false;
            alarmSeverity = recordAccess.getField("alarmSeverity");
            alarmStatus = recordAccess.getField("alarmStatus");
         }
     }
     }
 
}
== Beginning of class definition ==
    enum Mode {modeNone,modeInput,modeOutput};
   
    public class RecordCommonSupport implements RecordSupport,Callback, Common
    {
        private RecordInstance instance;
        private Mode          mode;
        private Input          input;
        private Output        output;
        private Alarm          alarm;
        private bool          requestProcess;
   
        // Constructor
        public RecordCommonSupport(RecordInstance instance) {
            this.instance = instance;
        }
 


RecordCommonSupport implements the following interfaces:
RecordCommonSupport implements the following interfaces:
; <tt>RecordSupport</tt>
; <tt>RecordSupport</tt>
: Record support for the fields in RecordCommon.
: Record support for the fields in RecordCommon.
; <tt>Callback</tt>
: The callbacks called by link support
; <tt>Common</tt>
; <tt>Common</tt>
: For now this just has the method <tt>setSeverity</tt>
: For now this just has the method <tt>setSeverity</tt>
Line 339: Line 183:
It has the following private fields:
It has the following private fields:
* <tt>instance</tt> -  The record instance to which it is attached. <tt>RecordCommonSupport</tt> has a single constructor which is passed the <tt>RecordInstance</tt> to which it is attached.
* <tt>instance</tt> -  The record instance to which it is attached. <tt>RecordCommonSupport</tt> has a single constructor which is passed the <tt>RecordInstance</tt> to which it is attached.
* <tt>mode</tt> - This keeps the state for record processing:
** <tt>modeNone</tt> - The record is not being processed.
** <tt>modeInput</tt> - The disable link is active but not complete.
** <tt>modeOutput</tt> - The processLinks are active but not complete.
* <tt>input</tt> - Input is a private class that provides access to the fields of disable.
* <tt>input</tt> - Input is a private class that provides access to the fields of disable.
* <tt>output</tt> - Output is a private class that provides access to processLink.
* <tt>output</tt> - Output is a private class that provides access to processLink.
* <tt>alarm</tt> - Alarm is a private class that provides access to fields alarmSeverity and alarmStatus.
* <tt>severity</tt>, ..., <tt>modifyingAlarm</tt> - private fields for implementing <tt>Common.setSeverity</tt>
* <tt>requestProcess</tt> - This is used by callback to decide if a request should be made to process the record.


Notes about <tt>Input</tt>, <tt>Output</tt>, and <tt>Alarm</tt>.
Notes about <tt>Input</tt> and <tt>Output</tt>.
* Each of these is a private class to RecordCommonSupport.
* Each of these is a private class to RecordCommonSupport.
* Each makes fields available to RecordCommonSupport.
* Each has "introspection" code that gets the values of the private fields from RecordCommon fields during record initialization of if the an RecordCommon fields is changed via a call to <tt>Dbf.put</tt>.
*  The implementations of each of these private classes is given after the implementaion of RecordCommonSupport.
*  The implementations of each of these private classes is given after the implementaion of RecordCommonSupport.


=== RecordSupport Methods ===
==== destroy ====
        public destroy() {} // destroy not needed for Java


== RecordSupport Methods ==
Since Java does garbage collection destroy is not needed.
=== initialize ===
Note that iocCore will call all the link support destroy methods.
==== initialize ====
      
      
        public initialize(int pass) {
    public initialize(int pass) {
            if(pass==0) return;
        if(pass==0) return;
            input.init(instance);
        input = new Input(instance);
            output.init(instance);
        output = new Output(instance);
            alarm.init(instance);
        alarmSeverity = instance.getField("alarmSeverity");
         }
        alarmStatus = instance.getField("alarmStatus");
        severity = alarmSeverity.get();
         alarmStatus.get(status);
    }


During the first pass of initialization, nothing needs to be done.
During the first pass of initialization, nothing needs to be done.
Before initialize is called on pass 1, iocInit has
Before initialize is called on pass 1, iocInit has
already called link support so that it connects.
already called link support so that it connects.
Initialize just calls its private class init methods. They get the
Initialize just creates it's private class instances for Input and Output,
current values of their private fields.
and initializes the private fields for implementing setSeverity.


=== destroy ===
==== process ====
        public destroy() {} // destroy not needed for Java


Since Java does garbage collection destroy is not needed.
<tt>process</tt> is called by dbProcess at the beginning of record processing,
Note that iocCore will call all the link support destroy methods.
when a request is made to process while the record is active,
 
=== process ===
 
<tt>process</tt> is called by dbProcess at the beginning of record processing
and after the record specific record support completes.
and after the record specific record support completes.
During the beginning of record processing
During the beginning of record processing
process takes care of field <tt>disable</tt>
process takes care of field <tt>disable</tt>, via class Input,
During the end of record processing process
During the end of record processing process
takes care of field <tt>processLink</tt>.
takes care of field <tt>processLink</tt>, via class Output


A few features of <tt>process</tt> are:
    public ProcessReturn process(processState state,Callback callback) {
* implements a state machine that handles the links.
        if(state==processCancel) {
* interacts with Callback, which is called by link support when links complete.
            input.process(state,callback);
* interacts with special, which is called when fields in RecordCommon are modified.
            output.process(state,callback);
            return processDone;
        }
        if(state==processStart) {
            ProcessReturn result = input.process(state,callback);
            if(result!=processDone) return result;
        }
        if(state==processFinish) {
            ProcessReturn result = output.process(state,callback);
            if(result!=processDone) return result;
        }
        return output.process(state,callback)
    }


        public processState process(processState state) {
            switch(state) {
            case processCancel: mode = modeNone; return processDone;
            case processStart: {
                mode = modeInput;
                requestProcess = false;
                LinkWaitResult result = input.disableSupport.getWait(
                    input.disableInput,0.0,this);
                if(result==linkNoop || result==linkDone) return processDone;
                requestProcess = true;
            }
            // fall through to processInputActive
            case processInputActive: {
                if(requestProcess) return processInputActive;
                if(input.disable.disableValue == disable.disableInput)
                        return processDone;
                return processActive;
            }
            case processDone: {
                mode = modeOutput;
                output.nextLink = 0;
                output.numberLinksActive = 0;
                   
            }
            // fall through to processOutputActive
            case processOutputActive:
                LinkWaitResult result = linkWaitParallel;
                requestProcess = false;
                while(output.nextLink< output.link.length) {
                    if(result==linkWaitSequential) break;
                    result = output.link[nextLink].support.processWait(0.0,this);
                    if(result==linkWaitSequential || result==linkWaitParallel)
                        output.numberLinksActive++
                    output.nextLink++;
                }
                if(output.numberLinksActive==0) {
                      mode = modeNone;
                      return processDone;
                }
                requestProcess = true;
                return processOutputActive;
            default: throw java.lang.IllegalStateException;
            }
        }


=== special ===
==== special ====
<tt>special</tt> is called when any of the fields in <tt>disable</tt>,
<tt>special</tt> is called when any of the fields in <tt>disable</tt>,
<tt>alarmStatus</tt>, <tt>alarmSeverity</tt>, or <tt>processLink</tt> are changed.
<tt>alarmStatus</tt>, <tt>alarmSeverity</tt>, or <tt>processLink</tt> are changed.
        public bool special(bool after,Dbf[] field) {
    public boolean special(boolean after,Dbf[] field) {
            int nlevels = field.length;
        int nlevels = field.length;
            assert(nlevels>0);
        assert(nlevels>0);
            if(index==alarmStatusIndex || index==alarmSeverityIndex) {
        if(index==alarmStatusIndex || index==alarmSeverityIndex) {
                  if(!alarm.modifyingAlarm) return false;
              if(!alarm.modifyingAlarm) return false;
                  return true;
              return true;
            }
            if(!after) return;
            switch(index) {
                case disableIndex: {
                    if(nlevels==1) {
                        input.init(instance);
                    } else {
                        input.special(instance,Dbf[1]);
                    }
                    break;
                }
                case processLinkIndex : output.init(instance); break;
                default: printf("Why is special being called?\n");
            }
         }
         }
   
         if(!after) return;
== Private Class Methods ==
        switch(index) {
=== Callback methods ===
             case disableIndex: {
These are the callback methods that are called by link support.
                 if(nlevels==1) {
         // Start of Callback methods
                     input = new Input(instance);
        public void done() {
                } else {
            lock(instance);
                     input.special(instance,Dbf[1]);
            switch(mode) {
             case modeNone:
                unlock(instance);
                throw java.lang.IllegalStateException;
            case modeInput:
                 if(requestProcess) {
                     requestProcess = false;
                     DbAccess.process(instance);
                 }
                 }
                 break;
                 break;
            case modeOutput:
                output.numberLinksActive--;
                if(output.numberLinksActive==0 && requestProcess) {
                    requestProcess = false;
                    DbAccess.process(instance);
                }
                return;
             }
             }
             unlock(instance);
             case processLinkIndex : output = new Output(instance); break;
            default: printf("Why is special being called?\n");
         }
         }
        public void failure() { done(); }
    }
      
      
=== Common methods ===
==== setSeverity ====
For now the only method is <tt>setSeverity,</tt>
This is the implementation of Common.setSeverity.
The only way code can change severity and status is to call this method.


        // Start of Common methods
    boolean setSeverity(AlarmSeverity severity, string status) {
        bool setSeverity(AlarmSeverity severity, string status) {
        if(severity<= this.severity) return false;
            Alarm alarm = this.alarm;
        this.severity = severity;
            if(severity<= alarm.severity) return false;
        this.status = status;
            alarm.severity = severity;
        this.modifyingAlarm = true;
            alarm.status = status;
        this.alarmSeverity.putIndex(severity);
            alarm.modifyingAlarm = true;
        this.alarmStatus.put(status);
            alarm.alarmSeverity.putIndex(severity);
        this.modifyingAlarm = false;
            alarm.alarmStatus.put(status);
            alarm.modifyingAlarm = false;
        }
     }
     }
=== Input ===
This is the package private class the handles disable.


=== Input ===
class Input implements Callback{
This is a private class for RecordCommonSupport.
    RecordInstance instance;
It directly accesses the fields in this class.
    DisableData disable;
The init and special methods are called to get values for the private fields.
    LinkInt16  disableSupport;
      
     Callback    processCallback;
    // Definitions for modeInput
     class Input {
     Input(RecordInstance instance) {
         DisableData disable;
         this.instance = instance;
         LinkSupport disableSupport;
         DisplayLimitAccess.get(
        DbfInt16    disableInput;
            (DbfStruct)instance.getField("disable"),disable);
         DbfInt16    disableValue;
         disableSupport = DbAccess.getLinkSupport(disable.disableLink);
        DbfInt16    alarmSeverity;
    }
      
     public ProcessReturn process(processState state,Callback callback) {
         void init(RecordInstance instance) {
         switch(state) {
             RecordAccess recordAccess = instance.getRecordCommonAccess();
        case processCancel:
             DbfLink link = recordAccess.getField("disable.disableLink");
             processCallback = null;
             disableSupport = link.getSupport();
            disableSupport.cancel(); return(processDone);
             DisplayLimitAccess.get(
        case processStart:
                 recordAccess.getField("disable"),disable);
             LinkWaitResult result;
             recordAccess.getField("disable.disableInput",disableInput);
            result = disableSupport.getWait(disable.disableInput,this);
             switch(result) {
            case linkNoop:
             case linkDone:
                 if(disable.disableInput==disable.disableValue)
                    return processAbort;
                return processDone;
             case linkWait:
            case linkWaitBlock:
                processCallback = callback;
                return processActive;
        default:
                throw java.lang.IllegalStateException;
         }
         }
        void special(RecordInstance instance,Dbf field) {
    }
            int16 index = Dbf.getIndex();
    void special(RecordInstance instance,Dbf field) {
            switch(index) {
        int16 index = Dbf.getIndex();
                case disableValueIndex:
        switch(index) {
                    disableValue = ((DbfInt16)Dbf).get(); break;
            case disableValueIndex:
                case disableInputIndex:
                disable.disableValue = ((DbfInt16)Dbf).get(); break;
                    disableInput = ((DbfInt16)Dbf).get(); break;
            case disableInputIndex:
                case disableAlarmSeverityIndex:
                disable.disableInput = ((DbfInt16)Dbf).get(); break;
                    alarmSeverity = ((dbfMenu)Dbf).getIndex(); break;
            case disableAlarmSeverityIndex:
                case disableLinkIndex:
                disable.alarmSeverity = ((dbfMenu)Dbf).getIndex(); break;
                     init(instance); break;
            case disableLinkIndex:
                default:
            case disableIndex:
                    throw java.lang.IllegalStateException;
                DisplayLimitAccess.get(
            }
                     (DbfStruct)instance.getField("disable"),disable);
                disableSupport = disable.disableLink.getSupport();
                break;
            default:
                throw java.lang.IllegalStateException;
         }
         }
     }
     }
    // Start of Callback methods
    public void done() {
        DbAccess.lock(instance);
        Callback callback = processCallback;
        processCallback = null;
        DbAccess.unlock(instance);
        if(callback!=null) callback.done();
    }
    public void failure() { done(); }
}
      
      
=== Output ===
This is the package private class that handles processLink.


=== OutputLink and Output ===
Output is a private class for RecordCommonSupport.
It directly accesses the fields in this class.
The init  method is called to get values for the private fields.
OutputLink is used by Output.


    // Definitions for modeOutput
class OutputLink {
    class OutputLink {
    Link        link;
        Link        link;
    LinkProcess support;
        LinkSupport support;
    void init(RecordInstance instance,String linkFieldName) {
        void init(RecordInstance instance,String linkFieldName) {
        DbfLink link = (DbfLink)instance.getField(linkFieldName);
            RecordAccess recordAccess = instance.getRecordCommonAccess();
        support = DbAccess.getLinkSupport(link);
            DbfLink link = recordAccess.getField(linkFieldName);
    }
             support = link.getSupport();
};
class Output {
    RecordInstance instance;
    int          nextLink;
    int          numberLinksActive;
    OutputLink[] link;
    Callback    processCallback;
    Output(RecordInstance instance) {
        this.instance = instance;
        DbfArray array = instance.getField("processLink");
        int n = array.getNelements();
        link = new OutputLink[n];
        for(i=0; i< n; i++) {
             String field = "processLink[" + "i" + "]";
            link[i].init(instance,field);
         }
         }
     };
     }
      
     public ProcessReturn process(processState state,Callback processCallback) {
    class Output {
        switch(state) {
         int          nextLink;
         case processCancel:
        int         numberLinksActive;
            processCallback = null;
        OutputLink[] link;
            for(int i=0; i<link.length) {
        void init(RecordInstance instance) {
                link[i].support.cancel();
             RecordAccess recordAccess = instance.getRecordCommonAccess();
            }
             DbfArray array = recordAccess.getField("processLink");
             return(processDone);
             int n = array.getNelements();
        case processDone:
             link = new OutputLink[n];
             nextLink = 0;
             for(i=0; i< n; i++) {
             numberLinksActive = 0;
                 String field = "processLink[" + "i" + "]";
             // fall through to processOutputActive
                 link[i].init(instance,field);
        case processActive:
            LinkWaitResult result = linkWait;
             while(nextLink< link.length) {
                 result = link[nextLink].support.process(this);
                 if(result==linkWait || result==linkWaitBlock) {
                    link[nextLink].active = true;
                    numberLinksActive++;
                }
                if(result==linkWaitBlock) break;
                output.nextLink++;
             }
             }
            if(output.numberLinksActive==0) return processDone;
            return processActive;
        default:
                throw java.lang.IllegalStateException;
         }
         }
    // Start of Callback methods
    public void done() {
        DbAccess.lock(instance);
        numberLinksActive--;
        Callback callback = processCallback;
        processCallback = null;
        DbAccess.unlock(instance);
        if(numberLinksActive==0 && callback!=null) callback.done();
     }
     }
      
     public void failure() { done(); }
=== Alarm ===
}
This is a private class for RecordCommonSupport.
It directly accesses the fields in this class.
The init method is called to get values for the private fields.
 
    // Definitions for alarm handling
    class Alarm {
        short severity;
        String status;
        DbfMenu alarmSeverity;
        DbfString alarmStatus;
        bool modifyingAlarm;
        void init(RecordInstance instance) {
            RecordAccess recordAccess = instance.getRecordCommonAccess();
            alarmSeverity = recordAccess.getField("alarmSeverity");
            alarmStatus = recordAccess.getField("alarmStatus");
        }
    }
   
== RecordCommonSupportFactory ==
== RecordCommonSupportFactory ==
This is the factory for creating an RecordCommonSupport.
This is the factory for creating an RecordCommonSupport.

Latest revision as of 14:49, 4 October 2005

EPICS V4: RecordCommonSupport - October 04 2005


Overview

This document describes the implementation of the record support for RecordCommon, i.e. the set of fields associated with every record type.

For V4 dbCommon.dbd and RecordCommon.dbd replace the V3 dbCommon.dbd. dbCommon.dbd define widely used definitions and RecordCommon.dbd defines the fields that are common to all record types.


This document describes the following:

  • The code automatically generated from dbCommon.dbd and RecordCommon.dbd
  • A proposed implementation of RecordCommonSupport.

This document requires that the reader be familiar with:

  1. V3 iocCore
  2. V4 Design: Record Processing
  3. V4 Design: dbdInterfaces
  4. V4 Design: Runtime interfaces
  5. V4 Design: RecordCommon

Java is used as the example code for two reasons:

  1. For V4 iocCore it is intended that both C++ and Java implementations will be provided. Since V3 is only implemented in C and C++ the Java implemention of V4 is described first to make sure that the interfaces defined in dbdInterfaces will work for Java.
  2. Java provides facilities that make it easy to handle strings, arrays, and structures.

An implementaion of RecordCommonSupport is presented for two reasons:

  1. It demonstrates how to implement code that uses or implements the interfaces described in "V4 Design: dbdInterfaces" and "V4 Design: Runtime interfaces".
  2. It helps refine the interfaces described in them

dbCommon.dbd

See the description of dbCommon.dbd in "V4 Design: RecordCommon"

Link support must register support for each of the link definitions.

From dbCommon.dbd the following class implementations are generated:

  • For the link structures:
    • MonitorLink, MonitorLinkData, and MonitorLinkSupport
    • InputLink, InputLinkData, and InputLinkSupport
    • OutputLink, OutputLinkData, and OutputLinkSupport
    • ProcessLink, ProcessLinkData, and ProcessLinkSupport
  • For TimeStamp
    • TimeStamp, TimeStampData, and TimeStampSupport
  • For Scan
    • Scan, ScanData, and ScanSupport

See "V4 Design: Runtime interfaces" for an example of how these classes are defined.


RecordCommon.dbd

See the description of RecordCommon.dbd in "V4 Design: RecordCommon"

RecordCommon.dbd must be part of every record.

Implementations of Disable, DisableData, and DisableDataSupport are automatically generated.

An implementations of RecordCommon is also generated. See "V4 Design: Runtime interfaces" for an example of the generated classes.


RecordCommonSupport.java

Overview

This section describes the java implementation of RecordCommon functionality. This code handles processing of the following fields of RecordCommon:

disable
Decides if the record is disabled.
alarmStatus and alarmSeverity
The only way to change alarmStatus and alarmSeverity is via interface Common, which is implemented by RecordCommonSupport.
processLink
This is an array of process links, which replace the V3 FLNK

The following subsection describes the code for RecordCommonSupport.java. The remaining subsections describes the code for additional classes that are part of the implementation.

RecordCommonSupport.java

interface Common {
    boolean setSeverity(AlarmSeverity severity, string status);
}

public class RecordCommonSupport implements RecordSupport, Common
{
    private RecordInstance instance;
    private Input          input;
    private Output         output;
    // The following are for implementing Common.setSeverity
    private short severity;
    private String status;
    private DbfMenu alarmSeverity;
    private DbfString alarmStatus;
    private boolean modifyingAlarm;

    // Constructor
    public RecordCommonSupport(RecordInstance instance) {
        this.instance = instance;
    }

    // Beginning of RecordSupport
    public destroy() {} // destroy not needed for Java
    public initialize(int pass) {
        if(pass==0) return;
        input = new Input(instance);
        output = new Output(instance);
        alarmSeverity = instance.getField("alarmSeverity");
        alarmStatus = instance.getField("alarmStatus");
        severity = alarmSeverity.get();
        alarmStatus.get(status);
    }

    public ProcessReturn process(processState state,Callback callback) {
        if(state==processCancel) {
            input.process(state,callback);
            output.process(state,callback);
	    return processDone;
        }
        if(state==processStart) {
            ProcessReturn result = input.process(state,callback);
            if(result!=processDone) return result;
        }
        if(state==processFinish) {
            ProcessReturn result = output.process(state,callback);
            if(result!=processDone) return result;
        }
        return output.process(state,callback)
    }

    public boolean special(boolean after,Dbf[] field) {
        int nlevels = field.length;
        assert(nlevels>0);
        if(index==alarmStatusIndex || index==alarmSeverityIndex) {
             if(!alarm.modifyingAlarm) return false;
             return true;
        }
        if(!after) return;
        switch(index) {
            case disableIndex: {
                if(nlevels==1) {
                    input = new Input(instance);
                } else {
                    input.special(instance,Dbf[1]);
                }
                break;
            }
            case processLinkIndex : output = new Output(instance); break;
            default: printf("Why is special being called?\n");
        }
    }

    // Start of Common methods
    boolean setSeverity(AlarmSeverity severity, string status) {
        if(severity<= this.severity) return false;
        this.severity = severity;
        this.status = status;
        this.modifyingAlarm = true;
        this.alarmSeverity.putIndex(severity);
        this.alarmStatus.put(status);
        this.modifyingAlarm = false;
    }
}

RecordCommonSupport implements the following interfaces:

RecordSupport
Record support for the fields in RecordCommon.
Common
For now this just has the method setSeverity

It has the following private fields:

  • instance - The record instance to which it is attached. RecordCommonSupport has a single constructor which is passed the RecordInstance to which it is attached.
  • input - Input is a private class that provides access to the fields of disable.
  • output - Output is a private class that provides access to processLink.
  • severity, ..., modifyingAlarm - private fields for implementing Common.setSeverity

Notes about Input and Output.

  • Each of these is a private class to RecordCommonSupport.
  • The implementations of each of these private classes is given after the implementaion of RecordCommonSupport.

RecordSupport Methods

destroy

        public destroy() {} // destroy not needed for Java

Since Java does garbage collection destroy is not needed. Note that iocCore will call all the link support destroy methods.

initialize

    public initialize(int pass) {
        if(pass==0) return;
        input = new Input(instance);
        output = new Output(instance);
        alarmSeverity = instance.getField("alarmSeverity");
        alarmStatus = instance.getField("alarmStatus");
        severity = alarmSeverity.get();
        alarmStatus.get(status);
    }

During the first pass of initialization, nothing needs to be done. Before initialize is called on pass 1, iocInit has already called link support so that it connects. Initialize just creates it's private class instances for Input and Output, and initializes the private fields for implementing setSeverity.

process

process is called by dbProcess at the beginning of record processing, when a request is made to process while the record is active, and after the record specific record support completes. During the beginning of record processing process takes care of field disable, via class Input, During the end of record processing process takes care of field processLink, via class Output

    public ProcessReturn process(processState state,Callback callback) {
        if(state==processCancel) {
            input.process(state,callback);
            output.process(state,callback);
           return processDone;
        }
        if(state==processStart) {
            ProcessReturn result = input.process(state,callback);
            if(result!=processDone) return result;
        }
        if(state==processFinish) {
            ProcessReturn result = output.process(state,callback);
            if(result!=processDone) return result;
        }
        return output.process(state,callback)
    }


special

special is called when any of the fields in disable, alarmStatus, alarmSeverity, or processLink are changed.

    public boolean special(boolean after,Dbf[] field) {
        int nlevels = field.length;
        assert(nlevels>0);
        if(index==alarmStatusIndex || index==alarmSeverityIndex) {
             if(!alarm.modifyingAlarm) return false;
             return true;
        }
        if(!after) return;
        switch(index) {
            case disableIndex: {
                if(nlevels==1) {
                    input = new Input(instance);
                } else {
                    input.special(instance,Dbf[1]);
                }
                break;
            }
            case processLinkIndex : output = new Output(instance); break;
            default: printf("Why is special being called?\n");
        }
    }
    

setSeverity

This is the implementation of Common.setSeverity.

    boolean setSeverity(AlarmSeverity severity, string status) {
        if(severity<= this.severity) return false;
        this.severity = severity;
        this.status = status;
        this.modifyingAlarm = true;
        this.alarmSeverity.putIndex(severity);
        this.alarmStatus.put(status);
        this.modifyingAlarm = false;
    }

Input

This is the package private class the handles disable.

class Input implements Callback{
    RecordInstance instance;
    DisableData disable;
    LinkInt16   disableSupport;
    Callback    processCallback;

    Input(RecordInstance instance) {
        this.instance = instance;
        DisplayLimitAccess.get(
            (DbfStruct)instance.getField("disable"),disable);
        disableSupport = DbAccess.getLinkSupport(disable.disableLink);
    }
    public ProcessReturn process(processState state,Callback callback) {
        switch(state) {
        case processCancel:
            processCallback = null;
            disableSupport.cancel(); return(processDone);
        case processStart:
            LinkWaitResult result;
            result = disableSupport.getWait(disable.disableInput,this);
            switch(result) {
            case linkNoop:
            case linkDone:
                if(disable.disableInput==disable.disableValue)
                    return processAbort;
                return processDone;
            case linkWait:
            case linkWaitBlock:
                processCallback = callback;
                return processActive;
        default:
                throw java.lang.IllegalStateException;
        }
    }
    void special(RecordInstance instance,Dbf field) {
        int16 index = Dbf.getIndex();
        switch(index) {
            case disableValueIndex:
                disable.disableValue = ((DbfInt16)Dbf).get(); break;
            case disableInputIndex:
                disable.disableInput = ((DbfInt16)Dbf).get(); break;
            case disableAlarmSeverityIndex:
                disable.alarmSeverity = ((dbfMenu)Dbf).getIndex(); break;
            case disableLinkIndex:
            case disableIndex:
                DisplayLimitAccess.get(
                    (DbfStruct)instance.getField("disable"),disable);
                disableSupport = disable.disableLink.getSupport();
                break;
            default:
                throw java.lang.IllegalStateException;
        }
    }

    // Start of Callback methods
    public void done() {
        DbAccess.lock(instance);
        Callback callback = processCallback;
        processCallback = null;
        DbAccess.unlock(instance);
        if(callback!=null) callback.done();
    }
    public void failure() { done(); }
}
    

Output

This is the package private class that handles processLink.


class OutputLink {
    Link        link;
    LinkProcess support;
    void init(RecordInstance instance,String linkFieldName) {
        DbfLink link = (DbfLink)instance.getField(linkFieldName);
        support = DbAccess.getLinkSupport(link);
    }
};

class Output {
    RecordInstance instance;
    int          nextLink;
    int          numberLinksActive;
    OutputLink[] link;
    Callback     processCallback;

    Output(RecordInstance instance) {
        this.instance = instance;
        DbfArray array = instance.getField("processLink");
        int n = array.getNelements();
        link = new OutputLink[n];
        for(i=0; i< n; i++) {
            String field = "processLink[" + "i" + "]";
            link[i].init(instance,field);
        }
    }
    public ProcessReturn process(processState state,Callback processCallback) {
        switch(state) {
        case processCancel:
            processCallback = null;
            for(int i=0; i<link.length) {
                link[i].support.cancel();
            }
            return(processDone);
        case processDone: 
            nextLink = 0;
            numberLinksActive = 0;
            // fall through to processOutputActive
        case processActive:
            LinkWaitResult result = linkWait;
            while(nextLink< link.length) {
                result = link[nextLink].support.process(this);
                if(result==linkWait || result==linkWaitBlock) {
                    link[nextLink].active = true;
                    numberLinksActive++;
                }
                if(result==linkWaitBlock) break;
                output.nextLink++;
            }
            if(output.numberLinksActive==0) return processDone;
            return processActive;
        default:
                throw java.lang.IllegalStateException;
        }

    // Start of Callback methods
    public void done() {
        DbAccess.lock(instance);
        numberLinksActive--;
        Callback callback = processCallback;
        processCallback = null;
        DbAccess.unlock(instance);
        if(numberLinksActive==0 && callback!=null) callback.done();
    }
    public void failure() { done(); }
}

RecordCommonSupportFactory

This is the factory for creating an RecordCommonSupport. It is called by dbLoadRecords.

    public class RecordCommonSupportFactory implements RecordSupportFactory{
        RecordSupport create(RecordInstance instance) {
            return new RecordCommonSupport(instance);
        }
        void destroy(RecordSupport support) {
            RecordCommonSupport RecordCommonSupport = support;
            RecordCommonSupport.destroy();
        }
    }
    public final class RecordCommonSupportFactoryRegister {
        static public createAndRegister() {
            RecordCommonSupportFactory factory = new RecordCommonSupportFactory;
            RegisterSupport.record(factory,"RecordCommon");
        }
    }

Question Who calls RecordCommonSupportFactoryRegister.createAndRegister?