This product is made available subject to acceptance of the EPICS open source license.
This document describes pvDatabaseCPP, which is a framework for implementing a network accessable database of smart memory resident records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess. The framework must be extended in order to create record instances. The minimum that an extenson must provide is a top level PVStructure and a process method but the framework provides for complex extensions.
EPICS version 4 is a set of related products in the EPICS
V4 control system programming environment:
relatedDocumentsV4.html
This is the 27-Nov-2012 version of the definition of pvDatabaseCPP. This is the original version.
This is the beginning of the implementation of pvDataBaseCPP. It describes the features that will be provided. The class definitions for PVRecord and PVDatabase are defined but not implemented.
This document descibes a C++ implementation of some of the components in pvIOCJava. It extracts the core components required to create a network accessible database of smart memory resident records. pvDatabaseCPP does not and will not implement any of the specialized support that pvIOCJava provides. Instead other projects will implement the specialized support. It is expected that many services will be created that do not require the full features provided by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of them named pvDatabaseJava.
A brief description of a pvDatase is that it is a network accessible set of smart memory resident records. Each record has data composed of a top level PVStructure. Each record has a name which is the channelName for pvAccess. A local Channel Provider implements the complete ChannelProvider and Channel interfaces as defined by pvAccess. This local provider is accessed by the remote pvAccess server. A record is smart because code can be attached to a record.
This document describes components that provides the following features:
database does not itself implement pvRecord instances. Instead it provides a base classes that make it easy to create record instances. What does have to be implemented is a top level PVStructure and the following two methods:
Directory example/record has an example PVRecord implementation. It implements a counter. The top level structure is:
structure
long value
NOTE: The example compiles but does not build because nothing is implemented.
This is the class description. The example extends PVRecord.
class ExampleRecord :
public virtual PVRecord
{
public:
POINTER_DEFINITIONS(ExampleRecord);
static PVRecordPtr create(epics::pvData::String const & recordName);
virtual ~ExampleRecord();
virtual bool isSynchronous();
virtual void process(
epics::pvDatabase::RecordProcessRequesterPtr const &processRequester);
private:
ExampleRecord(epics::pvData::String const & recordName,
epics::pvData::PVStructurePtr const & pvStructure,
epics::pvData::PVLongPtr const &pvValue);
epics::pvData::PVLongPtr pvValue;
};
where
This is the class implementation.
ExampleRecord::~ExampleRecord(){}
PVRecordPtr ExampleRecord::create(String const & recordName)
{
String properties;
PVStructurePtr pvStructure = getStandardPVField()->scalar(pvLong,properties);
PVLongPtr pvValue = pvStructure->getLongField("value");
PVRecordPtr pvRecord(new ExampleRecord(recordName,pvStructure,pvValue));
return pvRecord;
}
ExampleRecord::ExampleRecord(
String const & recordName,
PVStructurePtr const & pvStructure,
PVLongPtr const &pvValue)
: PVRecord(recordName,pvStructure),
pvValue(pvValue)
{}
bool ExampleRecord::isSynchronous() {return true;}
void ExampleRecord::process(
RecordProcessRequesterPtr const &processRequester)
{
pvValue->put(pvValue->get() + 1);
processRequester->recordProcessResult(Status::Ok);
processRequester->recordProcessComplete();
}
where
This is a main for creating and running the example.
int main(int argc,char *argv[])
{
String recordName("exampleRecord");
PVRecordPtr pvRecord = ExampleRecord::create(recordName);
PVDatabasePtr pvDatabase = PVDatabase::getMaster();
pvDatabase->addRecord(pvRecord);
cout << recordName << "\n";
string str;
while(true) {
cout << "Type exit to stop: \n";
getline(cin,str);
if(str.compare("exit")==0) break;
}
return 0;
}
The main program creates an example record and adds it to the database. It then runs until the process is stopped by typing exit.
Until the process is stopped, pvAccess clients can put and get the value field. For example
pvget exampleRecord pvput exampleRecord 5
Will both work.
This documentation describes the first phase of a phased implementation of pvDatabaseCPP:
Future phases of pvDatabaseCPP should include:
The completion of each phase provides useful features that can be used without waiting for the completion of later phases. The rest of this document discusses only the first phase.
The first phase will only implement record processing, i. e. the process method has to do everything itself without any generic field support. This will be sufficient for starting to implement services. The following are the minimium features required
The following sections provide a first attempt to describe the classes required for the first phase.
The last section gives a brief overview of the features provided by pvIOCJava.
The classes in pvDatabase.h implement a database of memory resident smart records. The next subsection has the definitions for all the classes defined in this header file. It describes the following classes:
Each class is described in a separate subsection.
class PVRecord
{
public:
POINTER_DEFINITIONS(PVRecord);
PVRecord(
epics::pvData::String const & recordName,
epics::pvData::PVStructurePtr const & pvStructure);
virtual ~PVRecord();
virtual void process(
RecordProcessRequesterPtr const &recordProcessRequester) = 0;
virtual bool isSynchronous() = 0;
epics::pvData::String getRecordName();
PVRecordStructurePtr getPVRecordStructure();
PVRecordFieldPtr findPVRecordField(
epics::pvData::PVFieldPtr const & pvField);
void lock();
void unlock();
void registerClient(PVRecordClientPtr const & pvRecordClient);
void unregisterClient(PVRecordClientPtr const & pvRecordClient);
void detachClients();
void beginGroupPut();
void endGroupPut();
void registerListener(PVListenerPtr const & pvListener);
void unregisterListener(PVListenerPtr const & pvListener);
void removeEveryListener();
epics::pvData::Status processRequest();
void queueProcessRequest(
RecordProcessRequesterPtr const &recordProcessRequester);
void addRequester(epics::pvData::RequesterPtr const & requester);
void removeRequester(epics::pvData::RequesterPtr const & requester);
void message(
epics::pvData::String const & message,
epics::pvData::MessageType messageType);
epics::pvData::String toString();
epics::pvData::String toString(int indentLevel);
};
The methods are:
class PVRecordField {
public:
POINTER_DEFINITIONS(PVRecordField);
PVRecordField(
epics::pvData::PVFieldPtr const & pvField,
PVRecordPtr const & pvRecord);
virtual ~PVRecordField();
PVRecordStructurePtr getParent();
epics::pvData::PVFieldPtr getPVField();
epics::pvData::String getFullFieldName();
epics::pvData::String getFullName();
PVRecordPtr getPVRecord();
bool addListener(PVListenerPtr const & pvListener);
void removeListener(PVListenerPtr const & pvListener);
void postPut();
virtual void message(
epics::pvData::String const & message,
epics::pvData::MessageType messageType);
};
When PVRecord is created it creates a PVRecordField for every field in the PVStructure that holds the data. It has the following methods:
class PVRecordStructure : public PVRecordField {
public:
POINTER_DEFINITIONS(PVRecordStructure);
PVRecordStructure(
epics::pvData::PVStructurePtr const & pvStructure,
PVRecordFieldPtrArrayPtr const & pvRecordField);
virtual ~PVRecordStructure();
PVRecordFieldPtrArrayPtr getPVRecordFields();
epics::pvData::PVStructurePtr getPVStructure();
virtual void message(
epics::pvData::String const & message,
epics::pvData::MessageType messageType);
};
When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure that holds the data. It has the following methods:
class PVListener {
public:
POINTER_DEFINITIONS(PVListener);
virtual ~PVListener();
virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
virtual void dataPut(
PVRecordStructurePtr const &
requested,PVRecordFieldPtr const & pvRecordField) = 0;
virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
virtual void unlisten(PVRecordPtr const & pvRecord) = 0;
};
where
class RecordProcessRequester :
virtual public epics::pvData::Requester
{
public:
POINTER_DEFINITIONS(RecordProcessRequester);
virtual ~RecordProcessRequester();
virtual void becomeProcessor() = 0;
virtual void recordProcessResult(epics::pvData::Status status) = 0;
virtual void recordProcessComplete() = 0;
};
where
class PVRecordClient {
POINTER_DEFINITIONS(PVRecordClient);
virtual ~PVRecordClient();
virtual void detach(PVRecordPtr const & pvRecord);
};
where
class PVDatabase : virtual public epics::pvData::Requester {
public:
POINTER_DEFINITIONS(PVDatabase);
static PVDatabasePtr getMaster();
virtual ~PVDatabase();
PVRecordPtr findRecord(epics::pvData::String const& recordName);
bool addRecord(PVRecordPtr const & record);
bool removeRecord(PVRecordPtr const & record);
private:
PVDatabase();
};
where
Not yet described.
A brief description is that it must implement the following components of pvIOCJava:
The following are the direct sub packages of pvIOCJava/src/org/epics/pvioc:
In addition there is one class file JavaIOC.java. This is starting a IOC instance. This is not required for pvIOCCPP which is either a main or runs as part of a V3 IOC.