EPICS pvDataJava

EPICS v4 Working Group, Working Draft, 14-Dec-2011

This version:
pvDataJava_20111214.html
Latest version:
pvDataJava.html
Editors:
Marty Kraimer, BNL

Abstract

This document describes the Java implementation of EPICS v4 pvData.

For more information about the EPICS, please refer to the home page of the Experimental Physics and Industrial Control System.

Status of this Document

This is the 14-Dec-2011 version of the Java implementation of pvData. It is a complete implementation of pvData as currently defined.

CONTENTS

Introduction


This product is available via an open source license

This document is the project and package overviews for pvDataJava. The javaDOC is available at JavaDoc

pvData is one of a set of related projects. It describes and implements the data that the other projects support. Thus it is not useful by itself but understanding pvData is required in order to understand the other projects. The reader should also become familar with projects pvAccess and javaIOC, which are located via the same sourceforge site as this project.

pvData (Process Variable Data) defines and implements an efficent way to store, access, and transmit memory resident structured data.

definition
Package org.epics.pvData.pv has Java interface definitions that define pvData. Although defined with Java syntax they also document what is required for implementation in other languages such as C++.
implementation
The set of packages provides by this project provide a complete Java implementation of pvData. Project pvAccess is the network support for pvData, i.e. it provides a channel access client and server that fully support pvData. Project javaIOC provides an IOC (Input Output Controller) that supports pvData and provides record processing..
efficient
Small memory footprint, low cpu overhead, and concise code base.
data storage
pvData defines separate introspection and data interfaces. The introspection interfaces provide access to immutable objects, which allows introspection instances to be freely shared. The introspection interface for a process variable can be accessed without requiring access to the data.
data access
Client code can access pvData via the introspection and data interfaces. For "well known" data, e.g. timeStamp, specialized interfaces can be provided without requiring any changes to the core software.
data transfer
The separation of introspection and data interfaces allows for efficient network data transfer. At connection time introspection information can be passed from server to client. Each side can create a data instance. The data is transferred between these instances. The data in the network buffers does not have to be self describing since each side has the introspection information.
memory resident
pvData only defines memory resident data.
structured data
pvData has three types: scalar, array, and structure. A scalar can be one of the following: boolean, byte, short, int, long, float, double, string. An array is a one dimensional array with the element type being a scalar. A structure is an ordered set of fields where each field has a name and type. Since a field can have type structure complex structures are supported. No other types are needed since structures can be defined that simulate types.

The javaIOC implements a Process Variable (PV) Database, which is a memory resident database holding pvData, has the following features:

pvData was initially created to support the javaIOC and was part of the javaIOC project. It is now a separate project that is used by the javaIOC. In addition to the javaIOC, pvData is intended for use by 1) channel access clients, 2) Interface between client and network, 3) Interface between network and channel access server, 4) Interface between server and IOC database. Since it is an interface to data, it could also be used by other systems, e.g. TANGO, TINE, etc. A high level Physics application can hold data as pvData. By starting a channel access server, the data can made available to network clients.

pvData contains everything required to support Channel Access and Channel Access clients and servers.


PVData Meta Language


This section describes a meta language for describing pvData. Currently there are no plans for a parser for the meta language. It is used for documentation. The toString introspection and data methods described below do present data in a format similar to the metadata syntax. The meta language is used to describe both introspection interfaces and data interfaces.

Definition

PVData supports structured data. All data appears as a top level structure. A structure has an ordered set of fields where each field has a fieldDef defined as follows:

type fieldName value // comment

where value is present for data objects and // indicates the the rest of the line is a comment.

type is one of the following:

scalar
A scalar field can be any of the following:
boolean
Has the valuetrue or false
byte
An 8 bit signed integer.
short
An 16 bit signed integer.
int
An 32 bit signed integer.
long
An 64 bit signed integer.
float
A IEEE float.
double
A IEEE double.
string
An immutable string.
scalarArray
A scalarArray field is an array of any of the scalar types.
boolean[]
byte[]
short[]
int[]
long[]
float[]
double[]
string[]
structure
A structure field has the definition:
     structure fieldName
         fieldDef
         ...
      
or
     xxx_t fieldName
         // if data object then following appear
         fieldDef
         ...
      
For structure fieldName each fieldDef must have a unique fieldName within the structure For "xxx_t fieldName" xxx_t must be a previously structure definition of the form:
    structure xxx_t
        ...
structureArray
A structureArray field has the definition:
     structure[] fieldName
          structureDef
          ...
          

or

    xxx_t[] fieldName
      

Thus a structure array is an array where each element is a structure but all elements have the same introspection interface. For introspection the structureDef appears once without any data valuies.

The above is used to describe introspection objects. Data objects are describe in a similar way but each scalar field and each array field has data values. The definition of the data values depends on the type. For scalars the data value is whatever is valid for the type.

boolean
The value must be true or false
byte,...long
Any valid integer or hex value, e.g. 3 0xff are valid values
float,double
Any valid integer or real e.g. 3 3.0 3e0 are valid values
string
The value can be an alphanumeric value or any set of characters enclosed in "" Within quotes a quote is expressed as \" Examples are aValue "a value" "a\" xxx" are valid values.

For scalar arrays the syntax is:

      = [value,...,value]

where each value is a valid scalar data value depending on the type. Thus it is a comma separated set of values enclosed in [] White space is permitted surrounding each comma.

Examples

Define the following top level structure:

structure timeStamp_t
    long secondsPastEpoch
    int nanoSeconds 

Then the following introspection objects can be defined:

structure scalarDoubleExample // introspection object
    double value
    timeStamp_t timeStamp
or
structure scalarDoubleExample // introspection object
    double value
    structure timeStamp
        long secondsPastEpoch
        int  nanoSeconds

The following data objects can be defined:

structure scalarDoubleExample // data object
    double value 1.0
    timeStamp_t timeStamp
         long secondsPastEpoch 0
         int  nanoSeconds 0
or
scalar arrayDoubleExample
    double[] value  [1.0,2.0]
    structure timeStamp
         long secondsPastEpoch 0
         int  nanoSeconds 0

If the following interface is defined:

structure point_t
    double x
    double y

Then the following introspection objects can be defined:

structure lineExample
    point_t begin
    point_t end

structure pointArrayExample
    point_t[] points
    
or
structure lineExample
    structure begin
        double x
        double y
    structure end
        double x
        double y

structure pointArrayExample
    structure[] points
        structure point
            double x
            double y

And the following data objects can be defined:

structure lineExample
    point_t begin
        double x 0.0
        double y 0.0
    point_t end
        double x 10
        double y 10

structure pointArrayExample
    point_t[] value
        structure point
            double x 0.0
            double y 0.0
        structure point
            double x 10.0
            double y 10.0

or

structure lineExample
    structure begin
        double x 0
        double y 0
    structure end
        double x 10
        double y 10

structure pointArrayExample
    structure[] value
        structure point
            double x 0.0
            double y 0.0
        structure point
            double x 10.0
            double y 10.0

Interface Definitions


Types

The following are the type definitions:

enum Type {
    scalar,
    scalarArray,
    structure,
    structureArray;
}

where

scalar
A field that has data with one of the scalarTypes.
scalarArray
A field that is an array where each element is the same scalarType.
structure
A field which has named subfields.
structureArray
A field that is an array of structures where each element has the same introspection interface, i.e. each element has the same structure.
enum ScalarType {
    pvBoolean,
    pvByte,pvShort,pvInt,pvLong,
    pvFloat,pvDouble,
    pvString;
    // The following are convenience methods
    public boolean isInteger();
    public boolean isNumeric();
    public boolean isPrimitive();
    public static ScalarType getScalarType(String type);
    public String toString();
}

where

boolean
true or false
byte
An 8 bit signed byte
short
16 bit signed integer
int
32 bit signed integer
long
64 bit signed integer
float
32 bit IEEE float
double
64 bit IEEE float
string
An immutable string. The Java implementation is String. For other implementations the network representation must be the same as for Java. Note that a string is treated like it is a scaler.

Introspection Interfaces

The complete set of introspection interfaces are:

interface Field {
    String getFieldName();
    Type getType();
    void toString(StringBuilder buf);
    void toString(StringBuilder buf,int indentLevel);
    String toString();
}

interface Scalar extends Field{
    ScalarType getScalarType();
}

interface ScalarArray extends Field{
    ScalarType getElementType();
}

interface Structure extends Field{
    Field getField(String fieldName);
    int getFieldIndex(String fieldName);
    Field[] getFields();
}

interface StructureArray extends Field{
    Structure getStructure();
}

The introspection interfaces provide access to immutable objects. This allows introspection interfaces to be freely shared between data objects. For example the introspection interface for a timeStamp, which is a structure containing two fields, can be shared by every record that has a time stamp.

Data Interfaces

PVField is the base interface for a data field:

enum MessageType {info,warning,error,fatalError}

interface Requester {
    String getRequesterName();
    void message(String message, MessageType messageType);
}

interface SerializableControl {
    void flushSerializeBuffer();
    void ensureBuffer(int size);
}

interface Serializable {
    void serialize(ByteBuffer buffer, SerializableControl flusher);
    void deserialize(ByteBuffer buffer, DeserializableControl control);
}

interface SerializableArray extends Serializable {
    void serialize(ByteBuffer buffer, SerializableControl flusher, int offset, int count);
}

interface PVAuxInfo {
    PVField getPVField();
    PVScalar createInfo(String key,ScalarType scalarType);
    Map<String,PVScalar> getInfos();
    PVScalar getInfo(String key);
    void toString()StringBuilder buf;
    void toString(StringBuilder buf,int indentLevel);
}

interface PVField extends Requester, Serializable {
    int getFieldOffset();
    int getNextFieldOffset();
    int getNumberFields();
    PVAuxInfo getPVAuxInfo(); //auxillary information
    Field getField();
    PVStructure getParent();
    boolean isMutable();
    void setImmutable();
    void replacePVField(PVField newPVField);
    void renameField(String newName);
    void postPut(); // calls PVRecordField.postPut if this is a field of a record
    void setPostHandler(PostHandler postHandler);
    void toString(StringBuilder buf);
    void toString(StringBuilder buf,int indentLevel);
    String toString();
}

Each scalar type has an associated data interface: PVBoolean, PVByte, PVShort, PVInt, PVLong, PVFloat, PVDouble, and PVString. Each has a get and a put method. For example:

interface PVDouble extends PVScalar{
    double get();
    void put(double value);
}

PVArray is the base class for arrays.

interface PVArray extends PVField, SerializableArray {
    int getLength();
    void setLength(int length);
    int getCapacity();
    void setCapacity(int length);
    boolean isCapacityMutable();q!
    void setCapacityMutable(boolean isMutable);
}
nterface PVAuxInfo {
    PVField getPVField();
    PVScalar c

PVScalarArray is the base class for scalar arrays.

interface PVScalarArray extends PVArray {
    ScalarArray getScalarArray();
}

For each scalar type an associated array data interface is defined. Each has a get and put method. For example:

public class DoubleArrayData {
    public double[] data;
    public int offset;
}

interface PVDoubleArray extends PVArray {
    int get(int offset, int len, DoubleArrayData data);
    int put(int offset,int len, double[] from, int fromOffset);
    void shareData(double[] from);
}

PVScalarArray is the interface for an array of structures where each element has the same iontrospection interface.

public class StructureArrayData {
    public PVStructure[] data;
    public int offset;
}

interface PVStructureArray extends PVArray{
    StructureArray getStructureArray();
    int get(int offset, int length, StructureArrayData data);
    int put(int offset,int length, PVStructure[] from, int fromOffset);
    void shareData(PVStructure[] from);
}

PVStructure is the data interface for a structure.

interface BitSetSerializable {
    void serialize(ByteBuffer buffer, SerializableControl flusher, BitSet bitSet);
    void deserialize(ByteBuffer buffer, DeserializableControl control, BitSet bitSet);
}

interface PVStructure extends PVField , BitSetSerializable{
    Structure getStructure();
    PVField[] getPVFields();
    PVField getSubField(String fieldName);
    PVField getSubField(int fieldOffset);
    void appendPVField(PVField pvField);
    void appendPVFields(PVField[] pvFields);
    void removePVField(String fieldName);
    // The following are convenience methods
    PVBoolean getBooleanField(String fieldName);
    PVByte getByteField(String fieldName);
    PVShort getShortField(String fieldName);
    PVInt getIntField(String fieldName);
    PVLong getLongField(String fieldName);
    PVFloat getFloatField(String fieldName);
    PVDouble getDoubleField(String fieldName);
    PVString getStringField(String fieldName);
    PVScalarArray getScalarArrayField(String fieldName);
    PVStructureArray getStructureArrayField(String fieldName);
    PVStructure getStructureField(String fieldName);
    PVArray getArrayField(String fieldName,ScalarType elementType);
    String getExtendsStructureName();
    boolean putExtendsStructureName(String extendsStructureName);
}

Introspection and Data creation

The following interface creates introspection instances:

public interface FieldCreate {
    Field create(String fieldName,Field field);
    Scalar createScalar(String fieldName,ScalarType scalarType);
    ScalarArray createScalarArray(String fieldName,ScalarType elementType);
    StructureArray createStructureArray(String fieldName,Structure elementStructure);
    Structure createStructure(String fieldName, Field[] field);
}

The following interface creates data instances:

public interface PVDataCreate {
    PVField createPVField(PVStructure parent, Field field);
    PVField createPVField(PVStructure parent,String fieldName,PVField fieldToClone);
    PVScalar createPVScalar(PVStructure parent,Scalar scalar);
    PVScalar createPVScalar(PVStructure parent,String fieldName,ScalarType fieldType);
    PVScalar createPVScalar(PVStructure parent,String fieldName,PVScalar scalarToClone);
    PVScalarArray createPVScalarArray(PVStructure parent,ScalarArray array);
    PVScalarArray createPVScalarArray(PVStructure parent,String fieldName,ScalarType elementType);
    PVScalarArray createPVScalarArray(PVStructure parent,String fieldName,PVScalarArray arrayToClone;
    PVStructureArray createPVStructureArray(PVStructure parent,StructureArray structureArray);
    PVStructure createPVStructure(PVStructure parent,Structure structure);
    PVStructure createPVStructure(PVStructure parent,String fieldName,Field[] fields);
    PVStructure createPVStructure(PVStructure parent,String fieldName,PVStructure structToClone);
    PVField[] flattenPVStructure(PVStructure pvStructure);
}

pvData Conversion

An interface named Convert provides all reasonable conversions to/from pvData. See org.epics.pvData.pv.Convert for details.


Package Summary


This document describes everything via Java definitions. The initial implementation is in Java but the functionality could also be implemented in other languages such as C++.

pvData is distributed as a eclipse Java Project named pvData. This project consists of the following java packages:

org.epics.pvData.pv

The Java enum, interface, and class definitions that define pvData. This section provides a complete definition of what pvData is. This package completely describes how pvData is accessed.

org.epics.pvData.factory

Java Facilities for creating a PV Database and pvData. It provides everything required for creating pvData. It provides the following factories:

FieldFactory
Creates introspection interfaces.
PVDataFactory
Creates data interfaces for all of the supported data types.
ConvertFactory
Converts between support data types.

Although pvDataFactory can provide the implementation for all supported data types, often it is desirable to provide other implementations. To make it easy to create alternate implementations a set of abstract and base classes are supplied.

org.epics.pvData.property

Provides a way to associated properties with a field.

The basic idea is to associate properties with any field named "value". All the fields in the structure that contains the value field are considered properties of value with the field name being the property name. See that package overview for details.

This package also provides support for "well known" field definitions like timeStamp, alarm, display,etc. Code that uses pvData can be simplified by using this support.

org.epics.pvData.misc

This package provides support that is used by pvData factories and might also be useful to software that uses pvData.

org.epics.pvData.monitor

Provides the ability to monitor changes to an arbitrary subset of the fields in a record.

org.epics.pvData.accessSecurity

Access security is no implement but a discusssion of what it should be is provided.


Package org.epics.pvData.pv


Overview

This package has the complete set of enum, interface, and class definitions that describe PVData. The implementation is provided in package org.epics.pvData.factory.

A PVStructure is a field that contains an array of subfields. Each field has code for accessing the field. The interface for each field is PVField or an interface that extends PVField. Each field also has an introspection interface, which is Field or an extension of Field. This package overview describes the complete set of data and introspection interfaces for pvData.

This package also describes an interface Convert, which provides a rich set of methods for converting and copying data between field.

The interface FieldCreate creates the introspection interfaces. The interface PVDataCreate creates the PVField interfaces. Between them they provide the ability to create every type of Field and PVField, i.e. they provide a complete implemenation of pvData. It is also possible for other code to provide implementations.

The javaIOC provides a database of PVRecords where each PVRecord has a top level PVStructure. PVAccess provides network access to PVData.

Process Variable Reflection

Given a pvname, which consists of a record name and field name, it is possible to introspect the field without requiring access to data. The reflection and data interfaces are separate because the data may not be available. For example when a PVaccess client connects to a PV, the client library can obtain the reflection information without obtaining any data. Only when a client issues an I/O request will data be available. This separation is especially important for arrays and structures so that a client can discover the type without requiring that a large array or structure be transported over the network.

Type

The types are defined by the Java definitions:

    enum Type {
        scalar,
        scalarArray,
        structure,
        structureArray;
    }

    enum ScalarType {
        pvBoolean,
        pvByte, pvShort, pvInt, pvLong,
        pvFloat,pvDouble,
        pvString;

        //Convenience methods
        public boolean isInteger();   // pvByte,...,pvLong
        public boolean isNumeric();    // pvByte,...pvDouble
        public boolean isPrimitive();  // pvBoolean,...pvDouble
        public static ScalarType getScalarType(String type);
        public String toString();
    }

Serializable

The following interfaces are called by pvAccess for transporting data over the network. The abstract and base classes ensure that these methods are properly implemented.

    interface Serializable {
        void serialize(ByteBuffer buffer,SerializableControl flusher);
        void deserialize(ByteBuffer buffer,DeserializableControl control);
    }

    interface SerializableControl {
        void flushSerializeBuffer();
        void ensureBuffer(int size);
    }

    interface DeserializableControl {
        void ensureData(int size);
    }

    interface SerializableArray extends Serializable {
        void serialize(ByteBuffer buffer, SerializableControl flusher, int offset, int count);
    }       

    interface BitSetSerializable {
       void serialize(ByteBuffer buffer, SerializableControl flusher, BitSet bitSet);
       void deserialize(ByteBuffer buffer, DeserializableControl control, BitSet bitSet);
    }

Reflection

This section defines the complete set of Java PV reflection interfaces.

    
    interface Field {
        String getFieldName();
        Type getType();
        void toString(StringBuilder buf));
        void toString(StringBuilder buf,int indentLevel);
        String toString();
    } 

    interface Scalar extends Field {
        ScalarType getScalarType();
    }

    interface ScalarArray extends Field{
        ScalarType getElementType();
    }

    interface Structure extends Field{
        Field getField(String fieldName);
        int getFieldIndex(String fieldName);
        Field[] getFields();
    }

    interface StructureArray extends Field{
        Structure getStructure();
    }

    interface FieldCreate {
        Field create(String fieldName,Field field);
        Scalar createScalar(String fieldName,ScalarType scalarType);
        ScalarArray createScalarArray(String fieldName,ScalarType elementType);
        StructureArray createStructureArray(String fieldName,Structure elementStructure);
        Structure createStructure(String fieldName, Field[] fields);
    }

The above definitions support the following:

Field
A field:
Has a name.
Has a Type.
Can be converted to a string. The format is the metadata format described in the pvData project overview.
Scalar
A scalar has a scalarType
ScalarArray
The element type is a scalarType
StructureArray
The field holds PVStructure[]. Each element has the same Structure interspection interface. A client can only get/put entire PVStructure elements NOT subfields of array elements.
Structure
Has fields that can be any of the supported types.
FieldCreate
This is an interface that provides methods to create introspection interfaces. A factory is provides to create FieldCreate.

Status

Status provides a way to pass status back to client code. It is new and not currently used by pvData but may be in the future. It is used by code that uses pvData.

    interface Status extends Serializable  {
        public enum StatusType {OK,WARNING,ERROR,FATAL};
        StatusType getType();
        String getMessage();
        String getStackDump();
        boolean isOK();
        boolean isSuccess();
    }
    
    interface StatusCreate {
        Status getStatusOK(); 
        Status createStatus(StatusType type, String message, Throwable cause);
        Status deserializeStatus(ByteBuffer buffer, DeserializableControl control);
    }

The Status methods are:

StatusType
An enum for the status type.
getType
Get the statusType.
getMessage
Get a message explaining the error.
getStackDump
Get a stack dump.

The StatusCreate methods are:

getStatusOK
Get a singleton that returns StatusType.OK and a null message and stackDump.
createStatus
Create a new Status.
deserializeStatus
Use this method instead of Status.deserialize(), since this allows OK status optimization.

PVField - Data Interfaces

This section defines the Java Interfaces for accessing the data within a PV record.

PVField

PVField is the base interface for accessing data. Every field of every structure of every record instance has a PVField associated with it. A structure and a record also has an associated PVField.

    interface PVField extends Serializable, Requester {
        int getFieldOffset();
        int getNextFieldOffset();
        int getNumberFields();
        PVAuxInfo getPVAuxInfo();
        boolean isImmutable();
        void setImmutable();
        Field getField();
        PVStructure getParent();
        void replacePVField(PVField newPVField);
        void renameField(String newName);
        void postPut();
        void setRequester(Requester requester);
        void setPostHandler(PostHandler postHandler);
        String toString();
        void toString(StringBuilder buf);
        void toString(StringBuilder buf,int indentLevel);
    }

where

Requester
PVField extends Requester which is described in the next sub-section.
getFieldOffset
Get offset of the PVField field within top level structure. Every field within the PVStructure has a unique offset. The top level structure has an offset of 0. The first field within the structure has offset equal to 1. The other offsets are determined by recursively traversing each structure of the tree.
getNextFieldOffset
Get the next offset. If the field is a scalar or array field then this is just offset + 1. If the field is a structure it is the offset of the next field after this structure. Thus (nextOffset - offset) is always equal to the total number of fields within the field.
getNumberFields
Get the total number of fields in this field. This is nextFieldOffset - fieldOffset.
getPVAuxInfo
Get the PVAuxInfo for this field. PVAuxInfo is described below.
isImmutable
Is the field immutable?
setImmutable
Make the field immutable. Once a field is immutable it can never be changed since there is no method to again make it mutable. This is an important design decision because it allows immutable array fields to share the internal primitive data array.
getField
Get the reflection interface.
getParent
Get the interface for the parent or null if this is the top level PVStructure.
replacePVField
Replace this PVField. This is called by support code that wants to replace the default implementation of a data field with it's own implementation.
renameField
Rename the field name.
postPut
If this field is a field of a record pvRecordField.postPut() is called. If not a field of a record nothing happens.
setRequester
Set a requester to receive messages sent to this field. A field can only have a single requester. An IllegalStateException is thrown if a requester is already attached.
toString
Converts the field data to a string. This is mostly for debugging purposes. The format is the meta data format described in the pvData project overview.

Requester

A PVField extends Requester. Requester is present so that when database errors are found there is someplace to send a message. As will be seen below, PVRecord provides methods to register message requesters. Also a PVDatabase provides an identical method. Thus when a message is generated for a field it is propagated up to the record with the full field name attached and then propagated to the PVDatabase which sends the messages to the registered requesters.

    enum MessageType {info,warning,error,fatalError}

    interface Requester {
        String getRequesterName();
        void message(String message, MessageType messageType);
    }

where

MessageType
Type of message.
Requester
The default implementation is:

PVAuxInfo

AuxInfo (Auxillary Information) is information about a field that is application specific. It will not be available outside the application that implements the database. In particular it will not be made available to Channel Access. It is used by the database itself to override the default implementation of fields. The JavaIOC uses it for attaching support code. Database Configuration and other tools can use it for configuration information. Each Field and each PVField can have have an arbitrary number of auxInfos. An auxInfo is a (key,PVScalar) pair where key is a string.

    public interface PVAuxInfo {
        PVField getPVField();
        PVScalar createInfo(String key,ScalarType scalarType);
        Map<String,PVScalar> getInfos();
        PVScalar getInfo(String key);
        void toString(StringBuilder buf);
        void toString(StringBuilder buf,int indentLevel);
    }

where

getPVField
Get the PVField to which this PVAuxInfo is attached.
createInfo
Create a new PVScalar of type scalarType.
getInfos
Get a map of all the auxInfos.
getInfo
Get the PVScalar with the specified key.
toString
Print all the auxInfo data in metadata format.

PVScalar and extensions

    interface PVScalar extends PVField {
        Scalar getScalar();
    }
Primitive PVField types

The interfaces for primitive data types are:

    interface PVBoolean extends PVScalar {
        boolean get();
        void put(boolean value);
    }

    interface PVByte extends PVScalar {
        byte get();
        void put(byte value);
    }

    interface PVShort extends PVScalar {
        short get();
        void put(short value);
    }

    interface PVInt extends PVScalar {
        int get();
        void put(int value);
    }

    interface PVLong extends PVScalar {
        long get();
        void put(long value);
    }

    interface PVFloat extends PVScalar {
        float get();
        void put(float value);
    }

    interface PVDouble extends PVScalar {
        double get();
        void put(double value);
    }
PVString

The interface for string is:

    interface PVString extends PVScalar, SerializableArray {
        String get();
        void put(String value);
    }

PVArray and Extensions

PVArray is the base interface for all the other PV Array interfaces. It extends PVField and provides the additional methods:

    interface PVArray extends PVField, SerializableArray {
        int getLength();
        void setLength(int len);
        int getCapacity();
        void setCapacity(int len);
        boolean isCapacityMutable();
        void setCapacityMutable(boolean isMutable);
    }
getLength
Get the current length. This is less that or equal to the capacity.
setLength
Set the length. If the PVField is not mutable then an exception is thrown. If this is greater than the capacity setCapacity is called.
getCapacity
Get the capacity, i.e. this is the sized of the underlying data array.
setCapacity
Set the capacity. The semantics are implementation dependent but typical semantics are as follows: If the capacity is not mutable an exception is thrown. A new data array is created and data is copied from the old array to the new array.
isCapacityMutable
Is the capacity mutable
setCapacityMutable
Specify if the capacity can be changed.
PVArray Extensions

The interface for each array type has get and put methods which have the same arguments except for the data type. For example PVDoubleArray is:

    public class DoubleArrayData {
        public double[] data;
        public int offset;
    }
    interface PVDoubleArray extends PVScalarArray {
        int get(int offset, int len, DoubleArrayData data);
        int put(int offset, int len, double[]from, int fromOffset);
        void shareData(double[] from);
    }

Get "exposes" it's internal array by setting data.data and data.offset. The caller is responsible for copying the array elements. This violates the principle that objects should not expose their internal data but is done for efficency. For example it makes it possible to copy between arrays with identical element types via a call to System.arraycopy without requiring an intermediate array.

Both get and put return the number of elements actually transfered. The arguments are:

offset
The offset in the PV array.
len
The maximum number of elements to transfer. The number actually transfered will be less than or equal to this value.
data
Get sets data.data to it's internal array and data.offset to the offset into the array. The caller is responsible for the actual data transfer.
from
The array from which the data is taken. This array is supplied by the caller
fromOffset
The offset in from

The caller must be prepared to make multiple calls to retrieve or put an entire array. A caller should accept or put partial arrays. For example the following reads an entire array:

    double[] getArray(PVDoubleArray pv)
    {
        int len = pv.getLength();
        double[] storage = new double[len];
        DoubleArrayData data = new DoubleArrayData();
        int offset = 0;
        while(offset < len) {
            int num = pv.get(offset,(len-offset),data);
            System.arraycopy(data.data,data.offset,storage,offset,num);
            offset += num;
        }
        return storage;
    } 

shareData results in the PVArray using the primitive array that is passed to this method. This is most useful for immutable arrays. In this case the caller must set the PVArray to be immutable. In the PVArray is not immutable then it is the applications responsibility to coordinate access to the array. This violates the principle that objects should not expose their internal data but is important for immutable arrays. For example pvData and the javaIOC define many enumerated structures where an enumerated structure has twofields: index and choices. Choices is a PVStringArray that holds the enumerated choices. Index is a PVInt that is the index of the currently selected choice. The choices can be immutable. Allowing the choices internal String[] to be shared between all the instances of an enumerated structure saves on storage. Another reason for allowing shared data is so that an application which processes an array can be separated into multiple modules that directly access the internal data array of a PVArray. This can be required for minimizing CPU overhead. In this case it is the applications responsibility to coordinate access to the array.

Complete set of PVArray Extensions
    interface PVScalarArray extends PVArray {
        ScalarArray getScalarArray();
    }

    public class BooleanArrayData {
        public boolean[] data;
        public int offset;
    }
    interface PVBooleanArray extends PVScalarArray {
        int get(int offset, int len, BooleanArrayData data);
        int put(int offset, int len, boolean[]from, int fromOffset);
        void shareData(boolean[] from);
    }

    public class ByteArrayData {
        public byte[] data;
        public int offset;
    }
    interface PVByteArray extends PVScalarArray {
        int get(int offset, int len, ByteArrayData data);
        int put(int offset, int len, byte[]from, int fromOffset);
        void shareData(byte[] from);
    }

    public class ShortArrayData {
        public short[] data;
        public int offset;
    }
    interface PVShortArray extends PVScalarArray {
        int get(int offset, int len, ShortArrayData data);
        int put(int offset, int len, short[]from, int fromOffset);
        void shareData(short[] from);
    }

    public class IntArrayData {
        public int[] data;
        public int offset;
    }
    interface PVIntArray extends PVScalarArray {
        int get(int offset, int len, IntArrayData data);
        int put(int offset, int len, int[]from, int fromOffset);
        void shareData(int[] from);
    }

    public class LongArrayData {
        public long[] data;
        public int offset;
    }
    interface PVLongArray extends PVScalarArray {
        int get(int offset, int len, LongArrayData data);
        int put(int offset, int len, long[]from, int fromOffset);
        void shareData(long[] from);
    }

    public class FloatArrayData {
        public float[] data;
        public int offset;
    }
    interface PVFloatArray extends PVScalarArray {
        int get(int offset, int len, FloatArrayData data);
        int put(int offset, int len, float[]from, int fromOffset);
        void shareData(float[] from);
    }

    public class DoubleArrayData {
        public double[] data;
        public int offset;
    }
    interface PVDoubleArray extends PVScalarArray {
        int get(int offset, int len, DoubleArrayData data);
        int put(int offset, int len, double[]from, int fromOffset);
        void shareData(double[] from);
    }

    public class StringArrayData {
        public String[] data;
        public int offset;
    }
    interface PVStringArray extends PVScalarArray {
        int get(int offset, int len, StringArrayData data);
        int put(int offset, int len, String[]from, int fromOffset);
        void shareData(String[] from);
    }

    public class StructureArrayData {
        public PVStructure[] data;
        public int offset;
    }

    interface PVStructureArray extends PVArray {
        StructureArray getStructureArray();
        int get(int offset, int length, StructureArrayData data);
        int put(int offset,int length, PVStructure[] from, int fromOffset);
        void shareData(PVStructure[] from);
    }

Notes about PVStructureArray: A client can only access the data in the elements of the array via the get and put methods, i.e. it is not possible to access subfields indirectly. PVStructureArray.getNumberFields() returns 1, i.e. the field looks like a leaf field.

PVStructure

The interface for a structure is:

    interface PVStructure extends PVField, BitSetSerializable {
        Structure getStructure();
        PVField[] getPVFields();
        PVField getSubField(String fieldName);
        PVField getSubField(int fieldOffset);
        void appendPVField(PVField pvField);
        void appendPVFields(PVField[] pvFields);
        void removePVField(String fieldName);
        // the following are convenience methods
        PVBoolean getBooleanField(String fieldName);
        PVByte getByteField(String fieldName);
        PVShort getShortField(String fieldName);
        PVInt getIntField(String fieldName);
        PVLong getLongField(String fieldName);
        PVFloat getFloatField(String fieldName);
        PVDouble getDoubleField(String fieldName);
        PVString getStringField(String fieldName);
        PVStructure getStructureField(String fieldName);
        PVScalarArray getScalarArrayField(String fieldName,ScalarType elementType);
        PVStructureArray getStructureArrayField(String fieldName);
        String getExtendsStructureName();
        boolean putExtendsStructureName(String extendsStructureName);
    }

where

getStructure
Get the introspection interface for the structure.
getPVFields
Returns the array of subfields. The set of subfields must all have different field names.
getSubField(String fieldName)
Get a subField of a field. For a PVStructure a non-null result is returned if fieldName is a field of the PVStructure. The fieldName can be of the form name.name...
getSubField(int fieldOffset)
Get the field located a fieldOffset, where fieldOffset is relative to the top level structure. This returns null if the specified field is not located within this PVStructure.
appendPVField
Append pvField to the end of this PVStructure. This should NOT be called if any code is attached to any of the fields in the top level structure.
appendPVFields
Append an array of pvFields to the end of this structure. Note that if the original number of fields is 0 than pvFields replaces the original. Thus the caller must NOT reuse pvFields after calling this method. This should NOT be called if any code is attached to any of the fields in the top level structure
removePVField
Remove the specified field from this structure. This should NOT be called if any code is attached to any of the fields in the top level structure.
getBooleanField
Look for fieldName. If found and it has the correct type return the interface. This and the following methods are convenience methods that allow a user to get the interface to a subfield without requiring introspection. fieldName can be of the form name.name...
getByteField
Look for fieldName. If found and it has the correct type return the interface.
getShortField
Look for fieldName. If found and it has the correct type return the interface.
getIntField
Look for fieldName. If found and it has the correct type return the interface.
getLongField
Look for fieldName. If found and it has the correct type return the interface.
getFloatField
Look for fieldName. If found and it has the correct type return the interface.
getDoubleField
Look for fieldName. If found and it has the correct type return the interface.
getStringField
Look for fieldName. If found and it has the correct type return the interface.
getScalarArrayField
Look for fieldName. If found and it has the correct type return the interface.
getStructureArrayField
Look for fieldName. If found and it has the correct type return the interface.
getExtendsStructureName
Get the name of structure that this structure extends.
putExtendsStructureName
Specify the structure that this structure extends.

PVDataCreate

PVDataCreate is an interface that provides methods that create PVField interfaces. A factory is provided that creates PVDataCreate.

interface PVDataCreate {
    PVField createPVField(PVStructure parent, Field field);
    PVField createPVField(PVStructure parent,String fieldName,PVField fieldToClone);
    PVScalar createPVScalar(PVStructure parent,Scalar scalar);
    PVScalar createPVScalar(PVStructure parent,String fieldName,ScalarType fieldType);
    PVScalar createPVScalar(PVStructure parent,String fieldName,PVScalar scalarToClone);
    PVScalarArray createPVScalarArray(PVStructure parent,ScalarArray array);
    PVScalarArray createPVScalarArray(PVStructure parent,String fieldName,ScalarType elementType);
    PVScalarArray createPVScalarArray(PVStructure parent,String fieldName,PVScalarArray arrayToClone);
    PVStructureArray createPVStructureArray(PVStructure parent,StructureArray structureArray);
    PVStructure createPVStructure(PVStructure parent,
        Structure structure);
    PVStructure createPVStructure(PVStructure parent,String fieldName,
        Field[] fields);
    PVStructure createPVStructure(PVStructure parent,String fieldName,
        PVField[] pvFields);
    PVStructure createPVStructure(PVStructure parent,String fieldName,
        PVStructure structToClone);
    PVField[] flattenPVStructure(PVStructure pvStructure);
}
where
createPVField
The PVField is created reusing the Field interface. Two methods are provided. Each calls the corresponding createPVScalar, createPVArray, or createPVStructure depending in the type os the last argument.
createPVScalar
Creates an instance of a PVScalar. Three versions are supplied. The first is passed an introspection interface. The second provides the field name and the scalarType. The last provides a field name and a PVScalar to clone. The newly created PVScalar will have the same auxInfos as the original.
createPVScalarArray
Create an instance of a PVArray. Three versions are supplied. The first is passed an introspection interface. The second provides the field name and the elementType. The last provides a field name and a PVArray to clone. The newly created PVArray will have the same auxInfos as the original.
createPVStructureArray
Create a PVStructureArray. It must be passed a structureToClone. This will become the Structure interface for ALL elements of the PVStructureArray. It MUST be used to create any new array elements.
createPVStructure
Create an instance of a PVStructure. Four methods are provided. The first method uses a previously created structure introspection interface. The second uses a Field array to initialize the sub-fields. The third uses a PVField[]. On this case the parent of each PVField will be set to the PVStructure that is being created. This is the fastest method. The fourth initializes the subfields by cloning the fields contained in structToClone. The newly created sub-fields will have the same values and auxInfos as the original. If structToClone is null then the new structure is initialized to have 0 sub-fields.
flattenPVStructure
Create an array of PVFields for the fields in the PVStructure. The order is according to fieldOffset.

Convert

NOTE about copying immutable array fields. If an entire immutable array field is copied to another array that has the same elementType, both offsets are 0, and the length is the length of the source array, then the shareData method of the target array is called and the target array is set immutable. Thus the source and target share the same primitive array.

This section describes the supported conversions between data types.

    interface Convert {
        void getFullFieldName(StringBuilder builder,PVField pvField)
        void getString(StringBuilder buf,PVField pv, int indentLevel);
        void getString(StringBuilder buf,PVField pv);
        void fromString(PVScalar pv,String from);
        void fromString(PVScalarArray pv,String from);
        int fromStringArray(PVScalarArray pv,
             int offset, int len, String[]from, int fromOffset);
        int toStringArray(PVScalarArray pv,
             int offset, int len, String[]to, int toOffset);
        boolean isCopyCompatible(Field from, Field to);
        void copy(PVField from,PVField to);
        boolean isCopyScalarCompatible(Field from, Field to);
        void copyScalar(PVField from, PVField to);
        boolean isCopyScalarArrayCompatible(ScalarArray from, ScalarArray to);
        int copyScalarArray(PVScalarArray from, int offset,
             PVScalarArray to, int toOffset, int len);
        boolean isCopyStructureCompatible(Structure from, Structure to);
        void copyStructure(PVStructure from, PVStructure to);
        boolean isCopyStructureArrayCompatible(StructureArray from, StructureArray to);
        void copyStructureArray(PVStructureArray from, PVStructureArray to);
        // For the following the pv Type must be PVByte, ...., PVDouble
        byte toByte(PVField pv);
        short toShort(PVField pv);
        int   toInt(PVField pv);
        long  toLong(PVField pv);
        float toFloat(PVField pv);
        double toDouble(PVField pv);
        String toString(PVScalar pv);
        void fromByte(PVField pv, byte from);
        void  fromShort(PVField pv, short from);
        void  fromInt(PVField pv, int from);
        void  fromLong(PVField pv, long from);
        void  fromFloat(PVField pv, float from);
        void  fromDouble(PVField pv, double from);
// For the following the element type must be pvByte, ...., pvDouble
        int toByteArray(PVScalarArray pv,
            int offset, int len, byte[]to, int toOffset);
        int toShortArray(PVScalarArray pv,
            int offset, int len, short[]to, int toOffset);
        int toIntArray(PVScalarArray pv,
            int offset, int len, int[]to, int toOffset);
        int toLongArray(PVScalarArray pv,
            int offset, int len, long[]to, int toOffset);
        int toFloatArray(PVScalarArray pv,
            int offset, int len, float[]to, int toOffset);
        int toDoubleArray(PVScalarArray pv,
            int offset, int len, double[]to, int toOffset);
        int fromByteArray(PVScalarArray pv,
            int offset, int len, byte[]from, fromOffset);
        int fromShortArray(PVScalarArray pv,
            int offset, int len, short[]from, fromOffset);
        int fromIntArray(PVScalarArray pv,
            int offset, int len, int[]from, fromOffset);
        int fromLongArray(PVScalarArray pv,
            int offset, int len, long[]from, fromOffset);
        int fromFloatArray(PVScalarArray pv,
            int offset, int len, float[]from, fromOffset);
        int fromDoubleArray(PVScalarArray pv,
            int offset, int len, double[]from, fromOffset);
        void newLine(StringBuilder builder, int indentLevel);
    }

The array methods all return the number of elements copied or converted. This can be less than len if the PVField array contains less than len elements.

newLine is a convenience method for code that implements toString It generates a newline and inserts blanks at the beginning of the newline.

The getString methods dump the data in the metadata syntax described in the pvData project overview. Note that the toString methods of PVField are implemented by calling these convert methods.

Examples

Accessing PVData

Assume that code wants to access two fields from a PVStructure:

value
Must be a PVDouble.
timeStamp
Just look for field with this name.

The following code uses introspection to get the desired information.

String getValueAndTimeStamp(PVStructure pvStructure) {
   PVField valuePV = pvStructure.getSubField("value");
   if(valuePV==null) {
       return "value field not found";
   }
   if(valuePV.getField.getType!=Type.scalar) {
       return "value field is not a scalar";
   }
   Scalar scalar = (Scalar)valuePV.getField();
   if(scalar.getScalarType!=ScalarType.pvDouble) {
       return "value field is not a double";
   }
   PVDouble pvDouble = (PVDouble)valuePV;
   PVField timeStampPV = pvStructure.getSubField("timeStamp");
   if(timeStampPV==null) {
       return "timeStamp field not found";
   }
   double value = valuePV.get();
   return value + " timeStamp " + timeStampPV.toString();
}

Creating PVData

Example of creating a scalar field.

    PVDataCreate pvDataCreate = PVDataFactory.getPVDataCreate();
    PVDouble pvValue = pvDataCreate.createPVScalar(null,"value",ScalarType.pvDouble);

Create a structure.

    
    FieldCreate fieldCreate = FieldFactory.getFieldCreate();
    PVDataCreate pvDataCreate = PVDataFactory.getPVDataCreate();

    PVStructure create() {
        Field[] fields = new Field[2];
        fields[0] = fieldCreate.createScalar("secondsSinceEpoch",ScalarType.pvLong);
        fields[1] = fieldeCreate.createScalar("nanoSeconds",ScalarType.pvInt);
        PVStructure pvStructure = pvDataCreate.createPVStructure(
            null,"timeStamp",fields);
        return(pvStructure);
    }

Create a PVStructure that has a value field and a timeStamp. Use the structure created in the previous example.

    Field[] fields = new Field[2];
    fields[0] = fieldCreate.createScalar("value",ScalarType.pvDouble);
    PVStructure pvStructure = create();
    fields[1] = fieldCreate.createStructure("timeStamp",pvStructure.getStructure.getFields();
    PVStructure pvStruct = pvDataCreate.createPVStructure(null,"myStructure",fields);

Package org.epics.pvData.factory


Overview

This package provides factories and classes to implement everything defined in package org.epics.pvData.pv

Factory

FieldFactory

    public final class FieldFactory {
        public static FieldCreate getFieldCreate();
    }

FieldFactory automatically creates a single instance of FieldCreate and provides a method to get the interface.

PVDataFactory

    public class PVDataFactory {
        public static PVDataCreate getPVDataCreate();
    }

PVDataFactory automatically creates a single instance of PVDataCreate and provides a method to get the interface.

ConvertFactory

    public final class ConvertFactory {
        public static Convert getConvert();
    }

ConvertFactory automatically creates a single instance of Convert and provides a method to get the interface.

StatusFactory

    public final class StatusFactory {
        public static StatusCreate getStatusCreate();
    }

This provides a singleton that implements StatusCreate.

Base Introspection Classes

This section describes base classes for implementing Field instances , i.e. introspection instances. For many applications they provide a complete introspection implementation but they could be extended if necessary.

    
    public class BaseField implements Field {
        public BaseField(String fieldName, Type type);
        // all Field methods
    }

    public class BaseScalar extends BaseField implements Scalar {
        public BaseScalar(String fieldName,ScalarType scalarType);
        // all Scalar methods
    }
    
    public class BaseStructure extends BaseField implements Structure {
        public BaseStructure(String fieldName,Field[] field);
        // all Structure methods
    }

    public class BaseStructureScalar extends BaseScalar implements StructureScalar {
        public BaseStructureScalar(String fieldName,Field[] fields);
        // all StructureScalar methods.
    }

    public class BaseScalarArray extends BaseField implements ScalarArray {
        public BaseScalarArray(String fieldName,ScalarType elementType);
        // all ScalarArray methods  
    }

    public class BaseStructureArray extends BaseArray implements StructureArray {
        public BaseStructureArray(String fieldName,Field[] fields);
        // all StructureArray methods
    }
}

Abstract and Base Classes for PVField

BasePVAuxInfo

This is a complete implementation of PVAuxInfo.

public class BasePVAuxInfo implements PVAuxInfo {
    public BasePVAuxInfo(PVField pvField);
    // All PVAuxInfo methods
}

AbstractPVField

This is an abstract base class for implementing PVField interfaces. It MUST be the base class for any class that extends PVField.

public abstract class AbstractPVField implements PVField{
    protected static Convert convert = ConvertFactory.getConvert();
    protected AbstractPVField(PVField parent, Field field);
    protected void replaceStructure();
    // All public methods of PVField
}

The public methods are described in package org.epics.pvData.pv. The protected methods are:

convert
A convenience for derived classes since implementations often need access to the conversion facility.
AbstractPVField
The constructor which must be called by any derived class.
replaceStructure
This is called by a derived PVStructure implementation when it wants to change the introspection interface for one of it's fields.

Classes For PVStructure

BasePVStructure

The following is a base class for any code that implements PVStructure. Any code that implements PVStructure MUST extend this class.

public class BasePVStructure extends AbstractPVField implements PVStructure
    public BasePVStructure(PVField parent, Structure structure);
    // all public methods of PVStructure
}

Classes For PVScalar

AbstractPVScalar

The following is an abstract base class for implementing PVScalar. It MUST be the base class for any code that implements an extension of PVScalar.

public class AbstractPVScalar extends AbstractPVField implements PVScalar {
    protected AbstractPVScalar(PVStructure parent, Scalar scalar);
    public Scalar getScalar();
}

BasePVBoolean

public class BasePVBoolean extends AbstractPVScalar implements PVBoolean
{
    protected boolean value = false;
    public BasePVBoolean(PVStructure parent,Scalar scalar);
    // public methods of PVBoolean
}

BasePVByte

public class BasePVByte extends AbstractPVScalar implements PVByte
{
    protected byte value;
    public BasePVByte(PVStructure parent,Scalar scalar);
    // public methods of PVByte
}

BasePVShort

public class BasePVShort extends AbstractPVScalar implements PVShort
{
    protected short value;
    public BasePVShort(PVStructure parent,Scalar scalar);
    // public methods of PVShort
}

BasePVInt

public class BasePVInt extends AbstractPVScalar implements PVInt
{
    protected int value;
    public BasePVInt(PVStructure parent,Scalar scalar);
    // public methods of PVInt
}

BasePVLong

public class BasePVLong extends AbstractPVScalar implements PVLong
{
    protected long value;
    public BasePVLong(PVStructure parent,Scalar scalar);
    // public methods of PVLong
}

BasePVFloat

public class BasePVFloat extends AbstractPVScalar implements PVFloat
{
    protected float value;
    public BasePVFloat(PVStructure parent,Scalar scalar);
    // public methods of PVFloat
}

BasePVDouble

public class BasePVDouble extends AbstractPVScalar implements PVDouble
{
    protected double value;
    public BasePVDouble(PVStructure parent,Scalar scalar);
    // public methods of PVDouble
}

BasePVString

public class BasePVString extends AbstractPVScalar implements PVString
{
    protected String value;
    public BasePVString(PVStructure parent,Scalar scalar);
    // public methods of PVString
}

Classes for PVArray

AbstractPVArray

The following is a abstract class for creating array fields. Any code that implements any extension of PVArray must extend this class.

public abstract class AbstractPVArray extends AbstractPVField implements PVArray{
    protected int length = 0;
    protected int capacity;
    protected boolean capacityMutable = true;
    
    protected AbstractPVArray(PVField parent,Array array);
    abstract public void setCapacity(int capacity);
    // public methods of PVArray
}

For each ScalarType there is a base class for implementing the corresponding array type. For most uses it is not necessary to extend these classes.

BasePVBooleanArray

public class BasePVBooleanArray extends AbstractPVArray implements PVBooleanArray
{
    protected boolean[] value;
    // implements all PVBooleanArray methods
}

BasePVByteArray

public class BasePVByteArray extends AbstractPVArray implements PVByteArray
{
    protected byte[] value;
    // implements all PVByteArray methods
}

BasePVShortArray

public class BasePVShortArray extends AbstractPVArray implements PVShortArray
{
    protected short[] value;
    // implements all PVShortArray methods
}

BasePVIntArray

public class BasePVIntArray extends AbstractPVArray implements PVIntArray
{
    protected int[] value;
    // implements all PVIntArray methods
}

BasePVLongArray

public class BasePVLongArray extends AbstractPVArray implements PVLongArray
{
    protected long[] value;
    // implements all PVLongArray methods
}

BasePVFloatArray

public class BasePVFloatArray extends AbstractPVArray implements PVFloatArray
{
    protected float[] value;
    // implements all PVFloatArray methods
}

BasePVDoubleArray

public class BasePVDoubleArray extends AbstractPVArray implements PVDoubleArray
{
    protected double[] value;
    // implements all PVDoubleArray methods
}

BasePVStringArray

public class BasePVStringArray extends AbstractPVArray implements PVStringArray
{
    protected String[] value;
    // implements all PVStringArray methods
}

BasePVStructureArray

public class BasePVStructureArray  extends AbstractPVArray implements PVStructureArray
{
   // implements all PVStructureArray methods
}

Package org.epics.pvData.property


Definition of Property

Only fields named "value" have properties. A record can have multiple value fields, which can appear in the top level structure of a record or in a substructure. All other fields in the structure containing a value field are considered properties of the value field. The fieldname is also the property name. The value field can have any type, i.e. scalar, scalarArray, or structure. Typical property fields are timeStamp, alarm, display, control, and history. The timeStamp is a special case. If it appears anywhere in the structure hieraracy above a value field it is a property of the value field.

For example the following top level structure has a single value field. The value field has properties alarm, timeStamp, and display.

structure counterOutput
    structure alarm
    double value
    structure display
        string description "Sample Description"
        string format "%f"
        string units volts
        structure limit
            double low 0.0
            double high 10.0

The following example has three value fields each with properties alarm and timeStamp. Voltage, Current, and Power each have a different alarms but all share the timeStamp.

structure powerSupplyValueStructure
    double value
    structure alarm

structure powerSupplySimple
    structure alarm
    structure timeStamp
    powerSupplyValueStructure voltage
    powerSupplyValueStructure power
    powerSupplyValueStructure current

Standard Properties

The following field names have special meaning, i.e. support properties for general purpose clients.

value
This is normally defined since most general purpose clients access this field. All other fields in the structure support or describe the value field. The type can any supported type but is usually one of the following:
scalar
One of boolean, byte, short, int, long, float, double, or string
scalarArray
An array with the elementType being a scalar type
enumerated structure
A structure that includes fields named index, choice, and choices. index is an int that selects a choice. choice is the currently selected choice. choices is an array of strings that defines the complete set of choices.
other
Other structure or array types can also be defined if clients and support code agree on the meaning. Some examples are: 1) A structure defining a 2D matrix, 2) A structure defining an image, 3) A structure that simulates a remote method, ...
timeStamp
The timeStamp. The type MUST be a timeStamp structure. Also if the PVData structure does not have a timeStamp then a search up the parent tree is made to find a timeStamp.
alarm
The alarm. The type MUST be an alarm structure.
display
A display structure as described below. It provides display characteristics for the value field.
control
A control structure as described below. It provides control characteristics for the value field.
history
Provides a history buffer for the value field. Note that currently PVData does not define history suppoprt.
other
Other standard properties can be defined.

In addition a structure can have additional fields that support the value field but are not recognized by most general purpose client tools. Typical examples are:

input
A field with support that changes the value field. This can be anything. It can be a channel access link. It can obtain a value from hardware. Etc.
valueAlarm
A field with support that looks for alarm conditions based on the value.
output
A field with support that reads the current value and sends it somewhere else. This can be anything. It can be a channel access link. It can write a value to hardware. Etc.

The model allows for device records. A device record has structure fields that that support the PVData data model. For example a powerSupport record can have fields power, voltage, current that each support the PVData data model.

PVProperty

Interface and factory for finding a field within a structure.

    interface PVProperty {
        PVField findProperty(PVField pvField,String fieldName);
        PVField findPropertyViaParent(PVField pvField,String propertyName);
        String[] getPropertyNames(PVField pvField);
    }

    public class PVPropertyFactory {
         public static PVProperty getPVProperty();
    }

Standard Properties

This section has structure definitions that support standard properties. These definitions are defined in project javaIOC.

TimeStamp

A timeStamp is represented by the following structure

structure timeStamp
    int64 secondsPartEpoch
    int32 nanoSeconds
    int32 userTag

The Epoch is the posix epoch, i.e. Jan 1, 1970 00:00:00 UTC. Both the seconds and nanoSeconds are signed integers and thus can be negative. Since the seconds is kept as a 64 bit integer, it allows for a time much greater than the present age of the universe. Since the nanoSeconds portion is kept as a 32 bit integer it is subject to overflow if a value that corresponds to a value that is greater than a little more than 2 seconds of less that about -2 seconds. The support code always adjust seconds so that the nanoSecconds part is normalized, i. e. it has is 0<=nanoSeconds<nanoSecPerSec..

TimeStamp.java

The Java definition of a timeStamp is:

interface TimeStamp {
    static final long milliSecPerSec = 1000;
    static final long microSecPerSec = milliSecPerSec*milliSecPerSec;
    static final long nanoSecPerSec = milliSecPerSec*microSecPerSec;
    static final long  posixEpochAtEpicsEpoch = 631152000;
    void normalize();
    long getSecondsPastEpoch();
    long getEpicsSecondsPastEpoch();
    int getNanoSeconds();
    int getUserTag();
    void setUserTag(int userTag);
    void put(long secondsPastEpoch,int nanoSeconds);
    long getMilliSeconds();
    void put(long milliSeconds);
    void getCurrentTime();
    boolean equals(TimeStamp other);
    boolean lt(TimeStamp other);
    boolean le(TimeStamp other);
    void add(long seconds);
    void add(double seconds);
    double diff(TimeStamp a,TimeStamp b);
}

where:

normalize
Adjust secondsPastEpoch and nanoSeconds so that 0<=nanoSeconds< nanoSecPerSec.
getSecondsPastEpoch
Get the seconds part of timeStamp
getEpicsSecondsPastEpoch
Get seconds relative to the EPICS epoch. The epics epoch starts on Jan 1 1990 00:00:00 UTC.
getNanoSeconds
Get the nanoSeconds part of timeStamp.
getUserTag
Get the userTag
setUserTag
Set the userTag
put(long secondsPastEpoch,int nanoSeconds)
Put a value into the timeStamp.
getMilliSeconds
Get bthe number of milliseconds since the epoch.
put(long milliSeconds);
Put a value into the timeStamp given the number of milliSeconds since the epoch.
getCurrentTime
Get the curent time.
equals
Is this time equal other?
lt
Is this time less than other.
le
Is this time less that or equal to other.
add(long seconds)
Add the specified number of seconds.
add(double seconds)
Add the specified number of seconds.
diff
Compute the a-b. The result is in seconds.

The TimeStamp class provides arithmetic and comparison methods for time stamps. The result is always kept in normalized form, which means that the nano second portion is 0≤=nano<nanoSecPerSec. Note that it is OK to have timeStamps for times previous to the epoch.

PVTimeStamp.java
interface PVTimeStamp {
    boolean attach(PVField pvField);
    void detach();
    boolean isAttached();
    void get(TimeStamp timeStamp);
    boolean set(TimeStamp timeStamp);
}

class PVTimeStampFactory implements PVTimeStamp {
    public static PVTimeStamp create();
}

where

attach
Attempts to attach to pvField It returns (false,true) if a timeStamp structure is found. It looks first at pvField itself and if is not an appropriate pvData structure but the field name is value it looks up the parent structure tree.
detach
Detach from the pvData structure.
isAttached
Is there an attachment to a timeStamp structure?
get
Copies data from the pvData structure to a TimeStamp. An exception is thrown if not attached to a pvData structure.
set
Copies data from TimeStamp to the pvData structure. An exception is thrown if not attached to a pvData structure.

Alarm

An alarm structure is defined as follows:

structure alarm
    int32 severity
    String message

Note that severity is NOT defined as an enumerated structure. The reason is performance, i. e. prevent passing the array of choice strings everywhere. The file alarm.h provides the choice strings. Thus all code that needs to know about alarms share the exact same choice strings.

The java definitions for alarm are:

enum AlarmSeverity {
 NONE,MINOR,MAJOR,INVALID,UNDEFINED;

    public static AlarmSeverity getSeverity(int value);
    private static final String[] alarmSeverityNames;
    public static String[] getSeverityNames() { return alarmSeverityNames;}
}

enum AlarmStatus {
    NONE,DEVICE,DRIVER,RECORD,DB,CONF,UNDEFINED,CLIENT;

    public static AlarmStatus getStatus(int value);
    private static final String[] alarmStatusNames;
    public static String[] getStatusNames() { return alarmStatusNames;}
}

class Alarm {
    public Alarm();
    public String getMessage();
    public void setMessage(String message);
    public AlarmSeverity getSeverity();
    public void setSeverity(AlarmSeverity alarmSeverity);
    public AlarmStatus getStatus();
    public void setStatus(AlarmStatus alarmStatus);
}

Alarm Severity defines the possible alarm severities

AlarmSeverity has the methods:

getSeverity
Get the alarm severity corresponding to the integer value.
getSeverityNames
Get the array of severity choices.

AlarmStatus has the methods:

getStatus
Get the alarm status corresponding to the integer value.
getStatusNames
Get the array of status choices.

Alarm has the methods:

Alarm
The constructor. It sets the severity to no alarm and the message to "".
getMessage
Get the message.
setMessage
Set the message.
getSeverity
Get the severity.
setSeverity
Set the severity.
getStatus
Get the status.
setStatus
Set the status.
PVAlarm
interface PVAlarm {
    boolean attach(PVField pvField);
    void detach();
    boolean isAttached();
    void get(Alarm alarm);
    boolean set(Alarm alarm);
}

class PVAlarmFactory implements PVAlarm{
    public static PVAlarm create();
}

where

PVAlarm
The default constructor. Attach must be called before get or set can be called.
attach
Attempts to attach to pvField It returns (false,true) if it found an appropriate pvData structure. It looks first a pvField itself and if is not an appropriate pvData structure but the field name is value it looks to see if the parent structure has an appropriate sub structure.
detach
Just detaches from the pvData structure.
isAttached
Is there an attachment to an alarm structure?
get
Copies data from the pvData structure to an Alarm. An exception is thrown if not attached to a pvData structure.
set
Copies data from Alarm to the pvData structure. An exception is thrown if not attached to a pvData structure.
re>

Control

Control information is represented by the following structure

structure control
    structure limit
        double low
        double high
The java definition for Control is:
class Control {
    Control();
    double getLow();
    double getHigh();
    void setLow(double value);
    void setHigh(double value);
}

where

Control
The default constructure.
getLow
Get the low limit.
getHigh
Get the high limit.
setLow
Set the low limit.
setHigh
Set the high limit.

In addition the following is used to access an alarm structure.

interface PVControl {
    boolean attach(PVField pvField);
    void detach();
    boolean isAttached();
    void get(Control control);
    boolean set(Control control);
}

class PVControlFactory{
    public static PVControl create();
}

where

attach
Attempts to attach to pvField It returns (false,true) if it found an appropriate pvData structure. It looks first a pvField itself and if is not an appropriate pvData structure but the field name is value it looks to see if the parent structure has an appropriate sub structure.
detach
Just detaches from the pvData structure.
isAttached
Is there an attachment to a control structure?
get
Copies data from the pvData structure to a Control. An exception is thrown if not attached to a pvData structure.
set
Copies data from Control to the pvData structure. An exception is thrown if not attached to a pvData structure.
create
Create a PVControl instance. Attach must be called before get or set can be called.

Display

Display information is represented by the following structure

structure display
    structure limit
        double low
        double high
    string description
    string format
    string units

The Java definition for display is:

class Display {
    Display();
    double getLow();
    double getHigh();
    void setLow(double value);
    void setHigh(double value);
    String getDescription();
    void setDescription(String value);
    String getFormat();
    void setFormat(String value);
    String getUnits();
    void setUnits(String value);
}

where

Control
The default constructure.
getLow
Get the low limit.
getHigh
Get the high limit.
setLow
Set the low limit.
setHigh
Set the high limit.
getDescription
Get the description.
setDescription
Set the description.
getFormat
Get the format.
setFormat
Set the format.
getUnits
Get the units.
setUnits
Set the units.

In addition the following is used to access a control structure.

interface PVDisplay {
    boolean attach(PVField pvField);
    void detach();
    boolean isAttached();
    void get(Display display);
    boolean set(Display display);
}

class PVDisplayFactory implements PVDisplay{
    public static PVDisplay create()
}

where

attach
Attempts to attach to pvField It returns (false,true) if it found an appropriate pvData structure. It looks first a pvField itself and if is not an appropriate pvData structure but the field name is value it looks to see if the parent structure has an appropriate sub structure.
detach
Just detaches from the pvData structure.
isAttached
Is there an attachment to a display structure?
get
Copies data from the pvData structure to a Display. An exception is thrown if not attached to a pvData structure.
set
Copies data from Display to the pvData structure. An exception is thrown if not attached to a pvData structure.
create
Create a PVDisplay instance. Attach must be called before get or set can be called.

Enumerated

An enumerated structure is a structure that has fields:

structure
    int32 index
    string[] choices

The following is the Java code for accessing an enumerated structure.

interface PVEnumerated {
    boolean attach(PVField pvField);
    void detach();
    boolean isAttached();
    boolean setIndex(int index);
    int getIndex();
    String getChoice();
    boolean choicesMutable();
    String[] getChoices();
    boolean setChoices(String[] choices);
}

class PVEnumeratedFactory{
    PVEnumerated create();
}

where

PVEnumerated
The default constructor. Attach must be called before any get or set method can be called.
attach
Attempts to attach to pvField It returns (false,true) if pvField (is not, is) an enumerated structure.
detach
Just detaches from the pvData structure.
isAttached
Is there an attachment to an enemerated structure?
setIndex
Set the index field in the pvData structure. An exception is thrown if not attached to a pvData structure.
getIndex
Get the index field in the pvData structure.
getChoice
Get the String value corresponding to the current index field in the pvData structure. An exception is thrown if not attached to a pvData structure.
choicesMutable
Can the choices be changed? Note that this is often true. An exception is thrown if not attached to a pvData structure.
getChoices
Get the array of choices. An exception is thrown if not attached to a pvData structure.
getNumberChoices
Get the number of choices. An exception is thrown if not attached to a pvData structure.
setChoices
Change the choices. An exception is thrown if not attached to a pvData structure.

Package org.epics.pvData.misc


Overview

This package provides utility code:

multiChoice
Support for a multiChoice structre, which is a structure containing two fields: choices and bitMask. choices is an array of strings and bitMask selects a subset of the choices.
Message Queue
Support for queuing messages to give to requesters.
Thread
ThreadCreate and Executor
TimeFunction
Time how long a function call requires.
LinkedList
A douuble linked list facility that requires the user to allocate a node. It is more efficient that ArrayList and does not require the implementation to allocate storage for the nodes.
Timer
An implementation of Timer that does not require an object to be created for each timer request.
BitSet
An implementation of BitSet that can be serialized.
Queue
A queue implementation.
Destroyable
A base interface for destroy.
Serialize
A helper for serialization.

MultiChoice

NOTE: This is not currently used by anything.

A MultiChoice consists of an array of strings and a bot set that selects an arbitrary set of the choices.

    public interface MultiChoice {
        interface Choices {
            String[] getChoices();
            int getNumberChoices();
        }
        byte[] getBitMask();
        String[] getChoices();
        Choices getSelectedChoices();
        void setBit(int index);
        void clear();
        int registerChoice(String choice);
    }

    public class MultiChoiceFactory{
         public static MultiChoice getMultiChoice(PVField pvField);
    }

where

getBitMask
Returns the bitMask.
getChoices
Returns the complete set of choices..
getSelectedChoices
Returns the interface for getting the selected choices..
setBit
Select the choice for specified bit..
clear
Clear the bitMask, i.e. no choices are selected..
registerChoice
Register a new choice. If thed choice already exists then it''s index is returned. If not it is appended to the choices.

BitSet

This is an implementation of BitSet that supports serialization, which the standard Jave implementation does not allow.

The following is also provided:

interface BitSetUtil {
    boolean compress(BitSet bitSet,PVStructure pvStructure);
}

class BitSetUtilFactory {
    public static BitSetUtil getCompressBitSet();
}

This provides functions that operate of a BitSet for a PVStructure. It currently has only one method:

compress
Compress the bits in a BitSet related to a structure.
For each structure:
  1. If the bit for the structure is set then the bit for all subfields of the structure are cleared.
  2. If the bit for the structure is not set but all immediate subfields have their bit set then the bit for the structure is set and the bits for all subfields are cleared.
Note that this is a recursive algorithm. That is if every immediate subfield has it's offset bit set then the bits for ALL fields that reside in the structure will be cleared.
Channel Access can call this before sending data. It can then pass entire structures if the structure offset bit is set.

Message Queue

Definitions

    public class MessageNode {
        public String message;
        public MessageType messageType;
    }

    public interface MessageQueue {
        MessageNode get();
        boolean put(String message,MessageType messageType);
        boolean isEmpty();
        boolean isFull();
        int getClearOverrun();
    }
    
    public class MessageQueueFactory {
        public static MessageQueue create(int size);
    }

MessageQueue

This is for use by code that wants to handle messages without blocking higher priority threads.

A messageNode is a class with two public data members:

message
The message.
messageType
The message type.

A messageQueue is an interface with methods:

put
Put a new message into the queue. False is returned if the queue was full and true otherwise.
isEmpty
Is the queue empty?
isFull
Is the queue full?
getClearOverrun
Get the number of times replaceFirst or replaceLast have been called since the last call to getClearOverrun. The internal counter is reset to 0.

MessageQueueFactory provides the public method:

create
Create a MessageQueue and return the interface.

Thread

ThreadPriority

    public enum ThreadPriority {
        lowest,
        lower,
        low,
        middle,
        high,
        higher,
        highest;
        
        public static final int[] javaPriority;
        public int getJavaPriority();
        public static int getJavaPriority(ThreadPriority threadPriority);
    }

ThreadCreate

    interface ThreadReady {
        void ready();
    }

    interface RunnableReady {
        void run(ThreadReady threadReady);
    }

    interface ThreadCreate {
        Thread create(String name, int priority, RunnableReady runnableReady);
        Thread[] getThreads();
    }
    
    public class ThreadCreateFactory {
        public static ThreadCreate getThreadCreate();
    }

ThreadCreate provides two features:

  1. Create does not return until ReadyRunnable.run calls ThreadReady.ready().
  2. A list of all active threads is kept. getThreads provides access to the list.

Executor

An Executor is a thread that can execute any object that implements the Java Runnable interface. The user can request that a single command be executed. If the command is already in the list of commands to be executed it is NOT added to the list when add is called.

    public interface ExecutorNode {}

    public interface Executor {
        ExecutorNode createNode(Runnable command);
        void execute(ExecutorNode executorNode);
        void stop();
    }
    public class ExecutorFactory {
        static public Executor create(String name,ScanPriority priority);
    }
where
createNode
Create a ExecutorNode that can be passed to execute.
execute
Request that command be executed. If it is already on the run list nothing is done.
stop
Stop the thread and refuse additional execute requests.

Time Function Call

TimeFunction is a facility that measures the average number of seconds a function call requires. When timeCall is called, it calls function in a loop. It starts with a loop of one iteration. If the total elapsed time is less then .1 seconds it increases the number of iterrations by a factor of 10. It keeps repeating until the elapsed time is greater than .1 seconds. It returns the average number of seconds per call.

    public interface TimeFunctionRequester {
        void function();
    }

    public interface TimeFunction {
        double timeCall();
    }

    public class TimeFunctionFactory {
        public static TimeFunction create(TimeFunctionRequester requester);
    }

Linked List

LinkedList implements a double linked list that requires a user to allocate the nodes. It is more efficent that ArrayList for implementing stacks and queues. For lists that are traversed while new elements can be added or removed, LinkedListArray provides a way to get an array of the currrent elements without allocating a new array each time the array is traversed.

LinkedListArray converts a LinkedList to an LinkNode array. The implementation provided by LinkedListFactory only creates a new LinkNode array w.en the number of elements in the linkedList passed to setNodes is greater than the length of the current LinkNode array.

    public interface LinkedListNode<T> {
        public T getObject();
        boolean isOnList();
    }   
    
    public interface LinkedList {
        void addTail(LinkedListNode<T> listNode);
        void addHead(LinkedListNode<T> listNode);
        void insertAfter(LinkedListNode<T> listNode,LinkedListNode<T> addNode);
        void insertBefore(LinkedListNode<T> listNode,LinkedListNode<T> addNode);
        LinkedListNode<T> removeTail();
        LinkedListNode<T> removeHead();
        void remove(LinkedListNode<T> listNode);
        void remove(T object);
        LinkedListNode<T> getHead();
        LinkedListNode<T> getTail();
        LinkedListNode<T> getNext(LinkedListNode<T> listNode);
        LinkedListNode<T> getPrev(LinkedListNode<T> listNode);
        boolean isEmpty();
        boolean contains(T object);
    }

    public interface LinkedListArray<T> {
        void setNodes(LinkedList<T> linkedList);
        LinkedListNode<T>[] getNodes();
        int getLength();
        void clear();
    }
    
    public class LinkedListCreate<T> {
        public static LinkedList<T> create();
        public static LinkedListNode<T> createNode(Object object);
        public static LinkedListArray<T> createArray();
    }

LinkedListCreate is created as follows:

    LinkedListCreate<SomeObject> linkedListCreate = new LinkedListCreate<SomeObject>();

Then a linked list can be created as follows:

    LinkedList<SomeObject> linkedList = linkedListCreate.create();

The only way a node can be allocated is by calling linkedListCreate.createNode(SomeObject object). The object passed to createNode is what is returned by LinkedList.getObject. For example:

    private static class SomeObject {
        private LinkedListNode<SomeObject> listNode = null;
        // other definitions


        private Node(/* arguments*/) {
            listNode = linkedListCreate.createNode(this);
        }

        LinkedListNode<SomeObject> getListNode() {return listNode};
    }

    // then

    SomeObject someObject = new SomeObject(/* args */);
    linkedList.addTail(someObject);

A node can only be on one list at a time but can be put, at different times, on different lists as long as they all hold the same type of objects.

The class does not provide a lock but the user can synchronized via calls like:

    synchronized(linkedList) {
        linkedList.addTail(listNode);
    }

The following is an example of how to use LinkedListArray.

        LinkedListNode<SomeObject>[] nodes = null;
        int length = 0;
        synchronized(linkedList) {
            linkedListArray.setNodes(linkedList);
            nodes = linkedListArray.getNodes();
            length = linkedListArray.getLength();
        }
        for(int i=0; i<length; i++) {
            SomeObject object = nodes[i].getObject();
            // do something with object
        }

Timer

This provides a general purpose timer. It provides the following features not provided by java.util.Timer and java.util.TimerTask:

  1. Priority
    The java.util implementation does not allow the user to specify the priority of the timer thread. This implementation does.
  2. TimerNode
    A java.util.TimerTask is not reusable. Once a timerTask has been canceled or a delay timerTask has run, the TimerTask can not be reused. Instead a new TimerTask must be created. A TimerNode can be reused.

Java Definitions

interface Timer {
    interface TimerCallback {
        void callback();
        void timerStopped();
    }
    interface TimerNode {
        void cancel();
        boolean isScheduled();
    }
    void scheduleAfterDelay(TimerNode timerNode,double delay);
    void schedulePeriodic(TimerNode timerNode,double delay,double period);
    void stop();
}

class TimerFactory {
    static public Timer create(String threadName, ThreadPriority priority);
    static public TimerNode createNode(TimerCallback timerCallback);
}

TimerCallback

This is an interface that must be implemented by the user. It has the following methods:

callback
This is called when a timer expires. This is called with no locks held. When called a delay timer is no longer on the queue but a periodioc timer is on a queue. Thus the callback for a delay timer can issue a new schedule request but a periodic timer must not. Note the explaination of TimerNode.cancel below.
timerStopped
Timer.stop was called when a timer request was queued. or if the timer is stopped and a schedule request is made.

TimerNode

This is an interface implemented by TimerFactory. It is allocated by calling TimerFactory.createNode. It is passed as an argument when scheduling a callback. It has the single method:

cancel
This is called to cancel a timer request. If a callback has been dequeued but the callback not called when cancel is called then a callback may still happen. New schedule requests can be made after a cancel request has been made.
isScheduled
Is this node in the timerQueue.

Timer

This is the interface for scheduling a timer callback. A timer is created by calling TimerFactory.create. It has the methods:

scheduleAfterDelay
A request to schedule a callback after a delay specified in seconds. If stop has been called then TimerCallback.timerStopped is called immediately.
schedulePeriodic
Schedule a periodic callback. If stop has been called then TimerCallback.timerStopped is called immediately.
stop
Stop the timer. The queue is emptied and TimerCallback.timerStopped is called for each element of the queue. Further schedule calls result in a call to TimerCallback.timerStopped. Once stopped a timer is no longer useful. A new timer must be created.

TimerFactory

This is the factory that implements the Timer and TimerNode interfaces. It has the methods:

create
Create a new Timer.
createNode
Create a TimerNode. A timerNode can be used to schedule any timer but a timeNode can only be on a single timer queue and only once on a given timer queue.

BitSet

This is adapted from the java.util.BitSet. It adds serializable. See java.util.BitSet for a description. This implementation adds the following additional methods:

    public boolean getAndSet(int bitIndex);
    public void set(BitSet src);
    public void or_and(BitSet set1, BitSet set2);

where

getAndSet
gets and sets the bit at bitIndex.
set
fast copy operation from src to this bitSet
or_and
The result is the or of the current bitSet with the value of set1.and set2. The number of bits is the Math.max(set1.bitsInUse,set2.bitsInUse);
    

Queue

This provides a queue which has an immutable capacity, which is specified when the queue is created. When the queue is full the user code is expected to keep using the current el;ement until a new free element becomes avalable. This is used by pvData.monitor.

public class QueueCreate<T> {
    public Queue<T> create(QueueElement<T>[] queueElements) {
        return new QueueImpl<T>(queueElements);
    }

    public QueueElement<T> createQueueElement(T object) {
        return new QueueElementImpl<T>(object);
    }
}

public interface QueueElement<T> {
    public T getObject();
}

public interface Queue<T> {
    void clear();
    int getNumberFree();
    int capacity();
    QueueElement<T> getFree();
    void setUsed(QueueElement<T> queueElement);
    QueueElement<T> getUsed();
    void releaseUsed(QueueElement<T> queueElement);
}

A queueCreate instance is created via a call like the following:

 QueueCreate<MyObject> queueCreate = new QueueCreate<MyObject>();

Once a queueCreate is available a queue instance is created via code like the following:

Queue<MyObject> queue create(MyObject[] myObjects) {
    QueueElement<MyObject>[] queueElements = new QueueElement[length];
    for(int i=0; i<length; i++) {
        QueueElement<MonitorElement> queueElement =
                 queueCreate.createQueueElement(myObjects[i);
        queueElements[i] = queueElement;
    }
    return queueCreate.create(queueElements);
}

The queue methods are:

clear
Make the queue empty.
getNumberFree
Get the number of fee elements in the queue.
capacity
Get the capacity, i.e. the maximun number of elements the queue can hold.
getFree
Get the next free element. Null is returned if no free elements are available. If a non null value is returned then the element belongs to the caller until setUsed is called.
setUsed
Set a queue element used. This must be the element returned by the last call to getFree.
getUsed
Get the next used element of null if no more used elements are available.
releaseUsed
Set a queue element free. This must be the element returned by the last call to getUsed.

A producer calls getFree and setUsed via code like the following:

   MyObject getFree() {
       QueueElement<MyObject> queueElement = queue.getFree();
       if(queueElement==null) return null;
       return queueElement.getObject();
  }

A consumer calls getUsed and releaseUsed via code like the following:

     while(true) {
         QueueElement<MyObject> queueElement = queue.getUsed();
         if(queueElement==null) break;
         MyObject myObject = queueElement.getObject();
         // do something with myObject
         queue.releaseUsed(queueElement);
     }

Destroyable

This is a base interface used by many other interfaces.

interface Destroyable  {
    void destroy();
}

where

destroy
Destroy the object.

Serialize

This is a helper class for serialization, which is required for sending and receiving pvData over the nerwork.

class SerializeHelper {
    static void writeSize(final int s, ByteBuffer buffer);
    static int readSize(ByteBuffer buffer);
    static void serializeString(final String value, ByteBuffer buffer);
    static void serializeSubstring(final String value,
         int offset, int length, ByteBuffer buffer);
    static String deserializeString(ByteBuffer buffer);
}

where

writeSize
Serialize the size.
readSize
Deserialize the size.
serializeString
Serialize a String.
serializeSubstring
Serialize a substring.
deserializeString
Deserialize a string.

Package org.epics.pvData.monitor


Overview

Project javaIOC provides the ability to monitor changes to an arbitrary subset of the fields in a record. The next section describes how a client specifies monitor options. The rest of this package description is of interest to developers. Although this package does not implement monitors it does describe what the client and what pvAccess needs to know about monitors.

A client can monitor an arbitrary subset of the fields in a record instance. Each field has an associated monitor algorithm which decides if changes to that field should cause a monitor. The algorithms defined by this package are: onPut, onChange, and deadband. The set of algorithms is extensible.

The basic interface for monitoring is interface Monitor, which has four methods: start, stop, poll, and release. Code that implements the client side for monitors has code like the following:

    //somewhere the following request is issued
    monitor.start();
    //somewhere the following is done:
    while(true) {
       MonitorElement monitorElement = monitor.poll();
       if(monitorElement==null) {
          // no elements in the queue. Do something.
       } else {
          // got a monitor. Handle the element and then
          monitor.release(monitorElement);
       }
    }

This package provides the following support for monitors:

  1. How a client specifies monitor options
  2. Implementing new monitoring algorithms.
  3. Monitor Queues
    This package provides a complete implementation of montor queues for pvData.

Developers who need to understand or implement monitor algorithms will be interested in this package. For example it is used by pvAccess to implement monitors. The interfaces can be implemented by other servers, for example the javaIOC (org.epics.ioc.caV3) implements a version of interface Monitor that links to a version 3 EPICS IOC.

This package defines the following monitor algorithms:

onPut
A monitor is issued whenever a put is issued to the field. This is the default unless the record defines deadbands for a field. An exception is the top level timeStamp which by default is made onChange and monitor will not be raised.
onChange
This provides two options: 1) A monitor is raised whenever a field changes value, and 2) A monitor will never be raised for the field.
deadband
The field must be a numeric scalar. Whenever the absolute or percentage value of the field changes by more than a deadband a monitor is issued. The record instance can also define deadbands.
periodic
A monitor is issued at a periodic rate if a put was issued to any field being monitored.

The monitor queue implementation provides support for the following queue sizes greater than or equal to two.

Standard Monitor Requests

This section describes that monitor support provided by project javaIOC. Two issues are discussed: record options and field options.

Record Options

The options that apply to an entire record are:

periodic
A record can ask to be monitored periodically by specifying a periodic rate. A monitor will be issued no faster that the rate specified. A periodic monitor does not use a monitor queue.
queueSize
Queue size can be specified to be >=1. A size of 1 means no queue but just keep a single copy of the data. A size of 2 is the default.

Field Options

The following options are supported for all fields:

shareData
Has a value of true or false and specifies if the server just transfer data from the record keep a copy. This option can save memory for array fields. Note that it is implemented by PVCopy.

For each field the following monitor algorithms are supported:

onPut
A monitor is issued when a put is issued to the field. This is the default if no algorithm is specified and the field is not the top level timeStamp.
onChange
This has two features depending on the value of causeMonitor. If causeMomitor is true (the default) then a monitor is raised only if the value has changed since the last monitor. If causeMonitor is false then this field will never cause a monitor. An example is a top level timeStamp field. If a client monitors the top level timeStamp then unless the client requests causeMonitor to be false, if the only change is to the timeStamp field no monitior will be issued.
deadband
This can only be specified for a numeric scalar field. Details follow:

Monitor Request Options

The examples will use the syntax supported by calling:

   PVStructure pvRequest = org.epics.ca.client.CreateRequestFactory.createRequest(request);

The request string has the following:

   "record[option=value,...]field(fieldDef,...)"

The standard record options are:

queueSize
The value must be >=1.
periodicRate
The value is the maximum rate in seconds for monitors.

Each fieldDef is either of the form:

    fullRecordName

Where fullRecordName is the full name of the field in the record. The name in the structure the client will get is the field name from the record. Or fieldDef has the form:

    fieldName{fieldDef,...}
    

Each fieldDef is of the form:

    fullFieldName[option=value,...]

where fullFieldName is of the full field name of the record field and the options define the monitor options.

Monitor options are identified via the following option.

    algorithm=name
    

where name is the algorithm name. For the algorithms implemented by pvData this is one of: onPut, onChange, or deadband.

onPut is the default so it does not have to be specified.

onChange accepts one additional option:

    causeMonitor=value

where value must be "true" or "false". The default is true.

deadband accepts the following additional options:

    deadband=deadbandValue
    type=typeValue
    isPercent=isPercentValue

where

deadbandValue
The change in value (or percentage change) that causes a monitor. If not specified the deadband is taken from the record if the record defines it. If specified and the record also defines it the minimum value is taken.
typeValue
Must be "display" or "archive". This says if the record deadband is taken from display or from archive. The default is display.
isPercentValue
Must be true or false. The default is false. If true the deadband is a percentage change otherwise it is an absolute change.

Record Deadband

NOTE: This applys to records in a javaIOC.

Any numeric scalar field in a record instance can optionally have an associated deadband structure, which is defined as follows:

<structure structureName = "deadband">
    <structure name = "display">
         <scalar name = "isPercent" scalarType = "boolean"/>
         <!-- if true than percentage change. If false than absolute change -->
         <scalar name = "value" scalarType = "double"/>
         <!-- the deadband for display -->
    </structure>
    <structure name = "archive">
         <scalar name = "isPercent" scalarType = "boolean"/>
         <!-- if true than percentage change. If false than absolute change -->
         <scalar name = "value" scalarType = "double"/>
         <!-- the deadband for archive -->
    </structure>
</structure>

As an example:

<record name = "example">
    <scalar name = "value" scalarType = "double">
    <structure name = "deadband" extends = "deadband">
       <structure name = "display">
          <deadband>.1</deadband>
       </structure>
       <structure name = "archive">
          <deadband>.3</deadband>
       </structure
    </structure>
    <!-- other stuff -->
</record>

NOTE: If an algorithm is not defined by the user for a field and a deadband is defined for the record than the algorithm is made to be deadband and the display deadband is used as the deadband.

Examples

The following:

    String request = "alarm,timeStamp,power.value";
    PVStructure pvRequest = org.epics.ca.client.CreateRequestFactory.createRequest(request);

Creates a pvRequest that monitors the alarm, timeStamp, and power.value fields of a record. The client will receive a structure with the fields named alarm,timeStamp, and value. Since the client has not specified any options the defaults will be used. This means:

The following:

    String request = "timeStamp[algorithm=onChange,causeMonitor=true]";
    PVStructure pvRequest = org.epics.ca.client.CreateRequestFactory.createRequest(request);

Will cause monitior whenever the timeStamp is changed. This is a way to be notified whenever a javaIOC record is processed because this is normally the only time the timeStamp is changed.

The following:

    String request =
        "alarm,timeStamp,value{power.value[algortithm=deadband,isPercent=true,deadband=1.0}";
    PVStructure pvRequest = org.epics.ca.client.CreateRequestFactory.createRequest(request);

This requests a monitor whenever and alarm is raised or when the power.value changes by at least 1 percent.

Monitor Interfaces

Monitor

This is the interface implemented by a factory that implements a monitoring algorithm

interface Monitor extends Destroyable {
    void start();
    void stop();
    MonitorElement poll();
    void release(MonitorElement monitorElement);
}

where

destroy
destroy the monitor, i.e. release whatever resources are used by the implementation.
start
Start monitoring. An initial monitor with all data modified must be generated.
stop
Stop monitoring.
poll
Called to get modified data. If no data has been modified since the last poll null is returned.
release
release the element returned by the most recent poll request.

MonitorElement

This is the interface returned by a call to poll.

interface MonitorElement  {
    PVStructure getPVStructure();
    BitSet getChangedBitSet();
    BitSet getOverrunBitSet();
}

where

getPVStructure
The data structure.
getChangedBitSet
The bitSet showing which fields of the PVStructure have changed since the previous poll request.
getOverrunBitSet
the bitSey which shows which fields have been modified more than once since the last poll request.

MonitorRequester

This is the interface implemented by the code that request a monitor.

interface MonitorRequester extends Requester {
    void monitorConnect(Monitor monitor, Structure structure);
    void monitorEvent(Monitor monitor);
    void unlisten();
}

where

monitorConnect
The monitor has connected to a PVRecord.
monitorEvent
A monitor event has occured. Monitor.poll is called to get the data.
unlisten
The server has been told to disconnect from the record. The MonitorRequster can no longer issue any requests except destroy until monitorConnect is again called.

MonitorAlgorithm

The following are implemented by code that implements a monitor algorithm.

interface MonitorAlgorithm  {
    String getAlgorithmName();
    boolean causeMonitor();
    void monitorIssued();
}

where

getAlgorithmName
return the name of the algorithm.
causeMonitor
Called to see if a change to this field should cause a monitor
monitorIssued
Called after a monitor has been issued.

MonitorQueue

This is the interface implemented by MonitorQueueFactory.

interface MonitorQueue  {
    void clear();
    int getNumberFree();
    int capacity();
    MonitorElement getFree();
    void setUsed(MonitorElement monitorElement);
    MonitorElement getUsed();
    void releaseUsed(MonitorElement monitorElement);
}

class MonitorQueueFactory {
    static MonitorQueue create(MonitorElement[] monitorElements);
}

where

clear
Set all elements of the queue free.
getNumberFree
Get the number of free queue elements.
capacity
Get the number of queue elements.
getFree
Get a free queue element. If no free elements null is returned.
setUsed
Set the latest element returned by getFree to used.
getUsed
Get the oldest used element. Null is returned when there are no more used elements.
releaseUsed
release the element returned by the latest call to getUsed.

Package org.epics.pvData.accessSecurity


This package describes Access Security. Acceses security is not currently implemented so this section just discusses ideas of what access security should be.

Outstanding Issues

This section is temporary. It will be removed after access security is implemented.

access security level

Epics Access Security had the concept of field level with only two levels allowed. For pvData more than two levels are allowed. In addition names can be associated with levels via inteface:

interface AccessSecurityLevel {
    String getName(int level) throws IndexOutOfBoundsException;
    int getLevel(String name) throws NoSuchFieldException;
    String[] getNames();
}

A default implementation provides the following definition for names:

   String[] names = {"internal","configuration","calibration","runtime"};

Another implementations can also be provided but any implementation should define levels starting with 0. Level 0 should be acceptable as the default.


Overview


Access Security limits access to A PVData database. Access Security for a PVData database is similar to the EPICS V3 access security. The requirements for access security were generated at ANL/APS in 1992. The requirements document is: Channel Access Security Requirements The V3 implementation is described in the V3 Application Developer's Guide. In that document read chapter "Access Security".

This document discusses the following:


Quick Start


TBD. This section will describe how to start access security for a javaIOC.


Features and Limitations


Features

Access security protects IOC databases from unauthorized PVAccess Clients. Access security is based on the following:

Each record is assigned an assess security group or is put by default into a group named "default".

Each field of each record is assigned an access security level which is an integer that is greater than or equal to 0. Level 0 is the highest security level. If a client has access for level n then the client also has access for all levels greater than n. In addition names can be associated with the levels, which is implemeted via interface AccessSecurityLevel. A default implementation is provided which defines the names "internal","configuration","calibration" and ,"runtime". Other implementations can also be created but a particular site should define and use a common implementation.

NOTE: The definion of access security level is an extension to what V3 epics supports. It allows only two levels: 0 and 1 with 1 being the highest security level.

Limitations

PVAccess can be enhanced to implement authentication for udp and tcp access to a PVData database but this is not the responsibility of access security. The configuration file, however, provides sytax that allows for authentication.

A PVData database can be accessed only via PVAccess or via a shell. It is assumed that access to the shell is protected via physical security and standard networking and physical security methods.

No attempt has been made to protect against the sophisticated saboteur. Network security methods must be used to limit access to the subnet on which the iocs reside.


Access Security Configuration File


This section describes the format of a file containing definitions of the user access groups, host access groups, and access security groups. Lets first give a simple example and then a complete description of the syntax.

Simple Example

<accessSecurity>
  <credentials>
    <credential name="user">[publickey,sharedkey]</credential>
    <credential name="host">void</credential>
    <!-- an arbitrary number of credentials can follow-->
    <plugin name="publickey"></plugin>
    <plugin name="sharedkey"></plugin>
  <credentials>
  <groups>
      <group name = "users" credential = "user">[user1,user2]</group>
      <group name = "hosts" credential = "host">[host1,host2]</group>
  </group>
  <access>
     <group name = "default">
        <rule>
           <right>[read]</right>
        </rule>
        <rule>
           <right>[read,write,process]</right>
           <group>users.AND.hosts</group>
        </rule>
      </group>
   </access>
</accessSecurity>

These rules provide read access to anyone located anywhere and read,write, and process access to user1 and user2 if they are located at host1 or host2.

Syntax Definition

An access security configuration file must start and end as follows:

<?xml version="1.0" ?>
<accesssecurity>
   <!-- credentials, groups, states, access definitions -->
</accesssecurity>

??? Should accesssecurity tag define xmlns or xsd ??

Between the accessSecurity tags the following definitions appear:

  
  <credentials>
     <credential name="name">[method,....method]></credential>
     <!-- will look for plugin that supports method in the order specified,
       first plugin that grants access means access is allowed -->
     <!-- an arbitrary number of credentials can follow-->
     <plugin name="method">
         <!-- data for plugin. Syntax TBD -->
     </plugin>
     <!-- an arbitrary number of plugins can follow-->
  </credentials>
  <groups>
      <group name="name" credential="name">[name,...,name]</group>
      <!-- an arbitrary number of groups can be defined -->
  </groups>
  <states>
      <state name = "name">
         <provider>name</provider>
         <!-- The channel provider -->
         <pvname>name</pvname>
         <!-- name must be a record that has a top level enumerated field named value -->   
      </state>
      <!-- an arbitrary number of states may appear -->
  </states>
  <access>
     <group name = "name">
        <rule accessSecurityLevel = "name" >
            <right>[right,...,right]</right>
            <state name = "name">[state,...,state]</state>
            <group>expression</group>
            <!-- expression chooses a group from groups above-->
        </rule>
        <!-- an arbitrary number of rules may appear -->
     </group>
     <!-- an arbitrary number of groups can be defined -->
  </access>
  <log>
     <group name = "name">
        <rule accessSecurityLevel = "name" >
            <right>[right,...,right]</right>
            <state name = "name">[state,...,state]</state>
            <group>expression</group>
            <!-- expression chooses a group from groups above-->
        </rule>
        <!-- an arbitrary number of rules may appear -->
     </group>
     <!-- an arbitrary number of groups can be defined -->
  </log>
credentials

Credentials allow group names to be verified by PVAccess. All credential definitions are enclosed in a single credentials xml element. An arbitrary set of credential and plugin elements can appear within credentials. A credential has an array of methods. The implementation looks for plugins in array order and asks the plugin if the client is valid

A credential has a name and holds an array of method names. A method is used on the server for authentication For example "void" just uses the response from the client, "sharedkey" does some real work, "client-ip" yields the client's IP number from PVAccess.

groups

groups provide a way to combine a set of names with a single name. For example all accelerator physicists can be combined into a single group named acceleratorPhysist. Another example is that all workstations in the control room can be combined in a group named controlRoom.

A group has a name, selectes a credential, and contains a set of names.

states

states alow rules to apply only when something is or is not in a particular state. A single xml element "states" holds all possible states.

Each state has a name, which is referenced in access rules, and has two sub-elements: provider and pvname:

provider
This is the name of the PVAccess provider. At least the following are supported: local, PVAccess, and CAV3.
pvname
The name of the channel to which the provider will connect. For local and PVAccess this must be a channel that has a top level field named "value" and must be an enumerated structure. For CAV3 the VAL field must be type DBR_ENUM. In either case if the record is in alarm or the channel fails then the result is failure.
access and access group

access contains definitions for an arbitrary number of access groups. Each access group defines an arbitrary number of rules. Each record being proctected by access security is assigned to an accesss security group. Thus each record instance belongs to a single access group. The default group has the name "default", which is the group for all records that do not have the accessGroup defined. Unlike a V3 access group the access group name will not be available via the PVRecord interface but will be assigned and kept within the access security system itself.

Each access group has a name, which selects the set of records to which the access group applies. The group contains an arbitrary set of rule definitions.

A rule optionally has an attribute accessSecurityLevel, which chooses an access security level. If the access security level is not specified then level 0 is assumed. The value for accessSecurityLevel is the name associated with the level. Within a rule the following elements can appear:

right
The access rights which is any combination of read, write, process or else just none. If not specified no access is allowed..
state
This refers to a state definition from states. If the current state is one of the states in the array then the rule is valid.
group
This selects groups from the groups defined above. An expression that can use the following operators: '(', ')', '.OR.', '.AND.', and '.NOT.'. Each variable is a name of a group defined in groups. The syntax for the expression follows the same precedence rules as Java. Note that all arguments are of the form "Is the client part of the group defined in groups" The result of every operation is of type boolean. The final result is either true or false meaning that the rule (does not, does) apply.

The access privilege for a client is determined as follows:

  1. The access group associated with the record is searched.
  2. Each rule for that access group is checked for the following:
  3. The maximum access right allowed by step 2 is the access right chosen.
log and log group

The definitions are similar to those for access and access group. But these are differences:


Examples


Credentials

The following:

<credentials>
    <credential name="user">[publickey,sharedkey]</credential>
    <credential name="host">void</credential>
    <credential name="ip">pva-client-ip</credential>
    <!-- an arbitrary number of credentials can follow-->
    <plugin name="publickey">
        <!-- data for plugin. Syntax TBD -->
    </plugin>
    <plugin name="sharedkey">
        <!-- data for plugin. Syntax TBD -->
    </plugin>
    <plugin name="pva-client-ip">
        <!-- data for plugin. Syntax TBD -->
    </plugin>
</credentials> 

The example defines three credentials that may be used in the rules: user, host, and ip address. The publickey and sharedkey plugins are used (tried in that order) to authenticate users, the client may just declare its host name without further checking, and a PVAccess call is used to determine the numerical IP of the client.

Groups

<groups>
   <group name = "operator" credential = "user">[op1,op2,superguy]</group>
   <group name = "supervisor" credential = "user">[superguy]</group>
   <group name = "physicist" credential = "user">[joe,bill,sheng]</group>
   <group name = "controlRoom" credential = "host">[crhosr0,crhost1,crhost2]</group>
   <group name = "ioc" credential = "ip">[192.168.143.0/24,192.168.144.1,192.168.144.2]</group>
</group>

States

The following:

<states>
    <state name = "ringOpState">
        <provider>CAV3</provider>
        <pvname>OpIOC:ringOpState</pvname>
    </state>
    <state name = "linacOpState">
        <provider>CAV3</provider>
        <pvname>OpIOC:linacOpState</pvname>
    </state>
</states>

Defines states for a storage ring and for the linac. The choices for each state are determined by the site. As an example they could be something like: "offline", "standy", and "operational"

Access Rules

The following"

<access>
   <group name = "default">
       <rule>
          <right>[read]</right>
       </rule accessSecurityLevel = "runtime">
       <rule>
          <right>[read,write,process]</right>
          <group>(operator|physicist).AND.controlRoom</group>
       </rule>
       <rule>
          <right>[read,write]</right>
          <state name = "ringOpState">[offLine]</state>
          <group>ringDeveloper.AND.controlRoom</group>
       </rule>
</access>

Allows access to records in the default access security group as follows:

Logging Rules

The following:

<log>
   <group name = "default">
       <rule>
            <right>[write,process]</right>
            <group>.NOT.((operators.OR.physicists).AND.controlRoom)</group>
       </rule>
       <rule>
            <right>[write,process]</right>
            <state name = "ringOpState">[offLine]</state>
            <group>.NOT.ringDevelopers</group>
       </rule>
</log>

This will log all write and process requests to all fields of all records in access group default except:


Linac Example


Lets design a set of rules for a Linac. Assume the following:

  1. Anyone can have read access to all fields at anytime.
  2. Linac application developers, located in the control room, have read, write, and process access to all fields only if the Linac in development.
  3. Operators, located in the control room, can have read, write, and process access to fields that have access security level corresponding to runtime.
  4. The operations supervisor, linac supervisor, and the application developers can have read,write, and process access to all fields but must have some way of not changing something inadvertently.
  5. Most records use the above rules but a few (high voltage power supplies, etc.) are placed under tighter control. These will follow rules 1 and 4 but not 2 or 3. These records will be in access security group "permit".
  6. IOC PVAccess clients always have read, write, and process access to all fields of all records except those in group permit.

Most Linac IOC records will not have the ASG field defined and will thus be placed in group default.

The following records will have an ASG defined:

The following access configuration satisfies the above rules.

<credentials>
    <credential name="user">[publickey,sharedkey]</credential>
    <credential name="host">void</credential>
    <credential name="ip">pva-client-ip</credential>
    <!-- an arbitrary number of credentials can follow-->
    <plugin name="publickey">
        <!-- data for plugin. Syntax TBD -->
    </plugin>
    <plugin name="sharedkey">
        <!-- data for plugin. Syntax TBD -->
    </plugin>
    <plugin name="pva-client-ip">
        <!-- data for plugin. Syntax TBD -->
    </plugin>
</credentials> 
<groups>
   <group name = "op" credential = "user">[op1,op2,superguy]</group>
   <group name = "opSup" credential = "user">[superguy]</group>
   <group name = "linac" credential = "user">[waw,nassiri,grelick,berg,fuja,gsm]</group>
   <group name = "linacSup" credential = "user">[gsm]</group>
   <group name = "appDev" credential = "user">[nda,kko]</group>
   <group name = "cr" credential = "host">[mars,hera,gold]</group>
   <group name = "ioc" credential = "ip">
       [192.168.143.0/24,192.168.144.1,192.168.144.2]
   </group>
</groups>
<states/>
   <state name = "opstate">
      <provider>CAV3</provider>
      <pvname>LI:OPSTATE</pvname>
   </state>
   <state name = "permit">
      <provider>CAV3</provider>
      <pvname>LI:permit</pvname>
   </state>
</states>
<access>
   <group name = "default">
      <rule accessSecurityLevel = "runtime">
         <right>[read,write,process]</right>
         <state name = "opstate">[operational]</state>
         <group>op.AND.cr</group>
         
      </rule>
      <rule>
          <right>[read,write,process]</right>
          <state name = "opstate">[development]</state>
          <!-- during development operators do not have access -->
          <group>(opSup.OR.linacSup.OR.appdev).AND.cr</group>
      </rule>
      <rule>
          <right>[read,write,process]</right>
          <state name = "permit">[true]</state>
          <group>(opSup.OR.linacSup.OR.appdev).AND.cr</group>
      </rule>
      <rule>
          <right>[read]</right>
      </rule>
      <rule>
         <right>[read,write,process]</right>
         <group>ioc</group>
      </rule>
   </group>
   <group name = "critical">
       <rule>
         <right>[read,write,process]</right>
         <state name = "permit">[true]</state>
         <group>(opSup.OR.linacSup.OR.appDev)</group>
      </rule>
      <rule>
         <right>[read]</right>
      </rule>
      <rule>
         <right>[read,write,process]</right>
         <group>ioc</group>
      </rule>
   </group>
   <group name = "permit">
      <rule>
         <right>[read,write,process]</right>
         <group>opSup</group>
      </rule>
      <rule>
          <right>[read]</right>
      </rule>
    </group>
</access>

Design Summary


Summary of Design Requirements

A brief summary of the Functional Requirements is:

  1. Each field of each record type is assigned an access security level.
  2. Each record instance is assigned to a unique access security group.
  3. Each user is assigned to one or more user access groups.
  4. Each host is assigned to a host access group.
  5. For each access security group a set of access rules can be defined. Each rule specifies:

Additional Requirements

Performance
Although the functional requirements does not mention it, a fundamental goal is performance. The design provides almost no overhead during normal database access and moderate overhead for the following: PVAccess client/server connection, ioc initialization, a change in value of a process variable referenced by an access calculation, and dynamically changing a records access control group. Dynamically changing the user access groups, host access groups, or the rules, however, can be a time consuming operation. This is done, however, by a low priority thread and thus does not impact normal record processing.
Generic Implementation
Access security should be implemented as a stand alone system, i.e. it should not be imbedded tightly in database or PVAccess. For example it should be usable by a gateway as well as by a javaIOC.
No Access Security within a javaIOC
Within an IOC no access security is invoked. This means that database links and local PVAccess clients calls are not subject to access control. Also test routines such as dbgf should not be subject to access control.
Defaults
It must be possible to easily define default access rules.
Access Security is Optional
When a javaIOC is initialized, access security is optional.
process
Since PVAccess allows the client to request processing ( For V3 this was determined by record type database definition), the access rights must include process in addition to read and write.

Implementation Overview


TBD.


Java IOC Support


TBD. The plan is to start/restart access security via record instances that have support that interfaces to access security


PVAccess Support


TBD.


Logging PVAccess Puts


TBD.