more code implemented. See pvDatabaseCPP.html for details

This commit is contained in:
Marty Kraimer
2013-04-18 15:16:26 -04:00
parent 124d28d33e
commit 26c977c0ae
16 changed files with 1725 additions and 201 deletions
+105 -81
View File
@@ -38,7 +38,7 @@
<h1>pvDatabaseCPP</h1>
<!-- Maturity: Working Draft or Request for Comments, or Recommendation, and date. -->
<h2 class="nocount">EPICS v4 Working Group, Working Draft, 09-Apr-2013</h2>
<h2 class="nocount">EPICS v4 Working Group, Working Draft, 17-Apr-2013</h2>
<dl>
<dt>Latest version:</dt>
<dd><a
@@ -46,11 +46,11 @@
</dd>
<dt>This version:</dt>
<dd><a
href="http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvDatabaseCPP/raw-file/tip/documentation/pvDatabaseCPP_20130409.html">pvDatabaseCPP20131211.html</a>
href="http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvDatabaseCPP/raw-file/tip/documentation/pvDatabaseCPP_20130417.html">pvDatabaseCPP20130417.html</a>
</dd>
<dt>Previous version:</dt>
<dd><a
href="http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvDatabaseCPP/raw-file/tip/documentation/pvDatabaseCPP_20131211.html">pvDatabaseCPP20131211.html</a>
href="http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvDatabaseCPP/raw-file/tip/documentation/pvDatabaseCPP_20121211.html">pvDatabaseCPP20121211.html</a>
</dd>
<dt>Editors:</dt>
<dd>Marty Kraimer, BNL</dd>
@@ -68,42 +68,29 @@ which is a framework for implementing a network accessable database of smart mem
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 can be extended in order to create record instances that implements services.
The minimum that an extenson must provide is a top level PVStructure and a process method
but the framework provides for complex extensions.</p>
The minimum that an extenson must provide is a top level PVStructure and a process method.
</p>
<p>EPICS version 4 is a set of related products in the EPICS
V4 control system programming environment:</p>
<dl>
<dt><a
href="http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvDataJava/raw-file/tip/documentation/pvDataJava.html">pvData</a></dt>
<dd>pvData (Process Variable Data) defines and implements an efficent way
to store, access, and communicate memory resident structured data</dd>
<dt><a
href="http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvAccessJava/raw-file/tip/documentation/pvAccessJava.html">pvAccess</a></dt>
<dd>pvAccess is a software library for high speed controls network communications,
optimized for pvData</dd>
<dt><a
href="http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvIOCJava/raw-file/tip/documentation/pvIOCJava.html">pvIOC</a></dt>
<dd>pvIOC is a software framework for building network accessable "smart" real time
databases, suitable for interfacing devices in a distributed control system,
that can exchange pvData over pvAccess.
</dd>
<dt><a
href="http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvServiceJava/raw-file/tip/documentation/pvAccessJava.html">pvService</a></dt>
<dd>A middle layer for implementing data services.</dd>
</dl>
<p>Each of these products has a Java and a C++ implementation.</p>
<h2 class="nocount">Status of this Document</h2>
<p>This is the 09-Apr-2013 version of the definition of pvDatabaseCPP.
<p>This is the 17-Apr-2013 version of the definition of pvDatabaseCPP.
</p>
<p>This is the beginning of the implementation of pvDataBaseCPP.
It describes the features that will be provided.
The class definitions for PVRecord are implemented.
The class definition for PVDatabase are defined but not implemented.</p>
<p>The following Channel methods are implemented and working: getField,i
channelProcess, channelGet, channelPut, and channelPutGet.
But lots of work remains:</p>
<dl>
<dt>Other Channel Methods</dt>
<dd>Monitor is next.</dd>
<dt>Memory leaks at exit</dt>
<dd>May need help from Matej.</dd>
<dt>Scalar Arrays</dt>
<dd>Have not been tested. Share has not been implemented.</dd>
<dt>Structure Arrays</dt>
<dd>Has not been implemented</dd>
<dt>Testing</dt>
<dd>Needs lots more testing</dd>
</dl>
<div id="toc">
<h2 class="nocount" style="page-break-before: always">Table of Contents</h2>
@@ -121,7 +108,7 @@ 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, which is accessed via a method named <b>process</b>.</p>
A record is smart because code can be attached to a record, which is accessed via a method named process.</p>
<p>This document describes components that provides the following features:
<dl>
@@ -137,27 +124,27 @@ A record is smart because code can be attached to a record, which is accessed vi
Records can be added and removed from a database.</dd>
</dl>
<dt>pvAccess</dt>
<dd>This is a complete implementation of a local ChannelProvider and Channel as defined by pvAccess.
<dd>This is a complete implementation of ChannelProvider and Channel
as defined by pvAccess.
It is used by the server side of pvAccess to attach to pvRecords.
This component also includes the monitor and pvCopy components from pvIOCJava</dd>
</dl>
<p><b>database</b>
provides base classes that make it easy to create record instances.
<p>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:</p>
<dl>
<dt>init</dt>
<dd>This is a method for initializing the support.
It returns <b>true</b> if successful and <b>false</b> otherwise.
It returns true if successful and false otherwise.
</dd>
<dt>process</dt>
<dd>This is what makes a record <b>smart</b>.
<dd>This is what makes a record smart.
</dd>
</dl>
<h3>Relationship with pvIOCJava.</h3>
<p>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
PVDatabaseCPP 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.
@@ -190,10 +177,10 @@ mrk&gt; bin/linux-x86_64/exampleCounter
<dd>The source code for the counter.</dd>
<dt>exampleCounterMain.cpp</dt>
<dd>A main program that runs the example so that it can be accessed
by a <b>pvAccess</b> client.</dd>
by a pvAccess client.</dd>
</dl>
<h4>ExampleCounter.h</h4>
<p>The example resides in <b>src/database</b>.
<p>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</p>
@@ -208,26 +195,32 @@ public:
POINTER_DEFINITIONS(ExampleCounter);
static ExampleCounterPtr create(
epics::pvData::String const &amp; recordName);
virtual ~ExampleCounter() {}
virtual ~ExampleCounter();
virtual void destroy();
virtual bool init();
virtual void process();
private:
ExampleCounter(epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
epics::pvData::PVLongPtr pvValue;
epics::pvData::PVTimeStamp pvTimeStamp;
epics::pvData::TimeStamp timeStamp;
};
</pre>
<p>where</p>
<dl>
<dt>create<dt>
<dd>This is example specific but each support should provide
<dd>This is example specific but each support could provide
a similar static method.
</dd>
<dt>~ExampleCounter<dt>
<dd>The destructor must be declared virtual.</dd>
<dt><destroy</dt>
<dd>Called when the record is being destroyed.
This must call the base class destroy method.
<dt>init<dt>
<dd>A method to initialize the support. It returns <b>true</b>
if initialization is successful and <b>false</b> if not.
<dd>A method to initialize the support. It returns true
if initialization is successful and false if not.
NOTE that this is a virtual method of PVRecord itself.</dd>
<dt>process<dt>
<dd>
@@ -236,17 +229,18 @@ private:
<dt>ExampleCounter<dt>
<dd>For the example this is private.</dd>
<dt>pvValue</dt>
<dd>This is the field of the top level structure that <b>process</b>
<dd>This is the field of the top level structure that process
accesses.
</dd>
<dl>
<p>The implementation of <b>create</b> is:</p>
<p>The implementation of create method is:</p>
<pre>
ExampleCounterPtr ExampleCounter::create(
epics::pvData::String const &amp; recordName)
{
epics::pvData::PVStructurePtr pvStructure =
epics::pvData::getStandardPVField()-&gt;scalar(epics::pvData::pvDouble,"");
epics::pvData::getStandardPVField()-&gt;scalar(
epics::pvData::pvDouble,"timeStamp,alarm"");
ExampleCounterPtr pvRecord(
new ExampleCounter(recordName,pvStructure));
if(!pvRecord-&gt;init()) pvRecord.reset();
@@ -256,20 +250,38 @@ ExampleCounterPtr ExampleCounter::create(
This:
<ul>
<li>Creates the top level structure.</li>
<li>Creates a <b>ExampleCounterPtr</b> via the constructor.</li>
<li>Creates a ExampleCounterPtr via the constructor.</li>
<li>Calls init and if it fails resets the shared pointer.</li>
<li>Returns the shared pointer to the newly created record.</li>
</ul>
<p>The private constructor is just:</p>
<p>The private constructor method is:</p>
<pre>
ExampleCounter::ExampleCounter(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure)
: PVRecord(recordName,pvStructure)
{ }
{
pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
}
</pre>
The example is very simple. It just calls the base class constructor.
<p>The implementation of <b>init</b> is:</p>
<p>The destructor and destroy methods are:</p>
<pre>
ExampleCounter::~ExampleCounter()
{
destroy();
}
void ExampleCounter::destroy()
{
PVRecord::destroy();
}
</pre>
The destructor just calls destroy.
The destroy method, which is virtual, just calls the destroy method of the base class.
A more complicated example can clean up any resources it used but must call the base
class destroy method.
<p>The implementation of init is:</p>
<pre>
bool ExampleCounter::init()
{
@@ -283,23 +295,26 @@ bool ExampleCounter::init()
</pre>
This:
<ul>
<li>Calls <b>initRecord</b> which is implemented by the base class.
<li>Calls initRecord which is implemented by the base class.
It MUST be called.</li>
<li>Calls <b>getLongField</b> to get the interface to the value field,
<li>Calls getLongField to get the interface to the value field,
which must be a scalar with type long.</li>
<li>If a long value field was not found it returns false.</li>
<li>Returns true</li>
</ul>
<p>The implementation of <b>process</b> is:</p>
<p>The implementation of process is:</p>
<pre>
void ExampleCounter::process()
{
pvValue-&gt;put(pvValue-&gt;get() + 1.0);
timeStamp.getCurrent();
pvTimeStamp.set(timeStamp);
}
</pre>
It just adds 1.0 to the current value.
It adds 1.0 to the current value.
It then sets the timeStamp to the current time.
<h4>exampleCounterMain.cpp</h4>
<p>This is in <b>test/server</b>.
<p>This is in test/server.
The main program is:</p>
<pre>
int main(int argc,char *argv[])
@@ -326,10 +341,10 @@ This:
<ul>
<li>Gets a pointer to the master database.</li>
<li>Creates the local Channel Provider. This starts the pvAccess server.</li>
<li>Creates a <b>ExampleCounter</b> record with the name <b>exampleCounter</b>
<li>Creates a ExampleCounter record with the name exampleCounter
</li>
<li>Prints <b>exampleCounter</b> on standard out.</li>
<li>Runs forever until the user types <b>exit</b> on standard in.</li>
<li>Prints exampleCounter on standard out.</li>
<li>Runs forever until the user types exit on standard in.</li>
</ul>
<h3>Phased Development</h3>
<p>This documentation describes the first phase of a phased implementation of pvDatabaseCPP:</pp>
@@ -412,8 +427,8 @@ The following are the minimium features required</p>
</dd>
<dt>pvRecord.cpp</dt>
<dd>
The implementation of the base class for <b>PVREcord</b>.
It can also implement record instances with a <b>process</b>
The implementation of the base class for PVREcord.
It can also implement record instances with a process
method does nothing.
This can be used to create a "dumb" record where all changes are
done by clients.
@@ -435,7 +450,7 @@ The following are the minimium features required</p>
header file and creates a record instance.
</dd>
</dl>
<p>The classes in <b>pvDatabase.h</b> describe a database of memory resident
<p>The classes in pvDatabase.h describe a database of memory resident
smart records.
It describes the following classes:</p>
<dl>
@@ -443,7 +458,7 @@ It describes the following classes:</p>
<dd>This provides the methods required by localChannelProvider to implement Channel.</dd>
<dt>PVRecordField</dt>
<dt>PVRecordStructure</dt>
<dd>These <b>wrap</b> PVField and PVStructure so that pvCopy and monitor
<dd>These wrap PVField and PVStructure so that pvCopy and monitor
can be implemented.</dd>
<dt>PVRecordClient</dt>
<dd>This is called by anything that acceses PVRecord.</dd>
@@ -459,6 +474,7 @@ namespace epics { namespace pvDatabase {
class PVRecord;
typedef std::tr1::shared_ptr&lt;PVRecord&gt; PVRecordPtr;
typedef std::map&lt;epics::pvData::String,PVRecordPtr&gt; PVRecordMap;
class PVRecordField;
typedef std::tr1::shared_ptr&lt;PVRecordField&gt; PVRecordFieldPtr;
@@ -485,7 +501,7 @@ typedef std::tr1::shared_ptr&lt;PVDatabase&gt; PVDatabasePtr;
</pre>
<h3>class PVRecord</h3>
<p><b>NOTES:</b>
<p>NOTES:
<ul>
<li>This section uses the name record instead of "an instance of PVRecord".</li>
<li>Most clients will access a record via the local channel provider,
@@ -494,7 +510,7 @@ typedef std::tr1::shared_ptr&lt;PVDatabase&gt; PVDatabasePtr;
the local channel provider and record implementers.</li>
<li>Most readers will not care about most of the PVRecord methods.
Most of the methods are used by the pvAccess code.
Service implementers will mostly be interested in methods <b>init</b> and <b>process</b>.
Service implementers will mostly be interested in methods init and process.
These are described first.
</ul>
<hr>PVRecord Methods</h4>
@@ -512,9 +528,6 @@ public:
static PVRecordPtr create(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
PVRecord(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
virtual ~PVRecord();
virtual void destroy();
epics::pvData::String getRecordName();
@@ -546,6 +559,9 @@ public:
void toString(epics::pvData::StringBuilder buf);
void toString(epics::pvData::StringBuilder buf,int indentLevel);
protected:
PVRecord(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
void initPVRecord();
epics::pvData::PVStructurePtr getPVStructure();
PVRecordPtr getPtrSelf()
@@ -561,7 +577,7 @@ private:
<dt>init</dt>
<dd>Virtual method.
<p>Derived classes must implement this method.
This method <b>Must</b> call initPVRecord.</p>
This method Must call initPVRecord.</p>
</dd>
<dt>process</dt>
<dd>Virtual method.
@@ -571,8 +587,6 @@ private:
<dt>create</dt>
<dd>Static method to create dumb records, i.e. records with a process method
that does nothing.</dd>
<dt>PVRecord</dt>
<dd>The constructor. It requires a recordName and a top level PVStructure.</dd>
<dt>~PVRecord</dt>
<dd>The destructor which must be virtual. A derived class must also have
a virtual destructor.</dd>
@@ -598,14 +612,14 @@ private:
Any code accessing the data in the record or calling other PVRecord methods
must have the record locked.</dd>
<dt>tryLock</dt>
<dd>If <b>true</b> then just like <b>lock</b>.
If <b>false</b>client can not access record.
<dd>If true then just like lock.
If falseclient can not access record.
A client can try to simultaneously hold the lock for more than two records
by calling this method. But must be willing to accept failure.
</dd>
<dt>lockOtherRecord</dt>
<dd>A client that holds the lock for one record can lock one other record.
A client <b>must</b> not call this if the client already has the lock for
A client must not call this if the client already has the lock for
more then one record.
</dd>
<dt>addPVRecordClient</dt>
@@ -625,15 +639,17 @@ private:
<dd>End a group of puts.
This results in all registered PVListeners being called.</dd>
<dt>getRequesterName</dt>
<dd>virtual method of <b>Requester</b>
<dd>virtual method of Requester
</dd>
<dt>message</dt>
<dd>Can be called by implementation code.
The message will be sent to every requester.</dd>
<dt>toString</dt>
<dd>Just calls the top level PVStructure toString method.</dd>
<dt>initRecord</dt>
<dd>This method <b>must</b> be called by derived class.</dd>
<dt>PVRecord</dt>
<dd>The constructor. It requires a recordName and a top level PVStructure.</dd>
<dt>initPVRecord</dt>
<dd>This method must be called by derived class.</dd>
<dt>getPVStructure</dt>
<dd>Called by derived class.</dd>
</dl>
@@ -649,6 +665,7 @@ public:
PVRecordStructurePtr const &amp;parent,
PVRecordPtr const &amp; pvRecord);
virtual ~PVRecordField();
virtual void destroy();
PVRecordStructurePtr getParent();
epics::pvData::PVFieldPtr getPVField();
epics::pvData::String getFullFieldName();
@@ -679,6 +696,8 @@ that holds the data. It has the following methods:
<dd>The constructor.</dd>
<dt>~PVRecordField</dt>
<dd>The destructor.</dd>
<dt>destroy</dt>
<dd>Called by PVRecordStructure when it's destroy method is called.</dd>
<dt>getParent</dt>
<dd>Get the parent PVRecordStructure for this field.</dd>
<dt>getPVField</dt>
@@ -712,6 +731,7 @@ public:
epics::pvData::PVStructurePtr const &amp; pvStructure,
PVRecordFieldPtrArrayPtr const &amp; pvRecordField);
virtual ~PVRecordStructure();
virtual void destroy();
PVRecordFieldPtrArrayPtr getPVRecordFields();
epics::pvData::PVStructurePtr getPVStructure();
virtual void removeListener(PVListenerPtr const &amp; pvListener);
@@ -795,6 +815,7 @@ public:
POINTER_DEFINITIONS(PVDatabase);
static PVDatabasePtr getMaster();
virtual ~PVDatabase();
virtual void destroy();
PVRecordPtr findRecord(epics::pvData::String const&amp; recordName);
bool addRecord(PVRecordPtr const &amp; record);
bool removeRecord(PVRecordPtr const &amp; record);
@@ -812,6 +833,9 @@ private:
<dd>Get the master database. This is the database that localChannelProvider access.</dd>
<dt>~PVDatabase</dt>
<dd>The destructor.</dd>
<dt>destroy</dt>
<dd>This is called by remote channelAccess when process exits.
This destroys and removes all records in the PVDatabase.</dd>
<dt>findRecord</dt>
<dd>Find a record. An empty pointer is returned if the record is not in the database.</dd>
<dt>addRecord</dt>
@@ -829,7 +853,7 @@ private:
<p>Not yet described.
It is only of interest to someone who wants to understand how it works.
</p>
<p>A brief description is that it must implement the following components of pvIOCJava:</p>
<p>A brief description is that it implements the following components of pvIOCJava:</p>
<dl>
<dt>pvCopy</dt>
<dt>monitor</dt>