diff --git a/documentation/pvDatabaseCPP.html b/documentation/pvDatabaseCPP.html index a61cfe3..5ee9213 100644 --- a/documentation/pvDatabaseCPP.html +++ b/documentation/pvDatabaseCPP.html @@ -38,7 +38,7 @@
This is the 11-Dec-2012 version of the definition of pvDatabaseCPP. +
This is the 09-Apr-2013 version of the definition of pvDatabaseCPP.
This is the beginning of the implementation of pvDataBaseCPP. It describes the features that will be provided. @@ -113,22 +113,16 @@ The class definition for 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 +
A brief description of a pvDatabase is that it is a set of network accessible, 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 +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. +The local provider provides access to the records in the pvDatabase. This local provider is accessed by the remote pvAccess server. -A record is smart because code can be attached to a record.
+A record is smart because code can be attached to a record, which is accessed via a method named process. +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 +
database +provides base classes that make it easy to create record instances. +The code attached to each record must create the top level PVStructure and the following two methods:
This document descibes a C++ implementation of some of the components in pvIOCJava, +which also implements a pvDatabase. +It extracts the core components required to create a network accessible database of smart +memory resident records. +pvDatabaseCPP does not implement any of the specialized support that pvIOCJava +provides. +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.
+Similar to epics base, pvIOCJava implements the concept of synchronous and asynchronous record processing. +For pvDatabaseCPP the process method is allowed to block. +Until a need is demonstrated this will remain true. +The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess. +The server side of remote pvAccess creates two threads for each client and always accesses +a record via these threads. +It is expected that these threads will be sufficient to efficently handle all channel methods except +channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests. +If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility, +then the scanning facility will have to provide some way of handling process requests that block.
+Directory example/record has an example PVRecord implementation. -It implements a counter. -The top level structure is:
+The example implements a simple counter. +The example can be run on linux as follows:
-structure - long value --
NOTE: The example compiles but does not build because nothing -is implemented.
+mrk> pwd +/home/hg/pvDatabaseCPP +mrk> bin/linux-x86_64/exampleCounter -This is the class description. -The example extends PVRecord.
+ +The example consists of two components:
+The example resides in src/database. +The complete implementation is in the header file. +A serious implementation would probably break the code into two files: +1) a header, and 2) the implementation. The description consists of
-class ExampleRecord : - public virtual PVRecord +class ExampleCounter; +typedef std::tr1::shared_ptr<ExampleCounter> ExampleCounterPtr; + +class ExampleCounter : + public 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); + POINTER_DEFINITIONS(ExampleCounter); + static ExampleCounterPtr create( + epics::pvData::String const & recordName); + virtual ~ExampleCounter() {} + virtual bool init(); + virtual void process(); private: - ExampleRecord(epics::pvData::String const & recordName, - epics::pvData::PVStructurePtr const & pvStructure, - epics::pvData::PVLongPtr const &pvValue); + ExampleCounter(epics::pvData::String const & recordName, + epics::pvData::PVStructurePtr const & pvStructure); epics::pvData::PVLongPtr pvValue; };
where
This is the class implementation.
+The implementation of create is:
-ExampleRecord::~ExampleRecord(){} - -PVRecordPtr ExampleRecord::create(String const & recordName) +ExampleCounterPtr ExampleCounter::create( + epics::pvData::String const & recordName) { - String properties; - PVStructurePtr pvStructure = getStandardPVField()->scalar(pvLong,properties); - PVLongPtr pvValue = pvStructure->getLongField("value"); - PVRecordPtr pvRecord(new ExampleRecord(recordName,pvStructure,pvValue)); + epics::pvData::PVStructurePtr pvStructure = + epics::pvData::getStandardPVField()->scalar(epics::pvData::pvDouble,""); + ExampleCounterPtr pvRecord( + new ExampleCounter(recordName,pvStructure)); + if(!pvRecord->init()) pvRecord.reset(); 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,bool alreadyLocked) ++This: +
The private constructor is just:
++ExampleCounter::ExampleCounter( + epics::pvData::String const & recordName, + epics::pvData::PVStructurePtr const & pvStructure) +: PVRecord(recordName,pvStructure) +{ } ++The example is very simple. It just calls the base class constructor. +
The implementation of init is:
++bool ExampleCounter::init() { - if(!alreadyLocked) lock(); - pvValue->put(pvValue->get() + 1); - processRequester->recordProcessResult(Status::Ok); - unlock(); - processRequester->recordProcessComplete(); - dequeueProcessRequest(processRequester); + + initPVRecord(); + epics::pvData::PVFieldPtr pvField; + pvValue = getPVStructure()->getLongField("value"); + if(pvValue.get()==NULL) return false; + return true; }-
where
-This is a main for creating and running the example.
+This: +The implementation of process is:
++void ExampleCounter::process() +{ + pvValue->put(pvValue->get() + 1.0); +} ++It just adds 1.0 to the current value. +
This is in test/server. +The main program is:
int main(int argc,char *argv[]) { - String recordName("exampleRecord"); - PVRecordPtr pvRecord = ExampleRecord::create(recordName); - PVDatabasePtr pvDatabase = PVDatabase::getMaster(); - pvDatabase->addRecord(pvRecord); - cout << recordName << "\n"; + PVDatabasePtr master = PVDatabase::getMaster(); + ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create(); + String recordName("exampleCounter"); + PVRecordPtr pvRecord = ExampleCounter::create(recordName); + bool result = master->addRecord(pvRecord); + cout << "result of addRecord " << recordName << " " << result << endl; + pvRecord.reset(); + cout << "exampleServer\n"; string str; while(true) { cout << "Type exit to stop: \n"; @@ -281,16 +322,15 @@ int main(int argc,char *argv[]) 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: +This documentation describes the first phase of a phased implementation of pvDatabaseCPP:
Future phases of pvDatabaseCPP should include:
+Future phases of pvDatabaseCPP might include:
The first phase will only implement record processing, i. e. @@ -340,9 +384,9 @@ 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 following sections describes the classes required for the first phase.
The classes in pvDatabase.h implement a database of memory resident +
This directory has the following files:
+The classes in pvDatabase.h describe a database of memory resident smart records. It describes the following classes:
@@ -428,16 +505,17 @@ class PVRecord { public: POINTER_DEFINITIONS(PVRecord); + + virtual bool init() {initPVRecord(); return true;} + virtual void process() {} + + static PVRecordPtr create( + epics::pvData::String const & recordName, + epics::pvData::PVStructurePtr const & pvStructure); PVRecord( epics::pvData::String const & recordName, epics::pvData::PVStructurePtr const & pvStructure); virtual ~PVRecord(); - virtual void process( - RecordProcessRequesterPtr const &recordProcessRequester, - bool alreadyLocked) = 0; - virtual bool isSynchronous() = 0; - virtual bool requestImmediatePut(epics::pvData::PVFieldPtr const &pvField); - virtual void immediatePutDone(); virtual void destroy(); epics::pvData::String getRecordName(); PVRecordStructurePtr getPVRecordStructure(); @@ -445,25 +523,20 @@ public: epics::pvData::PVFieldPtr const & pvField); bool addRequester(epics::pvData::RequesterPtr const & requester); bool removeRequester(epics::pvData::RequesterPtr const & requester); + inline void lock_guard() { epics::pvData::Lock theLock(mutex); } void lock(); void unlock(); bool tryLock(); void lockOtherRecord(PVRecordPtr const & otherRecord); - void addPVRecordClient(PVRecordClientPtr const & pvRecordClient); - void removePVRecordClient(PVRecordClientPtr const & pvRecordClient); + bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient); + bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient); void detachClients(); + bool addListener(PVListenerPtr const & pvListener); + bool removeListener(PVListenerPtr const & pvListener); void beginGroupPut(); void endGroupPut(); - void queueProcessRequest( - RecordProcessRequesterPtr const &recordProcessRequester); - void dequeueProcessRequest( - RecordProcessRequesterPtr const &recordProcessRequester); - void queuePutRequest( - RecordPutRequesterPtr const &recordPutRequester); - void putDone( - RecordPutRequesterPtr const &recordPutRequester); - virtual epics::pvData::String getRequesterName(); - void message( + epics::pvData::String getRequesterName() {return getRecordName();} + virtual void message( epics::pvData::String const & message, epics::pvData::MessageType messageType); void message( @@ -472,63 +545,39 @@ public: epics::pvData::MessageType messageType); void toString(epics::pvData::StringBuilder buf); void toString(epics::pvData::StringBuilder buf,int indentLevel); - //init MUST be called after derived class is constructed - void init(); - -}; +protected: + void initPVRecord(); + epics::pvData::PVStructurePtr getPVStructure(); + PVRecordPtr getPtrSelf() + { + return shared_from_this(); + } +private: +... +}
The methods are:
Derived classes must implement this method. + This method Must call initPVRecord.
+Derived classes must implement this method. + The base implementation does nothing.
+Derived classes must implement this method.
-A client must only call this method when - RecordProcessRequester::becomeProcessor is called as a result - of a queueProcessRequest. - A client can either call lock before calling processs - or let process lock the record. - If a client wants to put data into the record it should lock, put, and then call - process.
-If the record is synchronous, process will return only when all processing - is complete. If the record is asynchronous then process arranges for some - other thread to do the processing and returns.
-When processing is done the record calls two client callbacks:
-The purpose is to allow the implementation to provide fields - that allow a client to abort process. - For example a motor record might provide a field stop
-The default always returns false.
-A record implementation can override the default and return true. - In it does requestImmediatePut it returns with the record locked.
-The client can change the value of the associated field and then call - immediatePutDone
-The default does nothing.
-Must be called by client as a result of a call to requestImmediatePut - that returns true.
-The default does nothing.
This is for code that wants to change data in a record without processing. - If RecordPutRequester::requestResult is called with result true - then the record is locked and the client can make changes. - When done the client must call putDone
-@@ -621,6 +660,14 @@ public: virtual void message( epics::pvData::String const & message, epics::pvData::MessageType messageType); +protected: + PVRecordFieldPtr getPtrSelf() + { + return shared_from_this(); + } + virtual void init(); +private: +... };
When PVRecord is created it creates a PVRecordField for every field in the PVStructure @@ -669,6 +716,10 @@ public: epics::pvData::PVStructurePtr getPVStructure(); virtual void removeListener(PVListenerPtr const & pvListener); virtual void postPut(); +protected: + virtual void init(); +private: +... };
When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure @@ -737,63 +788,6 @@ public:
-class RecordProcessRequester : - virtual public PVRecordClient, - virtual public epics::pvData::Requester -{ -public: - POINTER_DEFINITIONS(RecordProcessRequester); - virtual ~RecordProcessRequester(); - virtual void recordDestroyed() = 0; - virtual void becomeProcessor() = 0; - virtual void recordProcessResult(epics::pvData::Status status) = 0; - virtual void recordProcessComplete() = 0; -}; --
where
--class RecordPutRequester : - virtual public PVRecordClient -{ -public: - POINTER_DEFINITIONS(RecordPutRequester); - virtual ~RecordPutRequester(); - virtual void requestResult(bool result) = 0; -}; --
where
-class PVDatabase : virtual public epics::pvData::Requester { @@ -804,6 +798,10 @@ public: PVRecordPtr findRecord(epics::pvData::String const& recordName); bool addRecord(PVRecordPtr const & record); bool removeRecord(PVRecordPtr const & record); + virtual epics::pvData::String getRequesterName(); + virtual void message( + epics::pvData::String const &message, + epics::pvData::MessageType messageType); private: PVDatabase(); }; @@ -822,70 +820,21 @@ private:
Not yet described.
+Not yet described. +It is only of interest to someone who wants to understand how it works. +
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.