TODO
CONTENTS
This product is available via the open source license described at the end of this document.
PVData is one of a set of related projects: pvData, pvAccess, and javaIOC. It describes and implements the data that the other projects use. Thus it is not useful by itself but understanding pvData is required in order to understand the other projects. The reader should also become familar with pvAccess and javaIOC, which are located via the same sourceforge site as this project. Project PVAccess provides network support for transporting pvData. Project javaIOC provides a memory resident "smart" database of pvData data.
This document describes the C++ implementation of pvData, which was first implemented in Java. A C++ implementation of pvAccess is being developed in parallel with pvDataCPP. In the future a C++ implementation of javaIOC will be developed and the name javaIOC will become pvIOC.
pvData (Process Variable Data) defines and implements an efficent way to store, access, and transmit memory resident structured data.
The javaIOC implements a Process Variable (PV) Database, which is a memory resident database holding pvData with the following features:
pvData was initially created to support the javaIOC and was part of the javaIOC project. It is now a separate project that is used by the javaIOC. In addition to the javaIOC, pvData is intended for use by 1) channel access clients, 2) Interface between client and network, 3) Interface between network and channel access server, 4) Interface between server and IOC database. Since it is an interface to data, it could also be used by other systems, e.g. TANGO, TINE, etc. A high level Physics application can hold data as pvData. By starting a channel access server, the data can made available to network clients.
pvData contains everything required to support Channel Access and Channel Access clients and servers.
This project has many concepts that are similar to EPICS (Experimental Physics and Industrial Control System). This C++ implementation uses the EPICS build system and also EPICS libCom. The directory structure for this project is a standard EPICS application. The following source directories appear under pvDataApp:
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. Also the toString introspection and data methods described below present data in this format.
PVData supports structured data. All data appears as a top level structure. A structure has an ordered set of fields where each field has a fieldDef defined as follows:
type fieldName = value // comment
where = value is optional and // indicates the the rest of the line is a comment.
type is one of the following:
structure fieldName
fieldDef
...
or
structureName fieldName
fieldDef
...
For structure fieldName each
fieldDef must have a unique
fieldName within the structure
For structureName fieldName the structureName must be the fieldName of a previously defined top level structure
structure[] fieldName
structureDef
...
Thus a structure array is an array where each element is a structure but
all elements have the same introspection interface. For introspection the
structureDef appears once without any data valuies.Each field can optionally have data values assigned to it. The definition of the data values depends on the type. For scalars the data value is whatever is valid for the type.
For scalar arrays the syntax is:
= [value,...,value]
where each value is a valid scalar data value depending on the type. Thus it is a comma separated set of values enclosed in [] White space is permitted surrounding each comma.
Define the following top level structure:
structure timeStamp
long secondsPastEpoch
int nanoSeconds
Then the following can be defined
structure scalarDoubleExample
double value = 1.0
timeStamp timeStamp
scalar arrayDoubleExample
double[] value = [1.0,2.0]
timeStamp timeStamp
structure point
double x
double y
structure lineExample
point begin
x 0
y 0
point end
x 10
y 10
structure[] structureArrayExample
structure point
double x 0.0
double y 0.0
structure point
double x 10.0
double y 10.0
Directory pvDataApp/pv has header files that completely describe pvData. The implementation is provided in directory pvDataApp/factory. Test programs appears on testApp/pvTest.
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 section describes the complete set of data and introspection interfaces for pvData.
A class FieldCreate creates introspection objects. A class PVDataCreate creates data objects. A class Convert provides a rich set of methods for converting and copying data between fields.
Directory pvDataApp/pv has the following header files:
This provides C/C++ definitions for the pvData primitive types: boolean, byte, short, int, long, float, double, and string. Because pvData is network data, the C++ implementation must implement the proper semantics for the primitive types.
pvType.h provides the proper semantics. It has the definitions:
typedef bool boolean; typedef int8_t byte; typedef int8_t int8; typedef int16_t int16; typedef int32_t int32; typedef int64_t int64; typedef uint32_t uint32; typedef uint64_t uint64; // float and double are types typedef std::string String; typedef bool * BooleanArray; typedef int8 * ByteArray; typedef int16 * ShortArray; typedef int32 * IntArray; typedef int64 * LongArray; typedef float * FloatArray; typedef double * DoubleArray; typedef String* StringArray; // convenience definition for toString methods typedef std::string * StringBuilder;
where
This subsection describes pvIntrospect.h
Given a pvname, which consists of a record name and field name, it is possible to introspect the field without requiring access to data. The reflection and data interfaces are separate because the data may not be available. For example when a pvAccess client connects to a PV, the client library can obtain the reflection information without obtaining any data. Only when a client issues an I/O request will data be available. This separation is especially important for arrays and structures so that a client can discover the type without requiring that a large data array or structure be transported over the network.
Types are defined as:
enum Type {
scalar,
scalarArray,
structure,
structureArray;
};
class TypeFunc {
public:
static void toString(StringBuilder buf,const Type type);
};
enum ScalarType {
pvBoolean,
pvByte, pvShort, pvInt, pvLong,
pvFloat,pvDouble,
pvString;
};
class ScalarTypeFunc {
public:
static bool isInteger(ScalarType type);
static bool isNumeric(ScalarType type);
static bool isPrimitive(ScalarType type);
static ScalarType getScalarType(String value);
static void toString(StringBuilder buf,ScalarType scalarType);
};
Type is one of the following:
ScalarType is one of the following:
TypeFunction is a set of convenience methods for Type
ScalarTypeFunction is a set of convenience methods for ScalarType
This section describes the reflection interfaces which provide the following:
class Field : private NoDefaultMethods {
public:
int getReferenceCount() const;
String getFieldName() const;
Type getType() const;
virtual void toString(StringBuilder buf) const{toString(buf,0);}
virtual void toString(StringBuilder buf,int indentLevel) const;
void renameField(String newName);
void incReferenceCount() const;
void decReferenceCount() const;
void dumpReferenceCount(StringBuilder buf,int indentLevel) const;
virtual bool operator==(const Field& field) const;
virtual bool operator!=(const Field& field) const;
protected:
Field(String fieldName,Type type);
virtual ~Field();
};
class Scalar : public Field{
public:
ScalarType getScalarType() const {return scalarType;}
virtual void toString(StringBuilder buf) const{toString(buf,0);}
virtual void toString(StringBuilder buf,int indentLevel) const;
protected:
Scalar(String fieldName,ScalarType scalarType);
virtual ~Scalar();
...
};
class ScalarArray : public Field{
public:
ScalarType getElementType() const {return elementType;}
virtual void toString(StringBuilder buf) const{toString(buf,0);}
virtual void toString(StringBuilder buf,int indentLevel) const;
protected:
ScalarArray(String fieldName,ScalarType scalarType);
virtual ~ScalarArray();
...
};
class StructureArray : public Field{
public:
StructureConstPtr getStructure() const {return pstructure;}
virtual void toString(StringBuilder buf) const{toString(buf,0);}
virtual void toString(StringBuilder buf,int indentLevel) const;
protected:
StructureArray(String fieldName,StructureConstPtr structure);
virtual ~StructureArray();
...
};
class Structure : public Field {
public:
int getNumberFields() const {return numberFields;}
FieldConstPtr getField(String fieldName) const;
int getFieldIndex(String fieldName) const;
FieldConstPtrArray getFields() const {return fields;}
void appendField(FieldConstPtr field);
void appendFields(int numberFields,FieldConstPtrArray fields);
void removeField(int index);
virtual void toString(StringBuilder buf) const{toString(buf,0);}
virtual void toString(StringBuilder buf,int indentLevel) const;
protected:
Structure(String fieldName, int numberFields,FieldConstPtrArray fields);
virtual ~Structure();
...
};
class FieldCreate : NoDefaultMethods {
public:
FieldConstPtr create(String fieldName,FieldConstPtr field) const;
ScalarConstPtr createScalar(String fieldName,ScalarType scalarType) const;
ScalarArrayConstPtr createScalarArray(String fieldName,
ScalarType elementType) const;
StructureConstPtr createStructure (String fieldName,
int numberFields,FieldConstPtrArray fields) const;
StructureArrayConstPtr createStructureArray(String fieldName,
StructureConstPtr structure) const;
};
extern FieldCreate * getFieldCreate();
The following methods are common to all of the reflection class descriptions:
Field has the methods:
Scalar has the methods:
ScalarArray has the methods:
StructureArray has the methods:
Structure has the methods:
FieldCreate has the methods:
The file standardField.h has a class description for creating or sharing Field objects for standard fields. For each type of standard object two methods are defined: one with no properties and with properties. The property field is a comma separated string of property names of the following: alarm, timeStamp, display, control, and valueAlarm. An example is "alarm,timeStamp,valueAlarm". The method with properties creates a structure with fields named fieldName and each of the property names. Each property field is a structure defining the property. The details about each property is given in the section named "Property". For example the call:
StructureConstPtr example = standardField->scalar(
String("value"),
pvDouble,
String("value,alarm,timeStamp"));
Will result in a Field definition that has the form:
structure example
double value
structure alarm
structure severity
int index
string[] choices
structure timeStamp
long secondsPastEpoch
int nanoSeconds
In addition there are methods that create each of the property structures, i.e. the methods named: alarm, .... enumeratedAlarm."
standardField.h contains:
class StandardField : private NoDefaultMethods {
public:
StandardField();
~StandardField();
ScalarConstPtr scalar(String fieldName,ScalarType type);
StructureConstPtr scalar(String fieldName,
ScalarType type,String properties);
ScalarArrayConstPtr scalarArray(String fieldName,
ScalarType elementType);
StructureConstPtr scalarArray(String fieldName,
ScalarType elementType, String properties);
StructureArrayConstPtr structureArray(String fieldName,
StructureConstPtr structure);
StructureConstPtr structureArray(String fieldName,
StructureConstPtr structure,String properties);
StructureConstPtr structure(String fieldName,
int numFields,FieldConstPtrArray fields);
StructureConstPtr enumerated(String fieldName,
StringArray choices);
StructureConstPtr enumerated(String fieldName,
StringArray choices, String properties);
ScalarConstPtr scalarValue(ScalarType type);
StructureConstPtr scalarValue(ScalarType type,String properties);
ScalarArrayConstPtr scalarArrayValue(ScalarType elementType);
StructureConstPtr scalarArrayValue(ScalarType elementType,
String properties);
StructureArrayConstPtr structureArrayValue(StructureConstPtr structure);
StructureConstPtr structureArrayValue(StructureConstPtr structure,
String properties);
StructureConstPtr structureValue(
int numFields,FieldConstPtrArray fields);
StructureConstPtr enumeratedValue(StringArray choices);
StructureConstPtr enumeratedValue(StringArray choices,
String properties);
StructureConstPtr alarm();
StructureConstPtr timeStamp();
StructureConstPtr display();
StructureConstPtr control();
StructureConstPtr booleanAlarm();
StructureConstPtr byteAlarm();
StructureConstPtr shortAlarm();
StructureConstPtr intAlarm();
StructureConstPtr longAlarm();
StructureConstPtr floatAlarm();
StructureConstPtr doubleAlarm();
StructureConstPtr enumeratedAlarm();
private:
static void init();
};
extern StandardField * getStandardField();
Where
This section defines the Java Interfaces for accessing the data within a PV record.
PVField is the base interface for accessing data. A data structure consists of a top level PVStructure. Every field of every structure of every top level structure has a PVField associated with it.
class PostHandler {
public:
virtual void postPut() = 0;
};
class PVField
: public Requester,
public Serializable,
private NoDefaultMethods
{
public:
virtual ~PVField();
virtual void setRequester(Requester *prequester);
int getFieldOffset() ;
int getNextFieldOffset() ;
int getNumberFields() ;
PVAuxInfo * getPVAuxInfo();
bool isImmutable() ;
void setImmutable();
FieldConstPtr getField() ;
PVStructure * getParent() ;
bool renameField(String newName);
void postPut() ;
void setPostHandler(PostHandler *postHandler);
virtual void toString(StringBuilder buf) ;
virtual void toString(StringBuilder buf,int indentLevel) ;
virtual bool operator==(PVField &pv) = 0;
virtual bool operator!=(PVField &pv) = 0;
protected:
PVField(PVStructure *parent,FieldConstPtr field);
private:
};
PostHandler is a class that must be implemented by any code that calls setPostHandler. It's single virtual method. postPut is called whenever PVField::postPut is called.
Requester,Serializable, and NoDefaultMethods are described in a later section.
The public methods for PVField are:
AuxInfo (Auxillary Information) is information about a field that is application specific. It will not be available outside the application that implements the database. In particular it will not be made available to Channel Access. It is used by the database itself to override the default implementation of fields. The JavaIOC uses it for attaching support code. Database Configuration and other tools can use it for configuration information. Each Field and each PVField can have have an arbitrary number of auxInfos. An auxInfo is a (key,PVScalar) pair where key is a string.
class PVAuxInfo : private NoDefaultMethods {
public:
PVAuxInfo(PVField *pvField);
~PVAuxInfo();
PVField * getPVField();
PVScalar * createInfo(String key,ScalarType scalarType);
PVScalarMap getInfos();
PVScalar * getInfo(String key);
void toString(StringBuilder buf);
void toString(StringBuilder buf,int indentLevel);
private:
};
where
class PVScalar : public PVField {
public:
virtual ~PVScalar();
ScalarConstPtr getScalar() ;
protected:
PVScalar(PVStructure *parent,ScalarConstPtr scalar);
};
The interfaces for primitive data types are:
class PVBoolean : public PVScalar {
public:
virtual ~PVBoolean();
virtual bool get() = 0;
virtual void put(bool value) = 0;
protected:
PVBoolean(PVStructure *parent,ScalarConstPtr scalar)
: PVScalar(parent,scalar) {}
private:
};
class PVByte : public PVScalar {
public:
virtual ~PVByte();
virtual int8 get() = 0;
virtual void put(int8 value) = 0;
protected:
PVByte(PVStructure *parent,ScalarConstPtr scalar)
: PVScalar(parent,scalar) {}
private:
};
class PVShort : public PVScalar {
public:
virtual ~PVShort();
virtual int16 get() = 0;
virtual void put(int16 value) = 0;
protected:
PVShort(PVStructure *parent,ScalarConstPtr scalar)
: PVScalar(parent,scalar) {}
private:
};
class PVInt : public PVScalar{
public:
virtual ~PVInt();
virtual int32 get() = 0;
virtual void put(int32 value) = 0;
protected:
PVInt(PVStructure *parent,ScalarConstPtr scalar)
: PVScalar(parent,scalar) {}
private:
};
class PVLong : public PVScalar {
public:
virtual ~PVLong();
virtual int64 get() = 0;
virtual void put(int64 value) = 0;
protected:
PVLong(PVStructure *parent,ScalarConstPtr scalar)
: PVScalar(parent,scalar) {}
private:
};
class PVFloat : public PVScalar {
public:
virtual ~PVFloat();
virtual float get() = 0;
virtual void put(float value) = 0;
protected:
PVFloat(PVStructure *parent,ScalarConstPtr scalar)
: PVScalar(parent,scalar) {}
private:
};
class PVDouble : public PVScalar {
public:
virtual ~PVDouble();
virtual double get() = 0;
virtual void put(double value) = 0;
protected:
PVDouble(PVStructure *parent,ScalarConstPtr scalar)
: PVScalar(parent,scalar) {}
private:
};
class PVString : public PVScalar {
public:
virtual ~PVString();
virtual String get() = 0;
virtual void put(String value) = 0;
protected:
PVString(PVStructure *parent,ScalarConstPtr scalar)
: PVScalar(parent,scalar) {}
private:
};
PVArray is the base interface for all the other PV Array interfaces. It extends PVField and provides the additional methods:
class PVArray : public PVField, public SerializableArray {
public:
virtual ~PVArray();
int getLength() ;
void setLength(int length);
int getCapacity() ;
bool isCapacityMutable() ;
void setCapacityMutable(bool isMutable);
virtual void setCapacity(int capacity) = 0;
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) = 0;
virtual void deserialize(ByteBuffer *pbuffer,
DeserializableControl *pflusher) = 0;
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, int offset, int count) = 0;
protected:
PVArray(PVStructure *parent,FieldConstPtr field);
void setCapacityLength(int capacity,int length);
private:
class PVArrayPvt * pImpl;
};
The interface for each array type has get and put methods which have the same arguments except for the data type. For example PVDoubleArray is:
class DoubleArrayData {
public:
DoubleArrayData(){}
~DoubleArrayData(){};
DoubleArray data;
int offset;
};
class PVDoubleArray : public PVScalarArray {
public:
virtual ~PVDoubleArray();
virtual void setCapacity(int capacity) = 0;
virtual int get(int offset, int length, DoubleArrayData *data) = 0;
virtual int put(int offset,int length, DoubleArray from, int fromOffset) = 0;
virtual void shareData(DoubleArray value,int capacity,int length) = 0;
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) = 0;
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher) = 0;
protected:
PVDoubleArray(PVStructure *parent,ScalarArrayConstPtr scalar);
private:
};
Get "exposes" it's internal array by setting data.data and data.offset. The caller is responsible for copying the array elements. This violates the principle that objects should not expose their internal data but is done for efficency. For example it makes it possible to copy between arrays with identical element types without requiring an intermediate array.
Both get and put return the number of elements actually transfered. The arguments are:
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:
void doubleArray getArray(PVDoubleArray *pv,doubleArray *to,int lenArray)
{
int len = pv->getLength();
if(lenArray<len) len = lenArray;
DoubleArrayData data;
int offset = 0;
while(offset < len) {
int num = pv->get(offset,(len-offset),&data);
doubleArray from = &data.data[data.offset];
doubleArray to = &to[offset]
int numbytes = num*sizeof(double);
memcopy(from,to,numBytes);
offset += num;
}
}
shareData results in the PVArray using the primitive array that is passed to this method. This is most useful for immutable arrays. In this case the caller must set the PVArray to be immutable. In the PVArray is not immutable then it is the applications responsibility to coordinate access to the array. Again this violates the principle that objects should not expose their internal data but is important for immutable arrays. For example pvData and the javaIOC define many enumerated structures where an enumerated structure has 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. For many enumerated structures choices is 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.
class PVScalarArray : public PVArray {
public:
virtual ~PVScalarArray();
ScalarArrayConstPtr getScalarArray() ;
virtual void setCapacity(int capacity) = 0;
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) = 0;
virtual void deserialize(ByteBuffer *pbuffer,
DeserializableControl *pflusher) = 0;
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, int offset, int count) = 0;
protected:
PVScalarrray(PVStructure *parent,ScalarArrayConstPtr scalarArray);
private:
};
class BooleanArrayData {
public:
BooleanArray data;
int offset;
};
class PVBooleanArray : public PVScalarArray {
public:
virtual ~PVBooleanArray();
virtual void setCapacity(int capacity) = 0;
virtual int get(int offset, int length, BooleanArrayData *data) = 0;
virtual int put(int offset,int length, BooleanArray from, int fromOffset) = 0;
virtual void shareData(BooleanArray value,int capacity,int length) = 0;
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) = 0;
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher) = 0;
protected:
PVBooleanArrayclass ByteArrayData {
public:
ByteArray data;
int offset;
};
class PVByteArray : public PVScalarArray {
public:
virtual ~PVByteArray();
virtual void setCapacity(int capacity) = 0;
virtual int get(int offset, int length, ByteArrayData *data) = 0;
virtual int put(int offset,int length, ByteArray from, int fromOffset) = 0;
virtual void shareData(ByteArray value,int capacity,int length) = 0;
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) = 0;
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher) = 0;
protected:
PVByteArray(PVStructure *parent,ScalarArrayConstPtr scalar);
private:
};
class ShortArrayData {
public:
ShortArray data;
int offset;
};
class PVShortArray : public PVScalarArray {
public:
virtual ~PVShortArray();
virtual void setCapacity(int capacity) = 0;
virtual int get(int offset, int length, ShortArrayData *data) = 0;
virtual int put(int offset,int length, ShortArray from, int fromOffset) = 0;
virtual void shareData(ShortArray value,int capacity,int length) = 0;
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) = 0;
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher) = 0;
protected:
PVShortArray(PVStructure *parent,ScalarArrayConstPtr scalar);
private:
};
class IntArrayData {
public:
IntArray data;
int offset;
};
class PVIntArray : public PVScalarArray {
public:
virtual ~PVIntArray();
virtual void setCapacity(int capacity) = 0;
virtual int get(int offset, int length, IntArrayData *data) = 0;
virtual int put(int offset,int length, IntArray from, int fromOffset)= 0;
virtual void shareData(IntArray value,int capacity,int length)= 0;
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) = 0;
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher)= 0;
protected:
PVIntArray(PVStructure *parent,ScalarArrayConstPtr scalar);
private:
};
class LongArrayData {
public:
LongArray data;
int offset;
};
class PVLongArray : public PVScalarArray {
public:
virtual ~PVLongArray();
virtual void setCapacity(int capacity) = 0;
virtual int get(int offset, int length, LongArrayData *data) = 0;
virtual int put(int offset,int length, LongArray from, int fromOffset)= 0;
virtual void shareData(LongArray value,int capacity,int length)= 0;
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) = 0;
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher)= 0;
protected:
PVLongArray(PVStructure *parent,ScalarArrayConstPtr scalar);
private:
};
class FloatArrayData {
public:
FloatArray data;
int offset;
};
class PVFloatArray : public PVScalarArray {
public:
virtual ~PVFloatArray();
virtual void setCapacity(int capacity) = 0;
virtual int get(int offset, int length, FloatArrayData *data) = 0;
virtual int put(int offset,int length, FloatArray from, int fromOffset)= 0;
virtual void shareData(FloatArray value,int capacity,int length)= 0;
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) = 0;
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher)= 0;
protected:
PVFloatArray(PVStructure *parent,ScalarArrayConstPtr scalar);
private:
};
class DoubleArrayData {
public:
DoubleArrayData(){}
~DoubleArrayData(){};
DoubleArray data;
int offset;
};
class PVDoubleArray : public PVScalarArray {
public:
virtual ~PVDoubleArray();
virtual void setCapacity(int capacity) = 0;
virtual int get(int offset, int length, DoubleArrayData *data) = 0;
virtual int put(int offset,int length, DoubleArray from, int fromOffset) = 0;
virtual void shareData(DoubleArray value,int capacity,int length) = 0;
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) = 0;
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher) = 0;
protected:
PVDoubleArray(PVStructure *parent,ScalarArrayConstPtr scalar);
private:
};
class StringArrayData {
public:
StringArray data;
int offset;
};
class PVStringArray : public PVScalarArray {
public:
virtual ~PVStringArray();
virtual void setCapacity(int capacity) = 0;
virtual int get(int offset, int length, StringArrayData *data) = 0;
virtual int put(int offset,int length, StringArray from, int fromOffset)= 0;
virtual void shareData(StringArray value,int capacity,int length)= 0;
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) = 0;
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher)= 0;
protected:
PVStringArray(PVStructure *parent,ScalarArrayConstPtr scalar);
private:
};
The interface for a structure is:
class PVStructure : public PVField,public BitSetSerializable {
public:
virtual ~PVStructure();
StructureConstPtr getStructure();
PVFieldPtrArray getPVFields();
PVField *getSubField(String fieldName);
PVField *getSubField(int fieldOffset);
void appendPVField(PVField *pvField);
void appendPVFields(int numberFields,PVFieldPtrArray pvFields);
void removePVField(String fieldName);
PVBoolean *getBooleanField(String fieldName);
PVByte *getByteField(String fieldName);
PVShort *getShortField(String fieldName);
PVInt *getIntField(String fieldName);
PVLong *getLongField(String fieldName);
PVFloat *getFloatField(String fieldName);
PVDouble *getDoubleField(String fieldName);
PVString *getStringField(String fieldName);
PVStructure *getStructureField(String fieldName);
PVScalarArray *getScalarArrayField(
String fieldName,ScalarType elementType);
PVStructureArray *getStructureArrayField(String fieldName);
String getExtendsStructureName();
bool putExtendsStructureName(
String extendsStructureName);
virtual bool operator==(PVField &pv) ;
virtual bool operator!=(PVField &pv) ;
virtual void serialize(
ByteBuffer *pbuffer,SerializableControl *pflusher) ;
virtual void deserialize(
ByteBuffer *pbuffer,DeserializableControl *pflusher);
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, int offset, int count) ;
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher,BitSet *pbitSet) ;
virtual void deserialize(ByteBuffer *pbuffer,
DeserializableControl*pflusher,BitSet *pbitSet);
protected:
PVStructure(PVStructure *parent,StructureConstPtr structure);
private:
class PVStructurePvt * pImpl;
};
where
The interface for an array of structures is:
class StructureArrayData {
public:
PVStructurePtrArray data;
int offset;
};
class PVStructureArray : public PVArray {
public:
virtual ~PVStructureArray();
virtual StructureArrayConstPtr getStructureArray() = 0;
virtual void setCapacity(int capacity) = 0;
virtual int append(int number) = 0;
virtual bool remove(int offset,int number) = 0;
virtual void compress() = 0;
virtual int get(int offset, int length,
StructureArrayData *data) = 0;
virtual int put(int offset,int length,
PVStructurePtrArray from, int fromOffset) = 0;
virtual void shareData( PVStructurePtrArray value,int capacity,int length) = 0;
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) = 0 ;
virtual void deserialize(ByteBuffer *buffer,
DeserializableControl *pflusher) = 0;
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, int offset, int count) = 0;
protected:
PVStructureArray(PVStructure *parent,
StructureArrayConstPtr structureArray);
private:
};
where
The other methods are similar to the methods for other array types.
Important Note: Code that calls put must be aware of the fact that introsection interfaces are shared. When creating array elements the shared introspection interface must be used. The following shows an example:
void put(PVStructure* powerSupplyArrayStruct)
{
PVStructurePtrArray structureArray = new PVStructurePtr[3];
StructureConstPtr structure =
powerSupplyArray->getStructureArray()->getStructure();
structure->incReferenceCount();
structureArray[0] = pvDataCreate->createPVStructure(0,structure);
structure->incReferenceCount();
structureArray[1] = pvDataCreate->createPVStructure(0,structure);
structure->incReferenceCount();
structureArray[2] = pvDataCreate->createPVStructure(0,structure);
powerSupplyArray->put(0,3,structureArray,0);
}
Note that incReferenceCount is called before each element is created.
PVDataCreate is an interface that provides methods that create PVField interfaces. A factory is provided that creates PVDataCreate.
class PVDataCreate {
public:
PVField *createPVField(PVStructure *parent,
FieldConstPtr field);
PVField *createPVField(PVStructure *parent,
String fieldName,PVField * fieldToClone);
PVScalar *createPVScalar(PVStructure *parent,ScalarConstPtr scalar);
PVScalar *createPVScalar(PVStructure *parent,
String fieldName,ScalarType scalarType);
PVScalar *createPVScalar(PVStructure *parent,
String fieldName,PVScalar * scalarToClone);
PVScalarArray *createPVScalarArray(PVStructure *parent,
ScalarArrayConstPtr scalarArray);
PVScalarArray *createPVScalarArray(PVStructure *parent,
String fieldName,ScalarType elementType);
PVScalarArray *createPVScalarArray(PVStructure *parent,
String fieldName,PVScalarArray * scalarArrayToClone);
PVStructureArray *createPVStructureArray(PVStructure *parent,
StructureArrayConstPtr structureArray);
PVStructure *createPVStructure(PVStructure *parent,
StructureConstPtr structure);
PVStructure *createPVStructure(PVStructure *parent,
String fieldName,int numberFields,FieldConstPtrArray fields);
PVStructure *createPVStructure(PVStructure *parent,
String fieldName,PVStructure *structToClone);
protected:
PVDataCreate();
friend PVDataCreate * getPVDataCreate();
};
extern PVDataCreate * getPVDataCreate();
where
A class StandardPVField has methods for creating standard data fields. Like class StandardField it has two forms of the methods which create a field, one without properties and one with properties. Again the properties is some combination of alarm, timeStamp, control, display, and valueAlarm. And just like StandardField there are methods to create thye standard properties. The meythods are:
class StandardPVField : private NoDefaultMethods {
public:
StandardPVField();
~StandardPVField();
PVScalar * scalar(PVStructure *parent,String fieldName,ScalarType type);
PVStructure * scalar(PVStructure *parent,
String fieldName,ScalarType type,String properties);
PVScalarArray * scalarArray(PVStructure *parent,
String fieldName,ScalarType elementType);
PVStructure * scalarArray(PVStructure *parent,
String fieldName,ScalarType elementType, String properties);
PVStructureArray * structureArray(PVStructure *parent,
String fieldName,StructureConstPtr structure);
PVStructure* structureArray(PVStructure *parent,
String fieldName,StructureConstPtr structure,String properties);
PVStructure * enumerated(PVStructure *parent,
String fieldName,StringArray choices);
PVStructure * enumerated(PVStructure *parent,
String fieldName,StringArray choices, String properties);
PVScalar * scalarValue(PVStructure *parent,ScalarType type);
PVStructure * scalarValue(PVStructure *parent,
ScalarType type,String properties);
PVScalarArray * scalarArrayValue(PVStructure *parent,ScalarType elementType);
PVStructure * scalarArrayValue(PVStructure *parent,
ScalarType elementType, String properties);
PVStructureArray * structureArrayValue(PVStructure *parent,
StructureConstPtr structure);
PVStructure * structureArrayValue(PVStructure *parent,
StructureConstPtr structure,String properties);
PVStructure * enumeratedValue(PVStructure *parent,StringArray choices);
PVStructure * enumeratedValue(PVStructure *parent,
StringArray choices, String properties);
PVStructure * alarm(PVStructure *parent);
PVStructure * timeStamp(PVStructure *parent);
PVStructure * display(PVStructure *parent);
PVStructure * control(PVStructure *parent);
PVStructure * booleanAlarm(PVStructure *parent);
PVStructure * byteAlarm(PVStructure *parent);
PVStructure * shortAlarm(PVStructure *parent);
PVStructure * intAlarm(PVStructure *parent);
PVStructure * longAlarm(PVStructure *parent);
PVStructure * floatAlarm(PVStructure *parent);
PVStructure * doubleAlarm(PVStructure *parent);
PVStructure * enumeratedAlarm(PVStructure *parent);
PVStructure * powerSupply(PVStructure *parent);
};
NOTE about copying immutable array fields. If an entire immutable array field is copied to another array that has the same elementType, both offsets are 0, and the length is the length of the source array, then the shareData method of the target array is called and the target array is set immutable. Thus the source and target share the same primitive array.
This section describes the supported conversions between data types.
class Convert : NoDefaultMethods {
public:
Convert();
~Convert();
void getFullName(StringBuilder buf,PVField *pvField);
bool equals(PVField *a,PVField *b);
void getString(StringBuilder buf,PVField * pvField,int indentLevel);
void getString(StringBuilder buf,PVField *pvField);
void fromString(PVScalar *pv, String from);
int fromString(PVScalarArray *pv, String from);
int fromStringArray(PVScalarArray *pv, int offset, int length,
StringArray from, int fromOffset);
int toStringArray(PVScalarArray *pv, int offset, int length,
StringArray to, int toOffset);
bool isCopyCompatible(FieldConstPtr from, FieldConstPtr to);
void copy(PVField *from,PVField *to);
bool isCopyScalarCompatible(
ScalarConstPtr from, ScalarConstPtr to);
void copyScalar(PVScalar *from, PVScalar *to);
bool isCopyScalarArrayCompatible(ScalarArrayConstPtr from,
ScalarArrayConstPtr to);
int copyScalarArray(PVScalarArray *from, int offset,
PVScalarArray *to, int toOffset, int length);
bool isCopyStructureCompatible(
StructureConstPtr from, StructureConstPtr to);
void copyStructure(PVStructure *from, PVStructure *to);
bool isCopyStructureArrayCompatible(
StructureArrayConstPtr from, StructureArrayConstPtr to);
void copyStructureArray(
PVStructureArray *from, PVStructureArray *to);
int8 toByte(PVScalar *pv);
int16 toShort(PVScalar *pv);
int32 toInt(PVScalar *pv);
int64 toLong(PVScalar *pv);
float toFloat(PVScalar *pv);
double toDouble(PVScalar *pv);
String toString(PVScalar *pv);
void fromByte(PVScalar *pv,int8 from);
void fromShort(PVScalar *pv,int16 from);
void fromInt(PVScalar *pv, int32 from);
void fromLong(PVScalar *pv, int64 from);
void fromFloat(PVScalar* pv, float from);
void fromDouble(PVScalar *pv, double from);
int toByteArray(PVScalarArray *pv, int offset, int length,
ByteArray to, int toOffset);
int toShortArray(PVScalarArray *pv, int offset, int length,
ShortArray to, int toOffset);
int toIntArray(PVScalarArray *pv, int offset, int length,
IntArray to, int toOffset);
int toLongArray(PVScalarArray *pv, int offset, int length,
LongArray to, int toOffset);
int toFloatArray(PVScalarArray *pv, int offset, int length,
FloatArray to, int toOffset);
int toDoubleArray(PVScalarArray *pv, int offset, int length,
DoubleArray to, int toOffset);
int fromByteArray(PVScalarArray *pv, int offset, int length,
ByteArray from, int fromOffset);
int fromShortArray(PVScalarArray *pv, int offset, int length,
ShortArray from, int fromOffset);
int fromIntArray(PVScalarArray *pv, int offset, int length,
IntArray from, int fromOffset);
int fromLongArray(PVScalarArray *pv, int offset, int length,
LongArray from, int fromOffset);
int fromFloatArray(PVScalarArray *pv, int offset, int length,
FloatArray from, int fromOffset);
int fromDoubleArray(PVScalarArray *pv, int offset, int length,
DoubleArray from, int fromOffset);
void newLine(StringBuilder buf, int indentLevel);
};
extern Convert * getConvert();
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.
All code in project pvDataCPP appears in namespace:
namespace epics { namespace pvData {
// ...
}}
As described above pvType.h provides C++ typdefs for the pvData primitive types.
File pvInterspect.h, which is described in the next section has the typedefs:
typedef Field const * FieldConstPtr; typedef FieldConstPtr * FieldConstPtrArray; typedef Scalar const * ScalarConstPtr; typedef ScalarArray const * ScalarArrayConstPtr; typedef Structure const * StructureConstPtr; typedef StructureArray const * StructureArrayConstPtr;
These are definitions for introspection objects. All introspecton objects are immutable and always accessed via pointers.
File pvData.h has the typedefs:
typedef std::map<String,PVScalar * > PVScalarMap; typedef PVScalarMap::const_iterator PVScalarMapIter; typedef PVStructure * PVStructurePtr; typedef PVStructurePtr* PVStructurePtrArray; typedef PVField* PVFieldPtr; typedef PVFieldPtr * PVFieldPtrArray; typedef bool * BooleanArray; typedef int8 * ByteArray; typedef int16 * ShortArray; typedef int32 * IntArray; typedef int64 * LongArray; typedef float * FloatArray; typedef double * DoubleArray; typedef String * StringArray;
where
Any class that does not want the compiler to generate default methods can privately extend the following class which is defined in file noDefaultMethods.h:
class NoDefaultMethods {
protected:
// allow by derived objects
NoDefaultMethods(){};
~NoDefaultMethods(){}
private:
// do not implment
NoDefaultMethods(const NoDefaultMethods&);
NoDefaultMethods & operator=(const NoDefaultMethods &);
};
Introspection objects are meant to be shared. The constructors and destructors are all private or protected so that an introspection object can only be created via a call to one of the FieldCreate methods described abofe. The introspection implementation, together with PVField keeps reference counts and automatically deletes objects when the reference count goes to 0. Code that uses introspection objects always accesses introspection objects via pointers never via references or direct access to the object. When an introspection interface is created via one of the methods of class FieldCreate the reference count is set to 1. When any code wants to get an introspection interface from an existing data object and use it for another data object, i.e. share the interface, then incReferenceCount MUST be called.
The introspection destructors are all private. When code, normally ~PVField, is done using an intrispection interface it calls decReferenceCount. If the field is a structure then it is a recursive call. When the reference count for a field becomes 0 the field is deleted. Note than user code should never call decReferenceCount since the destructor for PVField calls it.
All PVData data objects must publically extend PVField, which does not allow default methods but does have a virtual destructor. It is expected that each data object is "owned" by some entity. For example a pvIOC (not implemented) database will own all records and thus all PVData data objects in the database. It is the ONLY entity that will create and destroy the data objects. All other code only receives pointers to the data objects. Before a record is deleted any code that is connected to a record is notified before the record is deleted. After deletion all pointers to data in the record are invalid. Similarly pvAccess creates and destroys PVData objects and notifies clients before destroying PVData data objects.
The classes in property, i.e. alarm, timeStamp, display, and control are all meant to be free copied and shared. They can be created on the stack. In most cases it is not necessary to create them on the heap.
Other clases privately extend NoDefaultClasses and are not normally meant to be extended. Thus they can only be created via "new" and must be destroyed via "delete".
Assume that code wants to print two fields from a PVStructure:
The following code uses introspection to get the desired information.
void getValueAndTimeStamp(PVStructurePtr pvStructure,Stringbuffer buf) {
PVField *valuePV = pvStructure->getSubField(String("value"));
if(valuePV==0) {
buf += "value field not found";
return;
}
buf += "value ";
valuePV->toString(buf);
PVField *timeStampPV = pvStructure->getSubField(String("timeStamp"));
if(timeStampPV==0) {
buf += "timeStamp field not found";
return;
}
buf += " timeStamp ";
timeStampPV->toString(buf);
}
Example of creating a scalar field.
PVDataCreate *pvDataCreate = getPVDataCreate();
PVDouble *pvValue = pvDataCreate->createPVScalar(
0,
String("value"),
pvDouble);
Create an alarm structure the hard way
FieldCreate *fieldCreate = getFieldCreate();
PVDataCreate *pvDataCreate = getPVDataCreate();
FieldConstPtrArray fields = new FieldConstPtr[2];
fields[0] = fieldCreate->createScalar(String("severity"),pvInt);
fields[1] = fieldCreate->createScalar(String("message"),pvString);
StructureConstPtralarmField = fieldCreate->createStructure(String("alarm"),2,fields);
Create an alarm structure the easy way.
StandardPVField *standardPVField = getStandardPVField();
PVStructure *pvAlarm = standardPVField->alarm(parent);
Create a PVStructure with field name example that has a double value field and a timeStamp and alarm. Do it the easy way.
StandardPVField *standardPVField = getStandardPVField();
PVStructure *pvStructure = standardPVField->scalar(
0, //parent is null
String("example"),
String("timeStamp,alarm"))
Only fields named "value" have properties. A record can have multiple value fields, which can appear in the top level structure of a record or in a substructure. All other fields in the structure containing a value field are considered properties of the value field. The fieldname is also the property name. The value field can have any type, i.e. scalar, scalarArray, or structure. Typical property fields are timeStamp, alarm, display, control, and history The timeStamp is a special case. If it appears anywhere in the structure hieraracy above a value field it is a property of the value field.
For example the following top level structure has a single value field. The value field has properties alarm, timeStamp, and display.
structure counterOutput
structure alarm
structure timeStamp
double value
structure display
string description "Sample Description"
string format "%f"
string units volts
structure limit
double low 0.0
double high 10.0
The following example has three value fields each with properties alarm and timeStamp. Voltage, Current, and Power each have a different alarms but all share the timeStamp.
structure powerSupplyValueStructure
double value
structure alarm
structure powerSupplySimple
structure alarm
structure timeStamp
powerSupplyValueStructure voltage
powerSupplyValueStructure power
powerSupplyValueStructure current
The following field names have special meaning, i.e. support properties for general purpose clients.
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:
The model allows for device records. A device record has structure fields that support the PVData data model. For example a powerSupport record can have fields power, voltage, current that each support the PVData data model.
Except for enumerated, each property has two files: a property.h and a pvProperty.h . For example: timeStamp.h and pvTimeStamp.h In each case the property.h file defined methods for manipulating the property data and the pvProperty.h provides methods to transfer the property data to/from a pvData structure.
All methods copy data via copy by value semantics, i.e. not by pointer or by reference. No property class calls new or delete and all allow the compiler to generate default methods. All allow a class instance to be generated on the stack. For example the following is permitted:
void example(PVField *pvField) {
Alarm alarm;
PVAlarm pvAlarm;
bool result;
result = pvAlarm.attach(pvField);
assert(result);
Alarm al;
al.setMessage(String("testMessage"));
al.setSeverity(majorAlarm);
result = pvAlarm.set(al);
assert(result);
alarm = pvAlarm.get();
...
}
A timeStamp is represented by the following structure
structure timeStamp
int64 secondsPartEpoch
int32 nanoSeconds
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 normlized, i. e. it has is 0<=nanoSeconds<nanoSecPerSec..
Two header files are provided for manipulating time stamps: timeStamp.h and pvTimeStamp.h timeStamp.h defines a time stamp independent of pvData, i.e. it is a generally useful class for manipulating timeStamps. pvTimeStamp.h is a class that can be attached to a time stamp pvData structure. It provides get and set methods to get/set a TimeStamp as defined by timeStamp.h
This provides
extern int32 milliSecPerSec;
extern int32 microSecPerSec;
extern int32 ;
extern int64 posixEpochAtEpicsEpoch;
class TimeStamp {
public:
TimeStamp();
TimeStamp(int64 secondsPastEpoch,int32 nanoSeconds = 0);
//default constructors and destructor are OK
//This class should not be extended
void normalize();
void fromTime_t(const time_t &);
void toTime_t(time_t &) const;
int64 getSecondsPastEpoch();
int64 getEpicsSecondsPastEpoch() const;
int32 getNanoSeconds() const;
void put(int64 secondsPastEpoch,int32 nanoSeconds = 0);
void put(int64 milliseconds);
void getCurrent();
double toSeconds() const ;
bool operator==(TimeStamp const &) const;
bool operator!=(TimeStamp const &) const;
bool operator<=(TimeStamp const &) const;
bool operator< (TimeStamp const &) const;
bool operator>=(TimeStamp const &) const;
bool operator> (TimeStamp const &) const;
static double diff(TimeStamp const & a,TimeStamp const & b);
TimeStamp & operator+=(int64 seconds);
TimeStamp & operator-=(int64 seconds);
TimeStamp & operator+=(double seconds);
TimeStamp & operator-=(double seconds);
int64 getMilliseconds(); // milliseconds since epoch
};
where
The TimeStamp class provides arithmetic operations on time stamps. The result is always kept in normalized form, which means that the nano second portion is 0≤=nano<nanoSecPerSec. Note that it is OK to have timeStamps for times previous to the epoch.
TimeStamp acts like a primitive. It can be allocated on the stack and the compiler is free to generate default methods, i.e. copy constructor, assignment constructor, and destructor.
One use for TimeStamp is to time how long a section of code takes to execute. This is done as follows:
TimeStamp startTime;
TimeStamp endTime;
...
startTime.getCurrent();
// code to be measured for elapsed time
endTime.getCurrent();
double time = TimeStamp::diff(endTime,startTime);
class PVTimeStamp {
public:
PVTimeStamp();
//default constructors and destructor are OK
//This class should not be extended
//returns (false,true) if pvField(isNot, is valid timeStamp structure
bool attach(PVField *pvField);
void detach();
bool isAttached();
// following throw logic_error if not attached to PVField
// a set returns false if field is immutable
void get(TimeStamp &) const;
bool set(TimeStamp const & timeStamp);
};
where
An alarm structure is defined as follows:
structure alarm
int32 severity
String message
Note that severity is NOT defined as an enumerated structure. The reason is performance, i. e. prevent passing the array of choice strings everywhere. The file alarm.h provides the choice strings. Thus all code that needs to know about alarms share the exact same choice strings.
Two header files are provided for manipulating alarms: alarm.h and pvAlarm.h alarm.h defines a time stamp independent of pvData, i.e. it is a generally useful class for manipulating alarms. pvAlarm.h is a class that can be attached to a time stamp pvData structure. It provides get and set methods to get/set a Alarm as defined by alarm.h
enum AlarmSeverity {
noAlarm,minorAlarm,majorAlarm,invalidAlarm
};
class AlarmSeverityFunc {
public:
static AlarmSeverity getSeverity(int value);
static StringArray getSeverityNames();
};
class Alarm {
public:
Alarm();
//default constructors and destructor are OK
String getMessage();
void setMessage(String value);
AlarmSeverity getSeverity() const;
void setSeverity(AlarmSeverity value);
};
Alarm Severity defines the possible alarm severities
AlarmSeverity has the methods:
Alarm has the methods:
class PVAlarm {
public:
PVAlarm() : pvSeverity(0),pvMessage(0) {}
//default constructors and destructor are OK
//returns (false,true) if pvField(isNot, is valid enumerated structure
//An automatic detach is issued if already attached.
bool attach(PVField *pvField);
void detach();
bool isAttached();
// each of the following throws logic_error is not attached to PVField
// set returns false if field is immutable
void get(Alarm &) const;
bool set(Alarm const & alarm);
};
where
Control information is represented by the following structure
structure control
structure limit
double low
double high
Two header files are provided for manipulating controls: control.h and pvControl.h control.h defines a time stamp independent of pvData, i.e. it is a generally useful class for manipulating controls. pvControl.h is a class that can be attached to a time stamp pvData structure. It provides get and set methods to get/set a Control as defined by control.h
class Control {
public:
Control();
//default constructors and destructor are OK
double getLow() const;
double getHigh() const;
void setLow(double value);
void setHigh(double value);
};
where
class PVControl {
public:
PVControl();
//default constructors and destructor are OK
//returns (false,true) if pvField(isNot, is valid enumerated structure
//An automatic detach is issued if already attached.
bool attach(PVField *pvField);
void detach();
bool isAttached();
// each of the following throws logic_error is not attached to PVField
// set returns false if field is immutable
void get(Control &) const;
bool set(Control const & control);
};
where
Display information is represented by the following structure
structure display
structure limit
double low
double high
string description
string format
string units
Two header files are provided for manipulating controls: display.h and pvDisplay.h display.h defines display information independent of pvData, i.e. it is a generally useful class for manipulating display infomation. pvDisplay.h is a class that can be attached to a display pvData structure. It provides get and set methods to get/set a Diaplay as defined by diaplay.h
class Display {
public:
Display();
//default constructors and destructor are OK
double getLow() const;
double getHigh() const;
void setLow(double value);
void setHigh(double value);
String getDescription() const;
void setDescription(String value);
String getFormat() const;
void setFormat(String value);
String getUnits() const;
void setUnits(String value);
};
where
class PVDisplay {
public:
PVDisplay()
: pvDescription(0),pvFormat(),pvUnits(),pvLow(),pvHigh() {}
//default constructors and destructor are OK
//An automatic detach is issued if already attached.
bool attach(PVField *pvField);
void detach();
bool isAttached();
// each of the following throws logic_error is not attached to PVField
// a set returns false if field is immutable
void get(Display &) const;
bool set(Display const & display);
};
where
An enumerated structure is a structure that has fields:
structure
int32 index
string[] choices
For enumerated structures a single header file pvEnumerted.h is available
class PVEnumerated {
public:
PVEnumerated();
//default constructors and destructor are OK
//This class should not be extended
//returns (false,true) if pvField(isNot, is valid enumerated structure
//An automatic detach is issued if already attached.
bool attach(PVField *pvField);
void detach();
bool isAttached();
// each of the following throws logic_error is not attached to PVField
// a set returns false if field is immutable
bool setIndex(int32 index);
int32 getIndex();
String getChoice();
bool choicesMutable();
StringArray getChoices();
int32 getNumberChoices();
bool setChoices(StringArray choices,int32 numberChoices);
};
where
Directory factory has code that implements everything described by the files in directory pv
TypeFunc.cpp implements the functions for the enums defined in pvIntrospecct.h
FieldCreateFactory.cpp automatically creates a single instance of FieldCreate and implements getFieldCreate.
PVDataCreateFactory.cpp automatically creates a single instance of PVDataCreate and implements getPVDataCreate.
PVAuxInfoImpl.cpp implements auxInfo.
Convert.cpp automatically creates a single instance of Convert and implements getConvert.
The following are included by PVDataCreateFactory and implement the corresponding class described in pvData.h:
There is a large set of files named "DefaultXXX.cpp" that implement each of the other classes described in pvData.cpp. Each is included and used by PVDataCreate.
This package provides utility code:
Note that directory testApp/misc has test code for all the classes in misc. The test code also can be used as examples.
This is adapted from the java.util.BitSet. bitSet.h is:
class BitSet /*: public Serializable*/ {
public:
BitSet();
BitSet(uint32 nbits);
virtual ~BitSet();
void flip(uint32 bitIndex);
void set(uint32 bitIndex);
void clear(uint32 bitIndex);
void set(uint32 bitIndex, bool value);
bool get(uint32 bitIndex) const;
void clear();
int32 nextSetBit(uint32 fromIndex) const;
int32 nextClearBit(uint32 fromIndex) const;
bool isEmpty() const;
uint32 cardinality() const;
BitSet& operator&=(const BitSet& set);
BitSet& operator|=(const BitSet& set);
BitSet& operator^=(const BitSet& set);
BitSet& operator-=(const BitSet& set);
BitSet& operator=(const BitSet &set);
void or_and(const BitSet& set1, const BitSet& set2);
bool operator==(const BitSet &set) const;
bool operator!=(const BitSet &set) const;
void toString(StringBuilder buffer);
void toString(StringBuilder buffer, int indentLevel) const;
private:
};
where
Clears all of the bits in this bitSet whose corresponding bit is set in the specified bitSet.
A ByteBuffer is used to serialize and deserialize primitive data. File byteBuffer.h is:
class ByteBuffer {
public:
ByteBuffer(int size = 32, int byteOrder = EPICS_BYTE_ORDER);
~ByteBuffer();
ByteBuffer* clear();
ByteBuffer* flip();
ByteBuffer* rewind();
bool getBoolean();
int8 getByte();
int16 getShort();
int32 getInt();
int64 getLong();
float getFloat();
double getDouble();
void get(char* dst, int offset, int count);
ByteBuffer* put(const char* src, int offset, int count);
ByteBuffer* putBoolean(bool value);
ByteBuffer* putByte(int8 value);
ByteBuffer* putShort(int16 value);
ByteBuffer* putInt(int32 value);
ByteBuffer* putLong(int64 value);
ByteBuffer* putFloat(float value);
ByteBuffer* putDouble(double value);
inline int getSize() const;
inline int getArrayOffset() const;
inline int getPosition() const;
inline int getLimit() const;
inline int getRemaining() const;
inline int getByteOrder() const;
inline const char* getArray() const;
// TODO must define arrays
private:
};
x
This class provides coordinates activity between threads. One thread can wait for the event and the other signals the event.
class Event : private NoDefaultMethods {
public:
Event(bool full);
~Event();
void signal();
bool wait (); /* blocks until full */
bool wait ( double timeOut ); /* false if empty at time out */
bool tryWait (); /* false if empty */
private:
epicsEventId id;
};
where
File epicsException.h describes:
class BaseException : public std::exception {
public:
BaseException(const char* message, const char* file,
int line, std::exception* cause = 0);
virtual ~BaseException();
virtual const char* what();
void toString(std::string& str, unsigned int depth = 0);
static inline void getStackTrace(
std::string* trace, unsigned int skip_frames = 0, unsigned int max_frames = 63)
private:
// ...
}
x
An Executor is a thread that can execute commands. The user can request that a single command be executed.
class ExecutorNode;
class Command {
public:
virtual void command() = 0;
};
class Executor : private NoDefaultMethods {
public:
Executor(String threadName,ThreadPriority priority);
~Executor();
ExecutorNode * createNode(Command *command);
void execute(ExecutorNode *node);
private:
class ExecutorPvt *pImpl;
};
Command is a class that must be implemented by the code that calls execute. It contains the single virtual method command, which is the command to execute.
Executor has the methods:
LinkedList implements a double linked list that requires a user to allocate the nodes. It is more efficent that std::list. linkedList.h is a template that is both a complete description and definition. It uses linkedListVoid for method definitions. linkedListVoid is not meant for use except via linkedList.
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. An exception is thrown if an attempt is made to put a node on a list if the node is already on a list.
template <typename T>
class LinkedList;
template <typename T>
class LinkedListNode : private LinkedListVoidNode {
public:
LinkedListNode(T *object);
~LinkedListNode();
T *getObject();
bool isOnList();
};
template <typename T>
class LinkedList : private LinkedListVoid {
public:
LinkedList();
~LinkedList();
int getLength();
void addTail(LinkedListNode<T> *node);
void addHead(LinkedListNode<T> *node);
void insertAfter(LinkedListNode<T> *node,
LinkedListNode<T> *addNode);
void insertBefore(LinkedListNode<T> *node,
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> *node);
LinkedListNode<T> *getPrev(LinkedListNode<T> *node;
bool isEmpty();
bool contains(T *object);
};
LinkedListNode has the methods:
LinkedList has the methods:
lock.h is:
class Mutex {
public:
Mutex() : id(epicsMutexMustCreate()){}
~Mutex() { epicsMutexDestroy(id) ;}
void lock(){epicsMutexMustLock(id);}
void unlock(){epicsMutexUnlock(id);}
private:
epicsMutexId id;
};
class Lock : private NoDefaultMethods {
public:
explicit Lock(Mutex *pm)
: mutexPtr(pm)
{mutexPtr->lock();}
~Lock(){mutexPtr->unlock();}
private:
Mutex *mutexPtr;
};
This is a complete description and definition of lock and mutex. These make it easy to have a lock that is as easy to use as Java synchronize. To protect some object just create a Mutex for the object and then in any method to be synchronized just have code like:
class SomeClass {
private
Mutex mutex;
...
public
SomeClass() : mutex(Mutex()) {}
...
void method()
{
Lock xx(&mutex);
...
}
The method will take the lock when xx is created and release the lock when the current code block completes.
Another example of Lock is initialization code that must initialize only once. This can be implemented as follows:
static void init(void) {
static Mutex mutex;
Lock xx(&mutex);
if(alreadyInitialized) return;
// initialization
}
class MessageNode {
public:
String getMessage() const;
MessageType getMessageType() const;
};
class MessageQueue : private NoDefaultMethods {
public:
MessageQueue(int size);
~MessageQueue();
MessageNode *get();
// must call release before next get
void release();
// return (false,true) if message (was not, was) put into queue
bool put(String message,MessageType messageType,bool replaceLast);
bool isEmpty() const;
bool isFull() const;
int getClearOverrun();
};
This is for use by code that wants to handle messages without blocking higher priority threads.
A messageNode is a class with two public data members:
A messageQueue is an interface with public methods:
Look at miscTest/testMessageQueue.cpp for an example.
If a class privately extends this class then the compiler can not create any of the following: default constructor, default copy constructror, or default assignment contructor.
/* This is based on Item 6 of
* Effective C++, Third Edition, Scott Meyers
*/
class NoDefaultMethods {
protected:
// allow by derived objects
NoDefaultMethods(){};
~NoDefaultMethods(){}
private:
// do not implment
NoDefaultMethods(const NoDefaultMethods&);
NoDefaultMethods & operator=(const NoDefaultMethods &);
};
A PVField extends Requester. Requester is present so that when database errors are found there is someplace to send a message.
enum MessageType {
infoMessage,warningMessage,errorMessage,fatalErrorMessage
};
extern StringArray messageTypeName;
class Requester {
public:
virtual String getRequesterName() = 0;
virtual void message(String message,MessageType messageType) = 0;
};
where
This is a helper class for serialization, which is required for sending and receiving pvData over the nerwork.
class SerializeHelper : public NoDefaultMethods {
public:
static void writeSize(int s, ByteBuffer* buffer,
SerializableControl* flusher);
static int readSize(ByteBuffer* buffer,
DeserializableControl* control);
static void serializeString(const String& value,
ByteBuffer* buffer,SerializableControl* flusher);
static void serializeSubstring(const String& value, int offset,
int count, ByteBuffer* buffer,
SerializableControl* flusher);
static String deserializeString(ByteBuffer* buffer,
DeserializableControl* control);
private:
};
class SerializableControl {
public:
virtual void flushSerializeBuffer() =0;
virtual void ensureBuffer(int size) =0;
};
class DeserializableControl {
public:
virtual void ensureData(int size) =0;
};
class Serializable {
public:
virtual void serialize(ByteBuffer *buffer,
SerializableControl *flusher) = 0;
virtual void deserialize(ByteBuffer *buffer,
DeserializableControl *flusher) = 0;
};
class BitSetSerializable {
public:
virtual void serialize(ByteBuffer *buffer,
SerializableControl *flusher,BitSet *bitSet) = 0;
virtual void deserialize(ByteBuffer *buffer,
DeserializableControl *flusher,BitSet *bitSet) = 0;
};
class SerializableArray : public Serializable {
public:
virtual void serialize(ByteBuffer *buffer,
SerializableControl *flusher, int offset, int count) = 0;
};
where
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.
x
x
This is a facility that allows a class to report how many objects of that class have been created and destroyed. This can help find memory leaks.
typedef int64 (*getTotal)();
class ConstructDestructCallback : private NoDefaultMethods {
public:
String getConstructName();
int64 getTotalConstruct();
int64 getTotalDestruct();
int64 getTotalReferenceCount();
private:
};
class ShowConstructDestruct : private NoDefaultMethods {
public:
static void registerCallback(
String name,
getTotalFunc construct,
getTotalFunc destruct,
getTotalFunc reference,
deleteStaticFunc deleteFunc);
ConstructDestructCallback* getConstructDestructCallback(String name);
void constuctDestructTotals(FILE *fd);
static void showDeleteStaticExit(FILE *fd);
private:
};
extern ShowConstructDestruct* getShowConstructDestruct();
ConstructDestructCallback is implemented by ShowConstructDestruct. MARTY EDIT how many instances of the class has been created and how many destroyed. The clas must implement this class and register it with ShowConstructDestruct. To see an example of how this is implemented look at misc/thread.cpp.
The public methods of ConstructDestructCallback are:
The public methods of ShowConstructDestruct are:
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.
The Status methods are:
The StatusCreate methods are:
enum ThreadPriority {
lowestPriority,
lowerPriority,
lowPriority,
middlePriority,
highPriority,
higherPriority,
highestPriority
};
class ThreadPriorityFunc {
public:
static unsigned int const * const getEpicsPriorities();
static int getEpicsPriority(ThreadPriority threadPriority);
};
class Runnable {
public:
virtual void run() = 0;
};
class Thread;
class Thread : private NoDefaultMethods {
public:
Thread(String name,ThreadPriority priority,Runnable *runnableReady);
~Thread();
String getName();
ThreadPriority getPriority();
static void showThreads(StringBuilder buf);
static void sleep(double seconds);
private:
};
Runnable must be implement by code that wants to be run via a thread. It has one virtual method: run. Run is the code that is run as a thread. When run compeletes it can not be restarted. If code wants to delete a thread then it MUST arrange that the run returns before the thread can be deleted. An exception is thrown if run remains active when delete is called.
Thread has the methods:
delete pthread;
TimeFunction is a facility that measures the average number of seconds a function call requires. When timeCall is called, it calls function in a loop. It starts with a loop of one iteration. If the total elapsed time is less then .1 seconds it increases the number of iterrations by a factor of 10. It keeps repeating until the elapsed time is greater than .1 seconds. It returns the average number of seconds per call.
class TimeFunctionRequester {
public:
virtual void function() = 0;
};
class TimeFunction : private NoDefaultMethods {
public:
TimeFunction(TimeFunctionRequester *requester);
~TimeFunction();
double timeCall();
private:
TimeFunctionRequester *requester;
};
TimeFunctionRequester must be implemented by code that wants to time how long a function takes. It has the single method:
TimeFunction has the methods:
This provides a general purpose timer. It allows a user callback to be called after a delay or periodically.
class TimerCallback {
public:
virtual void callback() = 0;
virtual void timerStopped() = 0;
};
class TimerNode : private NoDefaultMethods {
public:
TimerNode(TimerCallback *timerCallback);
~TimerNode();
void cancel();
bool isScheduled();
private:
};
class Timer : private NoDefaultMethods {
public:
Timer(String threadName, ThreadPriority priority);
~Timer();
void scheduleAfterDelay(TimerNode *timerNode,double delay);
void schedulePeriodic(TimerNode *timerNode,double delay,double period);
private:
};
TimerCallback must be implemented by the user. It has the following methods:
In order to schedule a callback client code must allocate a TimerNode It can be used to schedule multiple callbacks. It has the methods:
delete timerNode;
Timer has the methods:
This provides a queue which has an immutable capacity. When the queue is full the user code is expected to keep using the current element until a new free element becomes avalable.
template <typename T>
class QueueElement : private QueueElementVoid {
public:
T *getObject() { return static_cast<T *>(QueueElementVoid::getObject());}
protected:
};
template <typename T>
class Queue : private QueueVoid {
public:
Queue(T *array[],int number);
~Queue();
void clear();
int getNumberFree();
int capacity();
QueueElement<T> *getFree();
void setUsed(QueueElement<T> *queueElement);
QueueElement<T> *getUsed();
void releaseUsed(QueueElement<T> *queueElement);
};
miscTest/queueTest.cpp provides an example of how to define a queue.
The queue methods are:
A queue is created as follows:
class MyClass;
typedef MyQueueElement<MyClass> MyElement;
typedef MyQueue<MyClass> MyQueue;
int numElement = 5;
...
MyClass *array[numElements];
for(int i=0; i<numElements; i++) {
array[i] = new MyClass();
}
MyQueue *queue = new MyQueue(array,numElements);
A producer calls getFree and setUsed via code like the following:
MyClass *getFree() {
MyElement *element = queue->getFree();
if(element==0) return 0;
return element->getObject();
}
A consumer calls getUsed and releaseUsed via code like the following:
while(true) {
MyElement *element = queue->getUsed();
if(element==0) break;
MyClass *myClass = element->getObject();
// do something with myClass
queue->releaseUsed(element);
}
The following is also provided:
class BitSetUtil : private NoDefaultMethods {
public:
static bool compress(BitSet *bitSet,PVStructure *pvStructure);
};
This provides functions that operate on a BitSet for a PVStructure. It currently has only one method:
MultiChoice defines an array of strings and a bit set that selects an arbitrary set of the choices. This will be implemented if the java version is accepted.
NOT DONE
Copyright (c) 2008 Martin R. Kraimer
Copyright (c) 2007 Control System Laboratory,
(COSYLAB) Ljubljana Slovenia
Copyright (c) 2010 Brookhaven National Laboratory
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
________________________________________________________________________
This software is in part copyrighted by Brookhaven National Laboratory(BNL)
In no event shall BNL be liable to any party for direct, indirect,
special, incidental, or consequential damages arising out of the use of
this software, its documentation, or any derivatives thereof, even if
BNL has been advised of the possibility of such damage.
BNL specifically disclaims any warranties, including, but not limited
to, the implied warranties of merchantability, fitness for a particular
purpose, and non-infringement. This software is provided on an "as is"
basis, and BNL has no obligation to provide maintenance, support,
updates, enhancements, or modifications.
________________________________________________________________________
This software is in part copyrighted by the BERLINER SPEICHERRING
GESELLSCHAFT FUER SYNCHROTRONSTRAHLUNG M.B.H. (BESSY), BERLIN, GERMANY.
In no event shall BESSY be liable to any party for direct, indirect,
special, incidental, or consequential damages arising out of the use of
this software, its documentation, or any derivatives thereof, even if
BESSY has been advised of the possibility of such damage.
BESSY specifically disclaims any warranties, including, but not limited
to, the implied warranties of merchantability, fitness for a particular
purpose, and non-infringement. This software is provided on an "as is"
basis, and BESSY has no obligation to provide maintenance, support,
updates, enhancements, or modifications.
________________________________________________________________________
This software is in part copyrighted by the Deutsches Elektronen-Synchroton,
Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY.
In no event shall DESY be liable to any party for direct, indirect,
special, incidental, or consequential damages arising out of the use of
this software, its documentation, or any derivatives thereof, even if
DESY has been advised of the possibility of such damage.
DESY specifically disclaims any warranties, including, but not limited
to, the implied warranties of merchantability, fitness for a particular
purpose, and non-infringement. This software is provided on an "as is"
basis, and DESY has no obligation to provide maintenance, support,
updates, enhancements, or modifications.
________________________________________________________________________