EPICS pvDataJava

Release 5.1-DEV - 2016.03.18

Editors:
Marty Kraimer, BNL
Dave Hickin, Diamond

Abstract

pvDataJava is a computer software package for the efficient storage, access, and communication, of structured data. It is specifically the Java implementation of pvData, which is one part of the set of related products in the EPICS V4 control system programming environment:
relatedDocumentsV4.html

Status of this Document

This is the 18-March-2016 version for the 5.1-development release of the Java implementation of pvData.

RELEASE_NOTES.md provides changes since the last release. TODO.md describes things to do before the next release.

Table of Contents

Introduction

pvData is one of a set of related packages in the EPICS V4 core software. It describes and implements the data management system to which the the other projects interface.

pvData (Process Variable Data) defines and implements an efficient way to store, access, and communicate memory resident data structures.

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 provided 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.
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. image data, 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 defines data as structures with subfields where each each subfield has a name and type. The types are defined next.

The pvData types are:

scalar
A scalar field has one of the following ScalarTypes:
pvBoolean
false or true.
pvByte, pvShort, pvInt, pvLong
Signed integers of size 8,16,32,64 bits.
pvUByte, pvUShort, pvUInt, pvULong
Unsigned integers of size 8,16,32,64 bits.
pvFloat, pvDouble
IEEE floating point numbers of size 32 and 64 bits.
pvString
In Java a String and in C++ std::string
scalarArray
A scalar array field (of type scalarArray) is a one-dimensional array where each the element has the same ScalarType.
structure
A structure field (of type structure) is an ordered set of fields where each field has a name and type. Since a field can have type structure, structures of arbitrary complexity can be created.
structureArray
A structure array field (of type structureArray) is an array of structures where each element has the same structure introspection interface.
union
A union stores a single subfield with a variable type. A variant union allows the type to be any type. A regular union allows only a fixed set of types.
union array
A union array is an array where each element is a union, i.e. it stores a single subfield. Each element has the same introspection interface. Different elements can have different types but each must have a type determined by the union interface.

pvData contains everything required by pvAccess to create and pass data between pvAccess 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 meta language is used to describe both introspection interfaces and data interfaces.

The toString, introspection and data methods described below present data in a format similar to the metadata syntax. The C++ implementation uses streams I/O instead of toString but it implements the same metadata syntax.

Definition

PVData supports structured data. All data appears via top-level structures.

Introspection objects

The introspection object of a structure has an ordered set of fields and is of the form

structure
    { <field> }0+
Each <field>is of the general form:

<fieldType> <fieldName>[ // <comment>]
    [ <additionalInfo> ]

where angle brackets (<>) denote a choice, square brackets ([]) denote that the contents may or may not be present. Here:

<fieldType>
is described below
<fieldName>
is a string. Each subfield in a structure must have a different field name.
//
indicates the the rest of the line is a comment
<comment>
the comment
<additionalInfo>
depends on <fieldType>
Each field of a structure has has a pvData type - scalar, scalarArray, structureArray, structure, union or unionArray - and in each case <field> is one of the following based on the type:

scalar

A scalar field is of the form:

<fieldType> <fieldName>[ // comment]

where <fieldType> can be any of the following:

boolean, byte, short, int, long, ubyte, ushort, uint, ulong, float, double or string

For example:

double value // the value of the PV
scalarArray
A scalar array field is of the form:
<fieldType> <fieldName>[ // comment]

where <fieldType> can be any of the following:

boolean[], byte[], short[], int[], long[], ubyte[], ushort[], uint[], ulong[], float[], double[] or string[]

(In arrays square brackets should be taken literally.)

For example:

int[] dim // the dimensions of the matrix
structure
A structure field has a similar definition to a top-level structure:
structure <fieldName>[ // comment]
    { <field> }0+ 

Each <field> is a field as per a top-level structure.

For example:

structure value // the value of the enumerated type PV
    int index
    string[] choices

If a structure has been previously defined

xxx_t :=

structure
    field1
    ..
    fieldN
then a structure field introspection object can be of the form
xxx_t fieldName[ // comment]

For example, if we define

enum_t :=

structure
    int index
    string[] choices

then the same field above can be specified

enum_t value // the value of the enumerated type PV
structureArray
A structureArray field has the definition:
structure[] <fieldName>[ // comment]
    <structureObject>

where <structureObject> is any top-level introspection object.

Or for a previously defined structure introspection object xxx_t a structureArray field can be

xxx_t[] fieldName
    xxx_t

For example:

structure[] value // the value of the enumerated type array PV
    structure
        int index
        string[] choices

or

enum_t[] value // the value of the enumerated type array PV
    enum_t

Thus a structure array is an array where each element is a structure but all elements have the same introspection interface.

union

This has two flavors: a variant union or a union of a fixed set of types (regular union). A variant union can store single field that can be of any type. A regular union can store a single field of a type that is from a fixed set of types and a name associated with the type.

A variant union field introspection object has the form:

any <fieldName>[ // comment]

for example

any value

and a regular union is of the form

union
    {<fieldDef>}1+

for example

union value // a heterogeneous attribute
    int intValue
    double doubleValue
    string stringValue
unionArray

This is an array of unions. Each element has the same introspection interface but the subfield of each element can have a different type. So either element each element is a variant union or each element is a regular union with the same choice of fields. A variant union array is of the following form:

any[] <fieldName>
    any

and a regular union array is of the form

union[] <fieldName>[ // comment]
     union
        {<field>}1+

for example

union value // a heterogeneous attribute
    int intValue
    double doubleValue
    string stringValue
    enum_t enumValue

Note that the indentation of each line behaves as you might expect: A top-level structure's fields are indented. A structure field is indented relative to the top-level and its fields will be indented relative to it and so on for each structure field. The leaf fields of the resulting top-level structure will then be indented by the sum of all the indentation for each structure above it. The size of indentation is unimportant (as long as it is non-zero). Examples are given below. [It may help to think of the indentation of nested if statements in Python.]

Data objects

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 top-level structures the data object is the same as for an introspection object except for the fields introspection objects are replaced by data objects as follows:

scalar

For scalar fields the data object is of the form

<fieldType> <fieldName> <value> [ // comment]

<value> is a valid value for <fieldType>

boolean
The value must be true or false
byte,...,ulong
Any valid integer or hex value, e.g. 3 and 0x0ff are valid values
float,double
Any valid integer or real e.g. 3, 3.0, and 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 example

double value 3.14
scalarArray

For scalar array fields the data object is of the form

<fieldType> <fieldName> <value> [ // comment]

where <value> is a comma-separated set of values enclosed in []:

[<value1>,...,<valueN>]

For example

int[] dim [2,2]

Here each value is a valid scalar data value depending on the type (the square brackets,[], are to interpreted literally here). White space is permitted surrounding each comma.

structureArray

For structure arrays the data object is of the form:

structure <fieldName>[ // comment]
    { <structureDataObject> }0+

where each <structureDataObject> is a (top-level) structure data object corresponding to the structure introspection object that forms part of the structure array introspection object.

For example

structure[] value
    structure 
        int index 0
        string[] choices [Off,On] 
    structure 
        int index 1
        string[] choices [Off,On]
union

For unions only a single stored subfield will appear. So it will be of the form

any <fieldName>[ // comment]
    <dataObject>

for variant unions and

union <fieldName>[ // comment]
    <dataObject>

for regular unions. For example

any 
    double  3.14

and

union value
    int intValue 42

respectively. Note the variant union stores a top-level object (so like a top-level structure) its field name is empty.

unionArray

For a union array a single subfield will appear for each element.

For variant unions arrays the data object is of the form

any[] <fieldName>[ // comment]
    { <variantUnionObject> }0+

where in each case <variantUnionDataObject> is a variant union data object as described above.

For regular union arrays the data object is of the form

union[] <fieldName>[ // comment]
    { <regularUnionDataObject> }0+

where in each case <regularUnionDataObject> is a regular union data object as described above.

The following are examples corresponding to the introspection objects of the previous section

any[] value

any[] value
    any 
        double  3.14
    any 
        int  42

union[] value

union[] value
    union
        int intValue 42
    union
        enum_t enumValue
            int index 1
            string[] choices [Off,On]

structure
structure subfields behave similarly to top level structure. A data object will be of the form

structure <fieldName>[ // comment]
    { <dataObject> }0+

where in each case <dataObject> is a data object for the corresponding field in the structure introspection object.

For example for the field

structure value
   int index 
   string[] choices

the data object could be

structure value
   int index 1
   string[] choices [Off,On] 

Data objects can also be expressed in terms of pre-defined structures, in which case, unlike for introspection objects, the subfields appear (in order to accompany the values):

enum_t value
   int index 1
   string[] choices [Off,On] 

Examples

Given the introspection object

structure
    double value
    structure timeStamp
        long secondsPastEpoch
        int  nanoseconds
        int userTag

the following corresponding data object can be defined:

structure
    double value 1.0
    structure timeStamp
        long secondsPastEpoch 1426259294
        int nanoseconds 520088807
        int userTag 0

If the following structure is defined:

time_t :=

structure
    long secondsPastEpoch
    int nanoseconds
    int userTag

then the following introspection object can be defined:

structure
    double value
    time_t  timeStamp

The following data object can also be defined:

structure
    double value 1.0
    time_t timeStamp
         long secondsPastEpoch 0
         int  nanoseconds 0

Given the introspection object

structure 
    structure begin
        double x
        double y
    structure end
        double x
        double y

structure
    structure[] points
        structure point
            double x
            double y

the following corresponding data objects could be defined:

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

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

If the following structure is defined:

point_t :=

structure
    double x
    double y

then the following introspection objects can be defined:

lineExample := 

structure
    point_t begin
    point_t end

pointArrayExample := 

structure
    point_t[] points
    

and the following data objects can be defined:

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

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

Interface Definitions

This section gives a brief description of the pvData introspection and data interfaces. In this section the methods are shown but not described. See org.epics.pvdata.pv below for a description of each method.

Types

The following are the type definitions:

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

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.
union
A union stores a single subfield with a variable type. A variant union allows the type to be any type. A regular union allows only a fixed set of types
unionArray
A union array is an array of union fields where each element has the same introspection interface. Different elements can have different types but each must have a type determined by the union interface.
enum ScalarType {
    pvBoolean,
    pvByte,pvShort,pvInt,pvLong,
    pvUByte,pvUShort,pvUInt,pvULong,
    pvFloat,pvDouble,
    pvString;
    // The following are convenience methods
    public boolean isInteger();
    public boolean isUInteger();
    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
ubyte
An 8 bit unsigned byte
ushort
16 bit unsigned integer
uint
32 bit unsigned integer
ulong
64 bit unsigned 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.

NOTE: Java does not support unsigned integers so the Java implementation of each unsigned type is implemented as the corresponding signed type. The only exception is the Convert facility. When it performs a widening operation, e.g. from ubyte to short, it performs the correct conversion.

Introspection Interfaces

Field is the base introspection interface. It has only an ID and a type.

interface Field extends Serializable {
    String getId();
    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);
    Field getField(int fieldIndex);
    <T extends Field> T getField(Class<T> c, String fieldName);
    <T extends Field> T getField(Class<T> c, int fieldIndex);
    Field[] getFields();
    String[] getFieldNames();
    int getFieldIndex(String fieldName);
    String getFieldName(int fieldIndex);
}

interface StructureArray extends Field {
    Structure getStructure();
}

public interface Union extends Field{
    public static final String DEFAULT_ID = "union";
    public static final String ANY_ID = "any";
    Field getField(String fieldName);
    Field getField(int fieldIndex);
    <T extends Field> T getField(Class<T> c, String fieldName);
    <T extends Field> T getField(Class<T> c, int fieldIndex);
    Field[] getFields();
    String[] getFieldNames();
    int getFieldIndex(String fieldName);
    String getFieldName(int fieldIndex);
    boolean isVariant();
}

public interface UnionArray extends Field{
    Union getUnion();
}

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:

interface PVField extends  Serializable {
    String getFieldName();
    String getFullName();
    int getFieldOffset();
    int getNextFieldOffset();
    int getNumberFields();
    boolean isImmutable();
    void setImmutable();
    Field getField();
    PVStructure getParent();
    void postPut();
    void setPostHandler(PostHandler postHandler);
    void toString(StringBuilder buf);
    void toString(StringBuilder buf,int indentLevel);
    String toString();
    // The following should go away.
    PVAuxInfo getPVAuxInfo();
}

Each scalar type has an associated data interface: PVBoolean, PVByte, PVShort, PVInt, PVLong, PVUByte, PVUShort, PVUInt, PVULong, 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 {
    Array getArray();
    int getLength();
    void setLength(int length);
    int getCapacity();
    void setCapacity(int length);
    boolean isCapacityMutable();
    void setCapacityMutable(boolean isMutable);
}

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

PVStructureArray is the interface for an array of structures where each element has the same introspection 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 PVStructure extends PVField , BitSetSerializable{
    Structure getStructure();
    PVField[] getPVFields();
    PVField getSubField(String fieldName);
    PVField getSubField(int fieldOffset);
    <T extends PVField> T getSubField(Class<T> c, String fieldName);
    <T extends PVField> T getSubField(Class<T> c, int fieldOffset);
    // The following are convenience methods
    // Note that they are no longer needed because of above generic 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);
    PVUnion getUnionField(String fieldName);
    PVUnionArray getUnionArrayField(String fieldName);
    public boolean checkValid();
}

Introspection and Data creation

The following interface creates introspection instances:

public interface FieldCreate {
    FieldBuilder createFieldBuilder();
    Scalar createScalar(ScalarType scalarType);
    BoundedString createBoundedString(int maxLength);
    ScalarArray createScalarArray(ScalarType elementType);
    ScalarArray createFixedScalarArray(ScalarType elementType, int size);
    ScalarArray createBoundedScalarArray(ScalarType elementType, int bound);
    StructureArray createStructureArray(Structure elementStructure);
    UnionArray createUnionArray(Union elementUnion);
    UnionArray createVariantUnionArray();
    Structure createStructure(String[] fieldNames, Field[] field);
    Structure createStructure(String id,String[] fieldNames, Field[] field);
    Structure appendField(Structure structure,String fieldName, Field field);
    Structure appendFields(Structure structure,String[] fieldNames, Field[] fields);
    Structure createStructure(Structure structToClone);
    Union createVariantUnion();
    Union createUnion(String[] fieldNames, Field[] fields);
    Union createUnion(String id, String[] fieldNames, Field[] fields);
    Field deserialize(ByteBuffer buffer, DeserializableControl control);
}

The following is a convenience class for creating introspection objects:

public interface FieldBuilder
{
    public FieldBuilder setId(String id);
    public FieldBuilder add(String name, ScalarType scalarType);
    public FieldBuilder addBoundedString(String name, int maxLength);
    public FieldBuilder add(String name, Field field);
    public FieldBuilder addArray(String name, ScalarType scalarType);
    public FieldBuilder addFixedArray(String name, ScalarType scalarType, int size);
    public FieldBuilder addBoundedArray(String name, ScalarType scalarType, int bound);
    public FieldBuilder addArray(String name, Field element);
    public Structure createStructure();
    public Union createUnion();
    FieldBuilder addNestedStructure(String name); 
    FieldBuilder addNestedUnion(String name);
    FieldBuilder addNestedStructureArray(String name); 
    FieldBuilder addNestedUnionArray(String name);
    FieldBuilder endNested();
}

The following interface creates data instances:

public interface PVDataCreate {
    PVField createPVField(Field field);
    PVField createPVField(PVField fieldToClone);
    PVScalar createPVScalar(Scalar scalar);
    PVScalar createPVScalar(ScalarType fieldType);
    PVScalar createPVScalar(PVScalar scalarToClone);
    <T extends PVScalar, TA extends PVScalarArray> T createPVScalar(
        PVScalarType<T, TA> scalarType);
    PVScalarArray createPVScalarArray(ScalarArray array);
    PVScalarArray createPVScalarArray(ScalarType elementType);
    PVScalarArray createPVScalarArray(PVScalarArray arrayToClone;
    <T extends PVScalar, TA extends PVScalarArray> TA createPVScalarArray(
        PVScalarType<T, TA> elementType);
    PVStructureArray createPVStructureArray(StructureArray structureArray);
    PVUnionArray createPVUnionArray(UnionArray unionArray);
    PVUnionArray createPVVariantUnionArray();
    PVStructure createPVStructure(Structure structure);
    PVStructure createPVStructure(String[] fieldNames,Field[] fields);
    PVStructure createPVStructure(PVStructure structToClone);
    PVUnion createPVUnion(Union union);
    PVUnion createPVVariantUnion();
    PVUnion createPVUnion(PVUnion unionToClone);
	
    PVStructureArray createPVStructureArray(Structure structure);
    PVUnionArray createPVUnionArray(Union union);
    PVField[] flattenPVStructure(PVStructure pvStructure);
}

pvData Conversion

An interface named Convert provides all reasonable conversions to/from pvData. The convert utility is also used to implement the toString methods of PVField. 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 is also implemented in C++ (See pvDataCPP for details).

pvData is distributed as a sourceforge mercurial project named pvDataJava. It 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.

org.epics.pvdata.factory

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.
StatusFactory
Status is a class for communication status between code modules.
StandardFieldFactory
Creates introspection objects for "well known" data.
StandardPVFieldFactory
Creates data objects for "well known" data.

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.

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 implementation of pvData. It is also possible for other code to provide implementations.

The interface StandardField provides introspection objects for standard fields. The interface StandardPVField provides data objects for standard fields.

The interface StatusCreate and class Status provide status objects to pass between source modules.

Process Variable Reflection

Given the introspection object it is possible to introspect a 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,
    union,
    unionArrray;
}

enum ScalarType {
    pvBoolean,
    pvByte, pvShort, pvInt, pvLong,
    pvUByte, pvUShort, pvUInt, pvULong,
    pvFloat,pvDouble,
    pvString;

    //Convenience methods
    public boolean isInteger();   // pvByte,...,pvULong
    public boolean isUInteger();   // pvUByte,...,pvULong
    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);
}
where
serialize
Serialize into buffer. flusher is called when buffer is full.
deserialize
deserialize from byte buffer. control is called when more data is required.
interface SerializableControl {
    void flushSerializeBuffer();
    void ensureBuffer(int size);
    void alignBuffer(int alignment);
    void cachedSerialize(Field field, ByteBuffer buffer)
}
where
flushSerializeBuffer
Code that called serialize must empty the buffer. The call can block until the buffer is flushed.
ensureBuffer
Helper method. Ensures specified size of bytes, flushes if necessary.
alignBuffer
Align buffer.Note that this takes care only current buffer alignment. If streaming protocol is used, care must be taken that entire stream is aligned.
cachedSerialize
Serialize Field instance via cache.
interface DeserializableControl {
    void ensureData(int size);
    void alignData(int alignment);
    Field cachedDeserialize(ByteBuffer buffer)
}
where
ensureData
Helper method. Ensures specified size of bytes, provides it if necessary.
alignData
Align buffer.Note that this takes care only current buffer alignment. If streaming protocol is used, care must be taken that entire stream is aligned.
cachedDeserialize
Deserialize Field instance via cache.
interface SerializableArray extends Serializable {
    void serialize(ByteBuffer buffer, SerializableControl flusher, int offset, int count);
}       
where
serialize
Serialize field into given buffer.
interface BitSetSerializable {
   void serialize(ByteBuffer buffer, SerializableControl flusher, BitSet bitSet);
   void deserialize(ByteBuffer buffer, DeserializableControl control, BitSet bitSet);
}
where
serialize
Serialize field into given buffer. The BitSet shows the fields to serialize.
deserialize
Deserialize field into given buffer. The BitSet shows the fields to serialize.

Reflection

This section defines the complete set of Java PV reflection interfaces. Reflection consists of the following:

Field
A Field has an ID and a type. It can be converted to a string. The format is the metadata format described in the overview.
Scalar
A Scalar has a ScalarType
ScalarArray
A ScalarArray has a ScalarType corresponding to the element type
Structure
Has fields that can be any of the supported types. In addition it has a name for each field.
StructureArray
The field holds Structure[]. Each element has the same Structure introspection interface. A client can only get/put entire PVStructure elements NOT subfields of array elements.
Union
A union field stores a single field. A given union field can either store a field of any type (variant union) or any of a specified set of types (regular union). Union describes which of these applies and the set of types in the case of a regular union.
UnionArray
The field holds Union[]. Each element has the same Union introspection interface.
FieldCreate
This is an interface that provides methods to create introspection interfaces. A factory is provided to create a FieldCreate.
Field
    
interface Field extends Serializable {
    String getID();
    Type getType();
    void toString(StringBuilder buf));
    void toString(StringBuilder buf,int indentLevel);
    String toString();
} 
where
getID
Get the identification string. It can be empty. For scalar fields the ID is the metadata type, i.e. boolean, byte, etc. For scalarArray fields the ID is the metadata type, i.e. boolean[], byte[], etc. For structure fields the ID is determined by the argument specified when fieldCreate.createStructure is called. For structureArray fields the ID is XX[] where XX is the ID of the element structure.
getType
Get the field type.
toString
Create a String that shows the type. The format is the metadata syntax defined in the overview.
Scalar
interface Scalar extends Field {
    ScalarType getScalarType();
}
where
getScalarType
Return the ScalarType.
ScalarArray
interface ScalarArray extends Field{
    ScalarType getElementType();
}
where
getElementType
Return the ScalarType for each array element.
Structure
interface Structure extends Field{
    Field getField(String fieldName);
    Field getField(int fieldIndex);
    <T extends Field> T getField(Class<T> c, String fieldName);
    <T extends Field> T getField(Class<T> c, int fieldIndex);
    Field[] getFields();
    String[] getFieldNames();
    int getFieldIndex(String fieldName);
    String getFieldName(int fieldIndex);
}
where
getField
Return a field of the structure. The field can be requested by name or by index.
getField - generic versions
These are the methods that get a field of the structure and convert it to the proper type.
getFields
Return the array of sub fields.
getFieldNames
Return the fieldNames for the fields.
getFieldName
Return the Field for the index.
getFieldIndex
Return the index of the field with the specified fieldName.
StructureArray
interface StructureArray extends Field{
    Structure getStructure();
}
where
getStructure
Return the introspection interface for the array elements.
Union
public interface Union extends Field{
    public static final String DEFAULT_ID = "union";
    public static final String ANY_ID = "any";
    Field getField(String fieldName);
    Field getField(int fieldIndex);
    <T extends Field> T getField(Class<T> c, String fieldName);
    <T extends Field> T getField(Class<T> c, int fieldIndex);
    Field[] getFields();
    String[] getFieldNames();
    int getFieldIndex(String fieldName);
    String getFieldName(int fieldIndex);
    boolean isVariant();
}
where
getField
Given a name or an index the type is returned. null is returned if not found.
getFields
Get the array of types.
getFieldNames
Get the array of names.
getFieldIndex
Get the index for name. -1 is returned if not found.
getFieldName
Get the name for the specified index.
isVariant
returns true if this is variant array and false otherwise.
UnionArray
public interface UnionArray extends Field{
    Union getUnion();
}
where
getUnion
Get the union interface for each element.
FieldCreate

Syntax for fieldName and ID.

A fieldName must begin with a letter and must be a sequence of letters and digits. A letter is defined as 'A'-'Z', 'a'-'z','_', or any Unicode character that denotes a letter in a language. Similarly digits are '0'-'9' and any Unicode character that denotes a digit in a language. Note that this is the same as the Java syntax for variable names.

The syntax for ID is the same except the '.' is also allowed after the initial letter.

public interface FieldCreate {
    FieldBuilder createFieldBuilder();
    Scalar createScalar(ScalarType scalarType);
    BoundedString createBoundedString(int maxLength);
    ScalarArray createScalarArray(ScalarType elementType);
    ScalarArray createFixedScalarArray(ScalarType elementType, int size);
    ScalarArray createBoundedScalarArray(ScalarType elementType, int bound);
    StructureArray createStructureArray(Structure elementStructure);
    UnionArray createUnionArray(Union elementUnion);
    UnionArray createVariantUnionArray();
    Structure createStructure(String[] fieldNames, Field[] field);
    Structure createStructure(String id,String[] fieldNames, Field[] field);
    Structure appendField(Structure structure,String fieldName, Field field);
    Structure appendFields(Structure structure,String[] fieldNames, Field[] fields);
    Structure createStructure(Structure structToClone);
    Union createVariantUnion();
    Union createUnion(String[] fieldNames, Field[] fields);
    Union createUnion(String id, String[] fieldNames, Field[] fields);
    Field deserialize(ByteBuffer buffer, DeserializableControl control);
}
where
createFieldBuilder
Create an instance of FieldBuilder, which is described next.
createScalar
Return the Scalar with the specified ScalarType. Note that the implementation creates a single instance for each ScalarType.
createBoundedString
Return a scalar of type pvString that has a bounded size for a string.
createScalarArray
Return the ScalarArray with the specified elementType. Note that the implementation creates a single instance for each elementType.
createFixedScalarArray
Return the ScalarArray with the specified elementType and specifies that size of element size of any object that has this introspection interface.
createBoundedScalarArray
Return the ScalarArray with the specified elementType and specifies that maximum element size of any object that has this introspection interface.
createStructureArray
Return a StructureArray with the specified introspection interface for each array element.
createUnionArray
Return a UnionArray with the specified introspection interface.
createVariantUnionArray
Return a UnionArray of variant union elements.
createStructure
Return a Structure. There are two methods. The first creates a structure with an empty ID which results in an ID of structure.
appendField
Append a field to a structure.
appendFields
Append an array of fields to a structure.
createVariantUnion
Create a variant union, i.e. a union where the subfield can be any valid type.
createUnion
Create a union where the subfield can have any of the types an names defined by the arguments and the associated names. The default ID is union for regular unions and any for variant unions.
deserialize
Deserialize a field from the the buffer.
FieldBuilder

FieldBuilder is a convenience class for creating introspection interfaces. An example is:

FieldCreate fieldCreate = FieldFactory.getFieldCreate();
Structure s = fieldCreate.createFieldBuilder().
    add("double", ScalarType.pvDouble).
    addNestedStructure("nested").
        add("short", ScalarType.pvShort).
        add("long", ScalarType.pvLong).
        endNested().
    addArray("intArray", ScalarType.pvInt).
    createStructure();
System.out.println(s);
This produces:
structure
    double double
    structure nested
        short short
        long long
    int[] intArray

The Java interface is:

public interface FieldBuilder
{
    public FieldBuilder setId(String id);
    public FieldBuilder add(String name, ScalarType scalarType);
    public FieldBuilder addBoundedString(String name, int maxLength);
    public FieldBuilder add(String name, Field field);
    public FieldBuilder addArray(String name, ScalarType scalarType);
    public FieldBuilder addFixedArray(String name, ScalarType scalarType, int size);
    public FieldBuilder addBoundedArray(String name, ScalarType scalarType, int bound);
    public FieldBuilder addArray(String name, Field element);
    public Structure createStructure();
    public Union createUnion();
    FieldBuilder addNestedStructure(String name); 
    FieldBuilder addNestedUnion(String name);
    FieldBuilder addNestedStructureArray(String name); 
    FieldBuilder addNestedUnionArray(String name);
    FieldBuilder endNested();
}
setId
Specify the ID for the next createStructure or createUnion call.
add
Add a name and field for then next call to createStructure or createUnion. There are two forms: one for an existing field and the other for a scalar field.
addBoundedString
Add a bounded string field.
addArray
Add a name and array field for then next call to createStructure or createUnion. There are two forms: one for an existing field and the other for a scalar field. Note that for an existing field this specifies the element type.
addFixedArray
Add a scalar fixed array field.
addBoundedArray
Add a scalar bounded array field.
createStructure
Create a Structure from the previous add and addArray methods.
createUnion
Create a Union from the previous add and addArray methods.
addNestedStructure
Start a nested structure. This is followed by calls to other methods and ended by a call to endNested.
addNestedUnion
Start a nested union. This is followed by calls to other methods and ended by a call to endNested.
addNestedStructureArray
Start a nested structure array. This is followed by calls to other methods and ended by a call to endNested.
addNestedUnionArray
Start a nested union array. This is followed by calls to other methods and ended by a call to endNested.
endNested

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.

Status

interface Status extends Serializable  {
    public enum StatusType {OK,WARNING,ERROR,FATAL};
    StatusType getType();
    String getMessage();
    String getStackDump();
    boolean isOK();
    boolean isSuccess();
}
where
StatusType
An enum for the status type.
getType
Get the StatusType.
getMessage
Get a message explaining the error.
getStackDump
Get a stack dump.
isOK
Is the status the singleton object for StatusType.OK?
isSuccess
Is the StatusType either OK or WARNING?

StatusCreate

interface StatusCreate {
    Status getStatusOK(); 
    Status createStatus(StatusType type, String message, Throwable cause);
    Status deserializeStatus(ByteBuffer buffer, DeserializableControl control);
}
where
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.

Requester

Requester is an interface that allows messages to be given to client code. An instance of Requester must be implemented by the client. The definition is:

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:
  • getRequesterName
    This is the full field name concatenated to the record name.
  • message
    For the default implementation, PVField prepends the full field name to the message and calls PVRecord.message. The default implementation for PVRecord either displays the message on stdout or stderr or gives the message to message requesters.

PVField - Data Interfaces

This section defines the Java Interfaces for accessing the data within a top-level PVStructure.

PVField

PVField is the base interface for accessing data. Every field of a top-level PVStructure has a PVField associated with it. This includes the top-level structure itself.

interface PVField Serializable {
    String getFieldName();
    String getFullName();
    int getFieldOffset();
    int getNextFieldOffset();
    int getNumberFields();
    boolean isImmutable();
    void setImmutable();
    Field getField();
    PVStructure getParent();
    void postPut();
    void setPostHandler(PostHandler postHandler);
    void toString(StringBuilder buf);
    void toString(StringBuilder buf,int indentLevel);
    String toString();
    // The following should go away.
    PVAuxInfo getPVAuxInfo();
}

where

getFieldName
Get the field name for this field. This will be a empty string for the top-level structure.
getFullName
Return the path name and field name for this field.
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.
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.
postPut
If a PostHandler is registered it is called otherwise no action is taken.
NOTE: The implementation of the various data interfaces automatically call postPut when a field is changed. However this is not true for a subField of a PVUnion, PVUnionArray, or PVStructureArray. If a subField of any of these is changed then the code that is making the modification must call postPut for the PVUnion, PVUnionArray, or PVStructureArray field. Note also that it is not a good idea to modify a subfield of a PVUnionArray or a PVStructureArray since it violates the idea of Copy On Write for arrays.
setPostHandler
Set the handler for postPut. At most one handler can be set.
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. Note: The actual conversion is done by the Convert facility.

PVAuxInfo

NOTE: PVAuxInfo should go away.

AuxInfo (Auxiliary 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 used 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 PVUByte extends PVScalar {
    byte get();
    void put(byte value);
}

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

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

interface PVULong 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);
}

NOTE: The interface for each unsigned integer is the same as for the same signed type. This is because Java does not implement unsigned primitive types.

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 array interfaces. It extends PVField and provides the additional methods:

interface PVArray extends PVField, SerializableArray {
    Array getArray();
    int getLength();
    void setLength(int len);
    int getCapacity();
    void setCapacity(int len);
    boolean isCapacityMutable();
    void setCapacityMutable(boolean isMutable);
}
getArray
Get the Array introspection interface.
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 for PVDoubleArray it 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 efficiency. 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 transferred. The arguments are:

offset
The offset in the PVArray.
len
The maximum number of elements to transfer. The number actually transferred 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;
}

NOTE: The pvDataCPP now enforces COW (Copy on Write) for arrays. In the future pvDataJava should have similar rules but without the concept of const it may not be possible to enforce COW. shareData definitely violates COW if any code changes the shared data. It should not used except for immutable arrays.

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. If the PVArray is not immutable then the application is responsibility for coordinating 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 defines many enumerated structures where an enumerated structure has two fields: 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);
}
interface PVUByteArray 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);
}
interface PVUShortArray 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);
}
interface PVUIntArray 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);
}
interface PVULongArray 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);
}

public class UnionArrayData {
    public PVUnion[] data;
    public int offset;
}

interface PVUnionArray extends PVArray{
    UnionArray getUnionArray();
    int get(int offset, int length, UnionArrayData data);
    int put(int offset,int length, PVUnion[] from, int fromOffset);
    void shareData(PVUnion[] from);
}

NOTES:

Unsigned types
The implementation for each unsigned integer is the same as for the same signed type. This is because Java does not implement unsigned primitive types.
PVStructureArray and PVUnionArray
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);
    <T extends PVField> T getSubField(Class<T> c, String fieldName);
    <T extends PVField> T getSubField(Class<T> c, int fieldOffset);
    // The following are convenience methods
    // Note that they are no longer needed because of above generic 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);
    PVUnion getUnionField(String fieldName);
    PVUnionArray getUnionArrayField(String fieldName);
    public boolean checkValid();
}

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 the PVStructure. If the field does not exist null is returned. fieldName can be the name of an immediate subfield. More generally it can be the name of the field relative to the PVStructure. In other words it consists of one or more strings separated by periods (.s). The last of these strings is the name of the field. The other strings are the names of the structure subfields containing the requested field starting with the highest level subfield and descending to the leaf field, e.g. current.alarm.status.
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.
getSubField - generic versions
These are the methods to that get a subfield and convert it to the proper type. These two methods replace all the type specific methods, which are no longer required and should not be used.

PVDataCreate

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

interface PVDataCreate {
    PVField createPVField(Field field);
    PVField createPVField(PVField fieldToClone);
    PVScalar createPVScalar(Scalar scalar);
    PVScalar createPVScalar(ScalarType fieldType);
    PVScalar createPVScalar(PVScalar scalarToClone);
    <T extends PVScalar, TA extends PVScalarArray> T createPVScalar(
        PVScalarType<T, TA> scalarType);
    PVScalarArray createPVScalarArray(ScalarArray array);
    PVScalarArray createPVScalarArray(ScalarType elementType);
    PVScalarArray createPVScalarArray(PVScalarArray arrayToClone;
    <T extends PVScalar, TA extends PVScalarArray> TA createPVScalarArray(
        PVScalarType<T, TA> elementType);
    PVStructureArray createPVStructureArray(StructureArray structureArray);
    PVUnionArray createPVUnionArray(UnionArray unionArray);
    PVUnionArray createPVVariantUnionArray();
    PVStructure createPVStructure(Structure structure);
    PVStructure createPVStructure(String[] fieldNames,Field[] fields);
    PVStructure createPVStructure(PVStructure structToClone);
    PVUnion createPVUnion(Union union);
    PVUnion createPVVariantUnion();
    PVUnion createPVUnion(PVUnion unionToClone);
	
    PVStructureArray createPVStructureArray(Structure structure);
    PVUnionArray createPVUnionArray(Union union);
    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 of the 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 third provides a field name and a PVScalar to clone. The newly created PVScalar will have the same auxInfos as the original. The last is a generic version that casts to the appropriate type. An example is:
PVDouble value  = pvDataCeate.createPVScalar(PVScalarType.pvDouble);
createPVScalarArray
Create an instance of a PVArray. Three versions are supplied. The first is passed an introspection interface. The second provides and the elementType. The third provides an array to clone. The newly created PVArray will have the same values as the original. The last is a generic version that casts to the appropriate type. An example is:
PVDoubleArray value  = pvDataCeate.createPVScalarArray(PVScalarType.pvDouble);
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.
createPVUnionArray
Create a PVUnionArray. It must be passed a union. This will become the union interface for ALL elements of the PVUnionArray. It MUST be used to create any new array elements.
createPVVariantUnionArray
Create a variant PVUnionArray. Each array element can have any type.
createPVStructure
Create an instance of a PVStructure. Three methods are provided. The first method uses a previously created structure introspection interface. The second called with fieldName array and a Field array. The third initializes the subfields by cloning the fields contained in structToClone. The newly created subfields will have the same values and AuxInfos as the original. If structToClone is null then the new structure is initialized to have 0 subfields.
createPVUnion
Create a PVUnion. Two versions are provided. The first is passed a Union interface the single subfield. The second is passed a PVUnion to clone.
createPVVariantUnion
Create a variant PVUnion. The subfield can have any type.
flattenPVStructure
Create an array of PVFields for the fields in the PVStructure. The order is according to fieldOffset.

Convert

NOTE: 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 isCopyUnionCompatible(Union from, Union to);
    void copyUnion(PVUnion from, PVUnion to);
    boolean isCopyStructureArrayCompatible(StructureArray from, StructureArray to);
    void copyStructureArray(PVStructureArray from, PVStructureArray to);
    boolean isCopyUnionArrayCompatible(UnionArray from, UnionArray to);
    void copyUnionArray(PVUnionArray from, PVUnionArray 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  fromUByte(PVField pv, byte from);
    void  fromUShort(PVField pv, short from);
    void  fromUInt(PVField pv, int from);
    void  fromULong(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 fromUByteArray(PVScalarArray pv,
        int offset, int len, byte[]from, fromOffset);
    int fromUShortArray(PVScalarArray pv,
        int offset, int len, short[]from, fromOffset);
    int fromUIntArray(PVScalarArray pv,
        int offset, int len, int[]from, fromOffset);
    int fromULongArray(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.

StandardField and StandardPVField

Warning: The concept of StandardField is closely related to property which is described below. The section on property provides details about alarm, timeStamp, display, control, and enumerated. That section should be read in conjunction with this section.

A standard field is a field that tools know how to use. pvData defines and provides support for the following standard fields.

enumerated
This is a structure that has two subfields: index, and choices
value
This is any field with the name "value". It can be any type. The other standard fields can provide "properties" for the value field.
timeStamp
Encodes a time stamp. It can be a standalone field or be a property if a value field, in which case it should be a subfield of the same structure as the value field.
alarm
Encodes an alarm. It is normally a property if a value field, in which case it should be a subfield of the same structure as the value field.
display
Encodes a set of information for displaying a scalar or scalarArray value. It is normally a property if a value field, in which case it should be a sub field of the same structure as the value field.
control
Encodes a set of information for limits for a scalar or scalarArray value. It is normally a property if a value field, in which case it should be a sub field of the same structure as the value field.
valueAlarm
Defines a set of values for setting alarm values for a scalar value. It has separate definitions for booleanAlarm, byteAlarm, shortAlarm, intAlarm, longAlarm, floatAlarm, and doubleAlarm. There is also an enumeratedAlarm for an enumerated value field.

StandardField

interface StandardField {
    Structure scalar(ScalarType scalarType,String properties);
    Structure scalarArray(ScalarType elementType, String properties);
    Structure structureArray(Structure structure,String properties);
    Structure enumerated();
    Structure enumerated(String properties);
    Structure alarm();
    Structure timeStamp();
    Structure display();
    Structure control();
    Structure booleanAlarm();
    Structure byteAlarm();
    Structure shortAlarm();
    Structure intAlarm();
    Structure longAlarm();
    Structure floatAlarm();
    Structure doubleAlarm();
    Structure enumeratedAlarm();
}
where
scalar
Create a Structure that has a scalar field named value and with the specified ScalarType. The structure can have additional fields that are properties of the value field. properties is some combination of alarm, timeStamp, display, control, and valueAlarm.
scalarArray
Create a Structure that has a scalarArray field named value and with the specified element type. The structure can have additional fields that are properties of the value field. properties is some combination of alarm, timeStamp, display, and control.
structureArray
Create a Structure that has a structureArray field named value and with the specified structure as the introspection interface for each array element. The structure can have additional fields that are properties of the value field. properties is some combination of alarm and timeStamp.
enumerated()
Create an enumerated Structure
enumerated(String properties)
Create a Structure that has an enumerated structure field named value. The structure can have additional fields that are properties of the value field. properties is some combination of alarm, timeStamp, and enumeratedAlarm.
alarm
Create an alarm Structure.
timeStamp
Create a timeStamp Structure.
display
Create an display Structure.
control
Create a control Structure.
booleanAlarm
Create a booleanAlarm Structure.
byteAlarm
Create a byteAlarm Structure.
shortAlarm
Create a shortAlarm Structure.
intAlarm
Create a intAlarm Structure.
longAlarm
Create a longAlarm Structure.
floatAlarm
Create a floatAlarm Structure.
doubleAlarm
Create a doubleAlarm Structure.
enumeratedAlarm
Create a enumearatedAlarm Structure.

StandardPVField

interface StandardPVField {
    PVStructure scalar(ScalarType type,String properties);
    PVStructure scalarArray(ScalarType elementType, String properties);
    PVStructure structureArray(Structure structure,String properties);
    PVStructure enumerated(String[] choices);
    PVStructure enumerated(String[] choices,String properties);
}
where
scalar
Create a PVStructure that has a scalar field named value and with the specified ScalarType. The structure can have additional fields that are properties of the value field. properties is some combination of alarm, timeStamp, display, control, and valueAlarm.
scalarArray
Create a PVStructure that has a scalarArray field named value and with the specified elementType. The structure can have additional fields that are properties of the value field. properties is some combination of alarm, timeStamp, display, and control.
structureArray
Create a PVStructure that has a structureArray field named value and with the specified structure as the introspection interface for each array element. The structure can have additional fields that are properties of the value field. properties is some combination of alarm and timeStamp.
enumerated(String[] choices)
Create an enumerated PVStructure. The choices field is initialized with choices and set immutable.
enumerated(String[] choices,String properties)
Create a PVStructure that has an enumerated structure field named value. The choices field of value is initialized with choices and set immutable. The structure can have additional fields that are properties of the value field. properties is some combination of alarm, timeStamp, and enumeratedAlarm.

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(ScalarType.pvDouble);

Create a structure.

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

PVStructure create() {
    Field[] fields = new Field[2];
    String[] fieldNames = new String[2];
    fields[0] = fieldCreate.createScalar(ScalarType.pvLong);
    fields[1] = fieldeCreate.createScalar(ScalarType.pvInt);
    fieldNames[0] = "a";
    fieldNames[1] = "b";
    PVStructure pvStructure = pvDataCreate.createPVStructure(
        fieldNames,fields);
    return(pvStructure);
}

FieldBuilder provides an easier way to accomplish the previous:

PVStructure create() {
   return pvDataCreate.createPVStructure(
        fieldCreate.createFieldBuilder()
          .add("a",ScalarType.pvLong)
          .add("b",ScalarType.pvInt)
          .createStructure());
}

StandardPVField also provides an easy way to create fields. For example:

StandardPVField standardPVField = StandardPVFieldFactory.getStandardPVField();
PVStructure pvStructure = standardPVField.scalar(
    ScalarType.pvDouble,
    "alarm,timeStamp.display,control,valueAlarm");

Package org.epics.pvdata.factory

This package provides factories and classes to implement everything defined in package org.epics.pvdata.pv Look at the code for details.

Package org.epics.pvdata.property

Definition of Property

A field can have properties provided by other fields in the same structure. Often this field has the name value 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 field name 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 hierarchy 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
    double value
    alarm_t
        int severity 0
        int status 0
        string message
    double value
    display_t
        double limitLow 0.0
        double limitHigh 10.0
        string description "Sample Description"
        string format "%f"
        string units volts

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 powerSupplyValue
    double value
    alarm_t
        int severity 0
        int status 0
        string message

structure powerSupplySimple
    alarm_t
        int severity 0
        int status 0
        string message
    timeStamp_t
        long secondsPastEpoch
        int nanoseconds
        int userTag
    powerSupplyValue_t voltage
        double value
        alarm_t
            int severity 0
            int status 0
            string message
    powerSupplyValue_t power
        double value
        alarm_t
            int severity 0
            int status 0
            string message
    powerSupplyValue_t current
        double value
        alarm_t
            int severity 0
            int status 0
            string message

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
Any of the scalar types.
scalarArray
An array with elementType being a scalar type
enumerated structure
A structure that includes fields named index and choices. index is an int that selects a 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 support.
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 and current that each support the pvData data model.

PVProperty Interfaces and Classes

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);
}
where
findProperty
Find a field that is a subfield or property of this PVField. The fieldName is of the form name.name... pvField must be named value or it does not have properties.
findPropertyViaParent
Find a property by searching up the parent tree. The property name is expected to match the name of a field. The return value is the interface to the first field found that is not a null structure or null if not found.
getPropertyNames
Get a String array that holds the names of the properties for this field.

PVPropertyFactory

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
    long secondsPastEpoch
    int nanoseconds
    int 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

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 the 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 current 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 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 nanosecond portion is 0≤=nano<nanoSecPerSec. Note that it is OK to have timeStamps for times previous to the epoch.

TimeStampFactory
class TimeStampFactory implements TimeStamp {
    public static TimeStamp create();
}
PVTimeStamp
interface PVTimeStamp {
    boolean attach(PVField pvField);
    void detach();
    boolean isAttached();
    void get(TimeStamp timeStamp);
    boolean set(TimeStamp timeStamp);
}
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 time stamp 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.
PVTimeStampFactory
class PVTimeStampFactory implements PVTimeStamp {
    public static PVTimeStamp create();
}

alarm

An alarm structure is defined as follows:

structure alarm
    int severity
    int status
    string message

Note that severity and status are NOT defined as enumerated structures. The reason is performance, i.e. prevent passing the array of choice strings everywhere. The AlarmStatus and AlarmSeverity provide the equivalent of choices for an enumerated structure.

AlarmSeverity

Alarm Severity defines the possible alarm severities

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

    public static AlarmSeverity getSeverity(int value);
    private static final String[] alarmSeverityNames;
    public static String[] getSeverityNames() { return alarmSeverityNames;}
}
where
getSeverity
Get the alarm severity corresponding to the integer value.
getSeverityNames
Get the array of severity choices.
AlarmStatus

Alarm Status defines the possible alarm status conditions

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;}
}
where
getStatus
Get the alarm status corresponding to the integer value.
getStatusNames
Get the array of status choices.
Alarm
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);
}
where
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);
}

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.
PVAlarmFactory
class PVAlarmFactory implements PVAlarm{
    public static PVAlarm create();
}

control

Control information is represented by the following structure

structure control
    double limitLow
    double limitHigh
    double minStep
Control
The java definition for Control is:
class Control {
    Control();
    double getLow();
    double getHigh();
    double getMinStep();
    void setLow(double value);
    void setHigh(double value);
    void setMinStep(double value);
}

where

Control
The default constructor.
getLow
Get the low limit.
getHigh
Get the high limit.
getMinStep
Get the min step.
setLow
Set the low limit.
setHigh
Set the high limit.
setMinStep
Set the min step.
PVControl
interface PVControl {
    boolean attach(PVField pvField);
    void detach();
    boolean isAttached();
    void get(Control control);
    boolean set(Control control);
}

where

attach
Attempts to attach to pvField It returns (false,true) if it found an appropriate pvData structure. It looks first 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.
PVControlFactory
class PVControlFactory{
    public static PVControl create();
}

Display

Display information is represented by the following structure

structure display
    double limitLow
    double limitHigh
    string description
    string format
    string units
Display
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 constructor.
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.
PVDisplay
interface PVDisplay {
    boolean attach(PVField pvField);
    void detach();
    boolean isAttached();
    void get(Display display);
    boolean set(Display display);
}

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 a 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.
PVDisplayFactory
class PVDisplayFactory implements PVDisplay{
    public static PVDisplay create()
}

enumerated structure

An enumerated structure is a structure that has fields:

structure
    int index
    string[] choices
PVEnumerated
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);
}

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 enumerated 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.
PVEnumeratedFactory
class PVEnumeratedFactory{
    PVEnumerated create();
}

multiChoice

NOTE: This is not currently used by anything. Bob sees it as an important facility in the future.

MultiChoice is support for a multiChoice structure, which is a structure containing two fields:

choices
An array of strings
bitMask
A bitMask that selects a subset of the choices.

MultiChoice

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

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 the choice already exists then it's index is returned. If not it is appended to the choices.

MultiChoiceFactory

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

Package org.epics.pvdata.misc

Overview

This package provides utility code:

BitSet
An implementation of BitSet that can be serialized.
MessageQueue
Support for queuing messages for requesters.
Thread
ThreadCreate and Executor
TimeFunction
Time how long a function call requires.
LinkedList
A double 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.
Queue
A queue implementation.
Destroyable
A base interface for destroy.
Serialize
A helper for serialization.

bitSet

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

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

BitSetUtil

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

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.

BitSetUtilFactory

class BitSetUtilFactory {
    public static BitSetUtil getCompressBitSet();
}

Message Queue

MessageNode

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

MessageQueue

public interface MessageQueue {
    MessageNode get();
    boolean put(String message,MessageType messageType,boolean replaceLast);
    boolean isEmpty();
    boolean isFull();
    int getClearOverrun();
}

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

MessageNode is a class with two public data members:

message
The message.
messageType
The message type.

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

public class MessageQueueFactory {
    public static MessageQueue create(int size);
}
where
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);
}

ThreadReady

interface ThreadReady {
    void ready();
}

RunnableReady

interface RunnableReady {
    void run(ThreadReady threadReady);
}

ThreadCreate

interface ThreadCreate {
    Thread create(String name, int priority, RunnableReady runnableReady);
    Thread[] getThreads();
}

ThreadCreateFactory

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.

ExecutorNode
public interface ExecutorNode {}
Executor
public interface Executor {
    ExecutorNode createNode(Runnable command);
    void execute(ExecutorNode executorNode);
    void stop();
}
ExecutorFactory
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 a 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 iterations 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.

TimeFunctionRequester

public interface TimeFunctionRequester {
    void function();
}

TimeFunction

public interface TimeFunction {
    double timeCall();
}

TimeFunctionFactory

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 efficient 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 current 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.

LinkedListNode

public interface LinkedListNode<T> {
    public T getObject();
    boolean isOnList();
} 

LinkedList

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

LinkedListArray

public interface LinkedListArray<T> {
    void setNodes(LinkedList<T> linkedList);
    LinkedListNode<T>[] getNodes();
    int getLength();
    void clear();
}

LinkedListCreate

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(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.

Timer

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

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

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

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 TimerNode can only be on a single timer queue and only once on a given timer queue.

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 periodic 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 explanation 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.

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 element until a new free element becomes available. This is used by pvData.monitor.

QueueCreate

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

QueueElement

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

Queue

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

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

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.

SerializeHelper

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

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.

support for copy and monitor

copy and monitor are not used in this project. They are intended for use by pvAccess and by pvAccess servers. They are provided with this project because the code depends only on pvData itself.

This document describes Java specific code. pvRequest.html provides a language independent overview of copy and monitor.

Copy provides:

createRequest
The Channel create methods in pvAccess all have an argument PVStructure pvRequest.
Given an ASCII string createRequest creates a PVStructure that provides a pvData representation of the information from the ascii string. It is this structure that can be passed to the channel create methods.
The information in a pvRequest selects an arbitrary subset of the fields in a top-level structure that resides in the server. In addition options can be specified. Both global and field specific options can be specified.
pvCopy
This is a facility used by channel providers. It provides client specific code that manages a copy of an arbitrary subset of the fields in a top-level structure that resides in the provider. It also allows provider access to options specified by the client.
Monitor provides:
monitor
This is support code for channel providers that implement channel monitor. It, together with the queue facility, provides support for monitor queues.
monitorPlugin
This is support for implementing monitor plugins. A monitor plugin can be developed that has no knowledge of pvAccess but only pvData.

support for copy

copy provides the ability to create a structure that has a copy of an arbitrary subset of the fields in an existing top-level structure. In addition it allows global options and field specific options. It has two main components: createRequest and pvCopy. Given a string createRequest creates a pvRequest, which is a PVStructure that has the format expected by pvCopy.

createRequest

This is mainly used by pvAccess clients. Given a request string it creates a pvRequest structure that can be passed to the pvAccess create methods. In turn pvAccess passes the pvRequest to a local channel provider which then passes it to pvCopy.

The definition of the public members is:

class CreateRequest {
...
     static CreateRequestPtr create();
     virtual PVStructurePtr createRequest(String const &request);
     String getMessage();
};

An example of how it is used is:

CreateRequestPtr createRequest = CreateRequest::create();
PVStructurePtr pvRequest = createRequest->createRequest(request);
if(pvRequest==NULL) {
    String error = createRequest->getMessage();
    // take some action
} else {
    //success do something
}

pvCopy

The definition of the public members is:

class epicsShareClass PVCopyTraverseMasterCallback
{
...
    virtual void nextMasterPVField(PVFieldPtr const &pvField);
};

class class epicsShareClass PVCopy
{
...
    static PVCopyPtr create(
        PVStructurePtr const &pvMaster,
        PVStructurePtr const &pvRequest,
        String const & structureName);
    PVStructurePtr getPVMaster();
    void traverseMaster(PVCopyTraverseMasterCallbackPtr const & callback);
    StructureConstPtr getStructure();
    PVStructurePtr createPVStructure();
    size_t getCopyOffset(PVFieldPtr const  &masterPVField);
    size_t getCopyOffset(
        PVStructurePtr const  &masterPVStructure,
        PVFieldPtr const  &masterPVField);
     PVFieldPtr getMasterPVField(std::size_t structureOffset);
     void initCopy(
        PVStructurePtr const  &copyPVStructure,
        BitSetPtr const  &bitSet);
     void updateCopySetBitSet(
        PVStructurePtr const  &copyPVStructure,
        BitSetPtr const  &bitSet);
    void updateCopyFromBitSet(
        PVStructurePtr const  &copyPVStructure,
        BitSetPtr const  &bitSet);
    void updateMaster(
        PVStructurePtr const  &copyPVStructure,
        BitSetPtr const  &bitSet);
    PVStructurePtr getOptions(std::size_t fieldOffset);
...
};
where
PVCopyTraverseMasterCallback::nextMasterPVField
PVCopyTraverseMasterCallback is a callback which must be implemented by the code that uses pvCopy, normally the channel provider. It has the single method nextMasterPVField
nextMasterPVField is called for each field in the master as a result of a call to traverseMaster.
create
This is the method for creating a PVCopy instance.
pvMaster
the top-level structure managed by the server.
pvRequest
selects the set of subfields desired and options for each field.
structureName
the name for the top-level of any PVStructure created.
getPVMaster
Gets the top-level structure from pvMaster.
traverseMaster
Traverse all fields of the top-level structure of pvMaster. For each field the callback is called.
getStructure
Get the introspection interface for a PVStructure for copy.
createPVStructure
Create a copy instance. Monitors keep a queue of monitor elements. Since each element needs a PVStructure, multiple top-level structures will be created.
getCopyOffset
Given a field in pvMaster. return the offset in copy for the same field. A value of String::npos means that the copy does not have this field. Two overloaded methods are provided. The first is called if the field of master is not a structure. The second is for subfields of a structure.
getMasterPVField
Given a offset in the copy get the corresponding field in pvMaster.
initCopy
Initialize the fields in copyPVStructure by giving each field the value from the corresponding field in pvMaster. bitSet will be set to show that all fields are changed. This means that bit set will have the value {0}.
updateCopySetBitSet
Set all fields in copyPVStructure to the value of the corresponding field in pvMaster. Each field that is changed has it's corresponding bit set in bitSet.
updateCopyFromBitSet
For each set bit in bitSet set the field in copyPVStructure to the value of the corresponding field in pvMaster.
updateMaster
For each set bit in bitSet set the field in pvMaster to the value of the corresponding field in copyPVStructure.
getOptions
Get the options for the field at the specified offset. A NULL is returned if no options were specified for the field. If options were specified, the returnedPVStructure is a structure with a set of PVString subfields that specify name,value pairs. name is the subField name and value is the subField value.

support for monitor

This consists of two components:

monitor
Used by code that implements pvAccess monitors.
monitorPlugin
Code that provides special semantics for monitors.

monitor

class MonitorElement {
    MonitorElement(PVStructurePtr const & pvStructurePtr);
    PVStructurePtr pvStructurePtr;
    BitSetPtr changedBitSet;
    BitSetPtr overrunBitSet;
};

class Monitor {
    virtual Status start() = 0;
    virtual Status stop() = 0;
    virtual MonitorElementPtr poll() = 0;
    virtual void release(MonitorElementPtr const & monitorElement) = 0;
};

class MonitorRequester : public virtual Requester {
    virtual void monitorConnect(Status const & status,
        MonitorPtr const & monitor, StructureConstPtr const & structure) = 0;
    virtual void monitorEvent(MonitorPtr const & monitor) = 0;
    virtual void unlisten(MonitorPtr const & monitor) = 0;
};

monitorElement

MonitorElement holds the data for one element of a monitor queue. It has the fields:

pvStructurePtr
A top-level structure with data values at the time the monitors occurs.
changedBitSet
Shows which fields have changed since the previous monitor.
overrunBitSet
Shows which fields have changed more than once since the previous monitor.

monitorElement queue

A queue of monitor elements must be implemented by any channel provider that implements Channel::createMonitor. For an example implementation look at pvDatabaseCPP. It has the following:

typedef Queue<MonitorElement> MonitorElementQueue;
typedef std::tr1::shared_ptr<MonitorElementQueue> MonitorElementQueuePtr;

class MultipleElementQueue :
    public ElementQueue
{
public:
    POINTER_DEFINITIONS(MultipleElementQueue);
    virtual ~MultipleElementQueue(){}
    MultipleElementQueue(
        MonitorLocalPtr const &monitorLocal,
        MonitorElementQueuePtr const &queue,
        size_t nfields);
    virtual void destroy(){}
    virtual Status start();
    virtual Status stop();
    virtual bool dataChanged();
    virtual MonitorElementPtr poll();
    virtual void release(MonitorElementPtr const &monitorElement);
...
};

Monitor

Monitor must be implemented by any channel provider that implements Channel::createMonitor. Remote PVAccess also implements Monitor on the client side. Note that each client has it's own queue that is not shared with other client.

Monitor has the following methods:

start
Start monitoring. This will result in an initial monitor that has the current value of all fields.
stop
Stop monitoring.
poll
Called to get a monitor element. If no new elements are available then a null pointer is returned.
release
Release the monitor element. The caller owns the monitor element between the calls to poll and release.

MonitorRequester

This must be implemented by a pvAccess client. It has the methods:

monitorConnect
A monitor has either connected of disconnected.
monitorEvent
A new monitor element is available.
unlisten
The channel is going away. The client cam no longer access the monitor.

monitorPlugin

class MonitorPlugin
{
    virtual String const & getName() = 0;
    virtual bool causeMonitor(
        PVFieldPtr const &pvField,
        PVStructurePtr const &pvTop,
        MonitorElementPtr const &monitorElement) = 0;
    virtual void monitorDone(
        MonitorElementPtr const &monitorElement);
    virtual void startMonitoring();
    virtual void stopMonitoring();
    virtual void beginGroupPut();
    virtual void endGroupPut();
};

class MonitorPluginCreator
{
    virtual MonitorPluginPtr create(
        FieldConstPtr const &field,
        StructureConstPtr const &top,
        PVStructurePtr const &pvFieldOptions) = 0;
     virtual String const & getName() = 0;
}

class MonitorPluginManager
{
    static MonitorPluginManagerPtr get();
    bool addPlugin(
         String const &pluginName,
         MonitorPluginCreatorPtr const &creator);
    MonitorPluginCreatorPtr findPlugin(String const &pluginName);
    void showNames();
};

MonitorPlugin

MonitorPlugin must be implemented by the plugin implementation. It has methods:

getName
Get the name of the plugin.
causeMonitor
Should the value of pvField cause a monitor to be raised. pvField and pvTop are fields in the top-level structure being monitored. monitorElement has the top-level structure for the copy
. The implementation should not modify the fields in the structure being monitored. Called with pvTop locked.
monitorDone
Called just before monitorElement will be given to client. The plugin can change the data values and bitSets in monitorElement. Called with pvTop unlocked.
startMonitoring
Monitoring is starting.
stopMonitoring
Monitoring is being stopped.
beginGroupPut
A set of puts is starting. Called with pvTop locked.
endGroupPut
The set of puts is complete. Called with pvTop locked.

MonitorPluginCreator

MonitorPluginCreator must also be implemented by the plugin implementation. It is called for each field instance that has options of the from [plugin=name...] where name is the name of the plugin. Note that a plugin instance will belong to a single client. It has methods:

getName
Get the name of the plugin.
create
Create a new plugin instance. If the arguments are not compatible with the plugin a NULL shared pointer is returned.
pvFieldOptions is a structure with a set of PVString subfields that specify name,value pairs. name is the subField name and value is the subField value.
Note that a plugin will below to a single client.

MonitorPluginManager

MonitorPluginManager has the methods:

get
MonitorPluginManager is a singleton. The first call to get will create the single instance. Further calls will return the single instance.
addPlugin
Add a new plugin.
findPlugin
Find a plugin. A NULL shared pointer is returned if it has not been added.
showNames
Show the names of all plugins that have been added.

NOTE: Should the method causeMonitor have arguments pvField and pvTop be defined so that they can not be modified. This would be possible if the following was defined:

typedef std::tr1::shared_ptr<const PVField> PVFieldConstPtr;
typedef std::tr1::shared_ptr<const PVStructure> PVStructureConstPtr;
then the definition for causeMonitor could be:
virtual bool causeMonitor(
        PVFieldConstPtr const &pvField,
        PVStructureConstPtr const &pvTop,
        MonitorElementPtr const &monitorElement) = 0;
But just adding these definitions is not sufficient. In addition all methods defined in pvDataCPP must be checked. In particular many of the methods in Convert must have their arguments modified. Big job.

monitorPlugin example

Example Plugin Overview

This section describes an example plugin that:

As an example assume that a channel provided by pvAccess has a top-level structure that represents a power supply.

structure powerSupply
    structure alarm
    structure timeStamp
    structure power
       double value
       structure alarm
       structure display
    structure voltage
       double value
       structure alarm
       structure display
    structure current
       double value
       structure alarm
       structure display

A pvAccess client wants to create a monitor on the powerSupply as follows: The client wants a top-level structure that looks like:

structure powerSupply
    structure alarm
    structure timeStamp
    structure power
       double value
    structure voltage
       double value
    structure current
       double value
In addition the client wants monitors to occur only when one of the monitored fields changes value but not just because a put occurred. Also if only the timeStamp changes value then that should not cause a monitor.

The example monitor plugin implements the semantics the client wants. It can be attached to any field via the following options:

[plugin=onChange,raiseMonitor=value]
This plugin will trigger a monitor for the field only if the field changes value. In addition value equals false means do not raise a monitor for changes to this field. But if a change to another field does cause a monitor the change to this field will be passed to the client.

Assume that the client has already connected to the channel. The client can then issue the commands:

String request("field(alarm[plugin=onChange]");
request += ",timeStamp[plugin=onChange,raiseMonitor=false]";
request += ",power.value[plugin=onChange";
request += ",voltage.value[plugin=onChange";
request += ",current.value[plugin=onChange";

PVStructurePtr pvRequest = createRequest->createRequest(request);

MonitorPtr monitor = channel->createMonitor(monitorRequester,pvRequest);

Example Plugin Code

The header file to create the example has the definition:

class ExampleMonitorPlugin{
public:
    static void create();
};

The implementation is:

class OnChangePlugin : public MonitorPlugin
{
public:
    virtual ~OnChangePlugin(){}
    OnChangePlugin() {}
    bool init(
        FieldConstPtr const &field,
        StructureConstPtr const &top,
        PVStructurePtr const &pvFieldOptions)
   {
        pvField = getPVDataCreate()->createPVField(field);
        raiseMonitor = true;
        if(pvFieldOptions!=NULL) {
            PVStringPtr pvString =
                pvFieldOptions->getSubField<PVString>("raiseMonitor");
                if(pvString!=NULL) {
                    String value = pvString->get();
                    if(value.compare("false")==0) raiseMonitor = false;
                }
        }
        return true;
   }
   virtual String &getName(){return pluginName;}
   virtual bool causeMonitor(
        PVFieldPtr const &pvNew,
        PVStructurePtr const &pvTop,
        MonitorElementPtr const &monitorElement)
   {
       bool isSame = convert->equals(pvNew,pvField);
       if(isSame) return false;
       convert->copy(pvNew,pvField);
       return raiseMonitor;
   }
private:
   PVFieldPtr pvField;
   bool raiseMonitor;
};
class OnChangePluginCreator : public MonitorPluginCreator
{
public:
    virtual String &getName(){return pluginName;}
    virtual MonitorPluginPtr create(
        FieldConstPtr const &field,
        StructureConstPtr const &top,
        PVStructurePtr const &pvFieldOptions)
   {
       OnChangePluginPtr plugin(new OnChangePlugin());
       bool result = plugin->init(field,top,pvFieldOptions);
       if(!result) return MonitorPluginPtr();
       return plugin;
   }

};

void ExampleMonitorPlugin::create()
{
    static OnChangePluginCreatorPtr plugin;
    static Mutex mutex;
    Lock xx(mutex);
    if(plugin==NULL) {
        plugin = OnChangePluginCreatorPtr(new OnChangePluginCreator());
        MonitorPluginManager::get()->addPlugin(pluginName,plugin);
    }
}