more code implemented. See pvDatabaseCPP.html for details
This commit is contained in:
@ -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> 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 & recordName);
|
||||
virtual ~ExampleCounter() {}
|
||||
virtual ~ExampleCounter();
|
||||
virtual void destroy();
|
||||
virtual bool init();
|
||||
virtual void process();
|
||||
private:
|
||||
ExampleCounter(epics::pvData::String const & recordName,
|
||||
epics::pvData::PVStructurePtr const & 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 & recordName)
|
||||
{
|
||||
epics::pvData::PVStructurePtr pvStructure =
|
||||
epics::pvData::getStandardPVField()->scalar(epics::pvData::pvDouble,"");
|
||||
epics::pvData::getStandardPVField()->scalar(
|
||||
epics::pvData::pvDouble,"timeStamp,alarm"");
|
||||
ExampleCounterPtr pvRecord(
|
||||
new ExampleCounter(recordName,pvStructure));
|
||||
if(!pvRecord->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 & recordName,
|
||||
epics::pvData::PVStructurePtr const & 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->put(pvValue->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<PVRecord> PVRecordPtr;
|
||||
typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
|
||||
|
||||
class PVRecordField;
|
||||
typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
|
||||
@ -485,7 +501,7 @@ typedef std::tr1::shared_ptr<PVDatabase> 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<PVDatabase> 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 & recordName,
|
||||
epics::pvData::PVStructurePtr const & pvStructure);
|
||||
PVRecord(
|
||||
epics::pvData::String const & recordName,
|
||||
epics::pvData::PVStructurePtr const & 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 & recordName,
|
||||
epics::pvData::PVStructurePtr const & 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 &parent,
|
||||
PVRecordPtr const & 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 & pvStructure,
|
||||
PVRecordFieldPtrArrayPtr const & pvRecordField);
|
||||
virtual ~PVRecordStructure();
|
||||
virtual void destroy();
|
||||
PVRecordFieldPtrArrayPtr getPVRecordFields();
|
||||
epics::pvData::PVStructurePtr getPVStructure();
|
||||
virtual void removeListener(PVListenerPtr const & pvListener);
|
||||
@ -795,6 +815,7 @@ public:
|
||||
POINTER_DEFINITIONS(PVDatabase);
|
||||
static PVDatabasePtr getMaster();
|
||||
virtual ~PVDatabase();
|
||||
virtual void destroy();
|
||||
PVRecordPtr findRecord(epics::pvData::String const& recordName);
|
||||
bool addRecord(PVRecordPtr const & record);
|
||||
bool removeRecord(PVRecordPtr const & 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>
|
||||
|
865
documentation/pvDatabaseCPP_20130417.html
Normal file
865
documentation/pvDatabaseCPP_20130417.html
Normal file
@ -0,0 +1,865 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
|
||||
<title>pvDatabaseCPP</title>
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="http://epics-pvdata.sourceforge.net/base.css" />
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="http://epics-pvdata.sourceforge.net/epicsv4.css" />
|
||||
<style type="text/css">
|
||||
/*<![CDATA[*/
|
||||
.about { margin-left: 3em; margin-right: 3em; font-size: .83em}
|
||||
table { margin-left: auto; margin-right: auto }
|
||||
.diagram { text-align: center; margin: 2.5em 0 }
|
||||
span.opt { color: grey }
|
||||
span.nterm { font-style:italic }
|
||||
span.term { font-family:courier }
|
||||
span.user { font-family:courier }
|
||||
span.user:before { content:"<" }
|
||||
span.user:after { content:">" }
|
||||
.nonnorm { font-style:italic }
|
||||
p.ed { color: #AA0000 }
|
||||
span.ed { color: #AA0000 }
|
||||
p.ed.priv { display: inline; }
|
||||
span.ed.priv { display: inline; }
|
||||
/*]]>*/</style>
|
||||
<!-- Script that generates the Table of Contents -->
|
||||
<script type="text/javascript"
|
||||
src="http://epics-pvdata.sourceforge.net/script/tocgen.js">
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="head">
|
||||
<h1>pvDatabaseCPP</h1>
|
||||
<!-- Maturity: Working Draft or Request for Comments, or Recommendation, and date. -->
|
||||
|
||||
<h2 class="nocount">EPICS v4 Working Group, Working Draft, 17-Apr-2013</h2>
|
||||
<dl>
|
||||
<dt>Latest version:</dt>
|
||||
<dd><a
|
||||
href="http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvDatabaseCPP/raw-file/tip/documentation/pvDatabaseCPP.html">pvDatabaseCPP.html</a>
|
||||
</dd>
|
||||
<dt>This version:</dt>
|
||||
<dd><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_20121211.html">pvDatabaseCPP20121211.html</a>
|
||||
</dd>
|
||||
<dt>Editors:</dt>
|
||||
<dd>Marty Kraimer, BNL</dd>
|
||||
</dl>
|
||||
|
||||
<p class="copyright">This product is made available subject to acceptance of the <a
|
||||
href="http://epics-pvdata.sourceforge.net/LICENSE.html">EPICS open source license.</a></p>
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
<h2 class="nocount">Abstract</h2>
|
||||
|
||||
<p>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 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.
|
||||
</p>
|
||||
|
||||
|
||||
<h2 class="nocount">Status of this Document</h2>
|
||||
|
||||
<p>This is the 17-Apr-2013 version of the definition of pvDatabaseCPP.
|
||||
</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>
|
||||
</div>
|
||||
<div id="contents" class="contents">
|
||||
|
||||
|
||||
<h2>Introduction</h2>
|
||||
<h3>Overview</h3>
|
||||
<p>A brief description of a pvDatabase is that it is a set of network accessible, 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.
|
||||
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 process.</p>
|
||||
|
||||
<p>This document describes components that provides the following features:
|
||||
<dl>
|
||||
<dt>database<dt>
|
||||
<dd>This encapsulates the concept of a database of memory resident smart records.
|
||||
The two main components are:
|
||||
<dl>
|
||||
<dt>pvRecord</dt>
|
||||
<dd>This encapsulates the concept of a smart record. It can be processed.
|
||||
Changes to field values can be trapped. A record can be locked.</dd>
|
||||
<dt>pvDatabase<dt>
|
||||
<dd>This is a database of pvRecords.
|
||||
Records can be added and removed from a database.</dd>
|
||||
</dl>
|
||||
<dt>pvAccess</dt>
|
||||
<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>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 true if successful and false otherwise.
|
||||
</dd>
|
||||
<dt>process</dt>
|
||||
<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.
|
||||
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.
|
||||
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.</p>
|
||||
<p>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.</p>
|
||||
</p>
|
||||
<h3>Example PVRecord Extension</h3>
|
||||
<p>The example implements a simple counter.
|
||||
The example can be run on linux as follows:</p>
|
||||
<pre>
|
||||
mrk> pwd
|
||||
/home/hg/pvDatabaseCPP
|
||||
mrk> bin/linux-x86_64/exampleCounter
|
||||
|
||||
</pre>
|
||||
<p>The example consists of two components:</p>
|
||||
<dl>
|
||||
<dt>ExampleCounter.h</dt>
|
||||
<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 pvAccess client.</dd>
|
||||
</dl>
|
||||
<h4>ExampleCounter.h</h4>
|
||||
<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>
|
||||
<pre>
|
||||
class ExampleCounter;
|
||||
typedef std::tr1::shared_ptr<ExampleCounter> ExampleCounterPtr;
|
||||
|
||||
class ExampleCounter :
|
||||
public PVRecord
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ExampleCounter);
|
||||
static ExampleCounterPtr create(
|
||||
epics::pvData::String const & recordName);
|
||||
virtual ~ExampleCounter();
|
||||
virtual void destroy();
|
||||
virtual bool init();
|
||||
virtual void process();
|
||||
private:
|
||||
ExampleCounter(epics::pvData::String const & recordName,
|
||||
epics::pvData::PVStructurePtr const & 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 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 true
|
||||
if initialization is successful and false if not.
|
||||
NOTE that this is a virtual method of PVRecord itself.</dd>
|
||||
<dt>process<dt>
|
||||
<dd>
|
||||
This again is a virtual method of PVRecord.
|
||||
</dd>
|
||||
<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 process
|
||||
accesses.
|
||||
</dd>
|
||||
<dl>
|
||||
<p>The implementation of create method is:</p>
|
||||
<pre>
|
||||
ExampleCounterPtr ExampleCounter::create(
|
||||
epics::pvData::String const & recordName)
|
||||
{
|
||||
epics::pvData::PVStructurePtr pvStructure =
|
||||
epics::pvData::getStandardPVField()->scalar(
|
||||
epics::pvData::pvDouble,"timeStamp,alarm"");
|
||||
ExampleCounterPtr pvRecord(
|
||||
new ExampleCounter(recordName,pvStructure));
|
||||
if(!pvRecord->init()) pvRecord.reset();
|
||||
return pvRecord;
|
||||
}
|
||||
</pre>
|
||||
This:
|
||||
<ul>
|
||||
<li>Creates the top level structure.</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 method is:</p>
|
||||
<pre>
|
||||
ExampleCounter::ExampleCounter(
|
||||
epics::pvData::String const & recordName,
|
||||
epics::pvData::PVStructurePtr const & pvStructure)
|
||||
: PVRecord(recordName,pvStructure)
|
||||
{
|
||||
pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
|
||||
}
|
||||
</pre>
|
||||
The example is very simple. It just calls the base class constructor.
|
||||
<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()
|
||||
{
|
||||
|
||||
initPVRecord();
|
||||
epics::pvData::PVFieldPtr pvField;
|
||||
pvValue = getPVStructure()->getLongField("value");
|
||||
if(pvValue.get()==NULL) return false;
|
||||
return true;
|
||||
}
|
||||
</pre>
|
||||
This:
|
||||
<ul>
|
||||
<li>Calls initRecord which is implemented by the base class.
|
||||
It MUST be called.</li>
|
||||
<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 process is:</p>
|
||||
<pre>
|
||||
void ExampleCounter::process()
|
||||
{
|
||||
pvValue->put(pvValue->get() + 1.0);
|
||||
timeStamp.getCurrent();
|
||||
pvTimeStamp.set(timeStamp);
|
||||
}
|
||||
</pre>
|
||||
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 test/server.
|
||||
The main program is:</p>
|
||||
<pre>
|
||||
int main(int argc,char *argv[])
|
||||
{
|
||||
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";
|
||||
getline(cin,str);
|
||||
if(str.compare("exit")==0) break;
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
</pre>
|
||||
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 ExampleCounter record with the name exampleCounter
|
||||
</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>
|
||||
<dl>
|
||||
<dt>pvRecord</d>
|
||||
<dd>Wrapper on PVStructure that implements methods required by Local Channel Provider.</dd>
|
||||
<dt>pvDatabase</d>
|
||||
<dd>Database of PVRecords. Has methods find, add, and remove.</dd>
|
||||
<dt>Local Channel Provider</dt>
|
||||
<dd>Complete implementation of ChannelProvider and Channel.
|
||||
This means that pvCopy and monitor are also implemented.</dd>
|
||||
</dl>
|
||||
<p>Future phases of pvDatabaseCPP might include:</p>
|
||||
<dl>
|
||||
<dt>Install</dt>
|
||||
<dd>This provides complete support for on-line add and delete
|
||||
of sets of records.
|
||||
With the first phase each "service" is responsible for it's own implementation.
|
||||
All that is provided is addRecord and removeRecord.
|
||||
</dd>
|
||||
<dt>Field support</dt>
|
||||
<dd>Add the ability to optionally add support to fields.
|
||||
In addition some of the basic support defined in pvIOCJava could also be implemented.</dd>
|
||||
<dt>XML parser</dt>
|
||||
<dd>This provides the ability to create record instances without writing any code.</dd>
|
||||
</dl>
|
||||
<p>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.</p>
|
||||
<h3>Features Required for localChannelProvider</h3>
|
||||
<dl>
|
||||
<dt>pvCopy</dt>
|
||||
<dd>Creates a PVStructure that contains a copy of an arbitary
|
||||
subset of the fields of another top level PVStructure.
|
||||
It can copy data between the two and maintains a bitSet that show
|
||||
which fields are changed.<dd>
|
||||
<dt>monitor</dt>
|
||||
<dd>This provides the ability to monitor changes to fields of a record.</dd>
|
||||
<dt>PVRecord and PVDatabase</dt>
|
||||
<dd>Defined below.</dd>
|
||||
<dt>The localChannelProvider itself</dt>
|
||||
<dd>This is the pvAccess package in pvIOCJava.
|
||||
The localChannelProvider will access data from PVRecords.
|
||||
It will implement all channel methods.</dd>
|
||||
</dl>
|
||||
<h3>Minumum Features Required for pvRecord</h3>
|
||||
<p>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</p>
|
||||
<dl>
|
||||
<dt>PVDatabase</dt>
|
||||
<dd>This holds a set of PVRecords. It has methods to find, add, and remove records.</dd>
|
||||
<dt>PVRecord</dt>
|
||||
<dd>This, and a set of related interfaces, provides the following:
|
||||
<dl>
|
||||
<dt>Access to top level PVStructure</dt>
|
||||
<dd>PVRecord is a wrapper on a top level pvStructure.</dd>
|
||||
<dt>Record locking</dt>
|
||||
<dd>A record can be locked and unlocked.
|
||||
A record must be locked whenever data in the pvStructure is accessed.</dd>
|
||||
<dt>Trapping data changes</dt>
|
||||
<dd>A client can request to be notified when data in the pvStructure is modified.
|
||||
It can do this on a field by field basis.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
<p>The following sections describes the classes required for the first phase.</p>
|
||||
|
||||
<h2>database</h2>
|
||||
<p>This directory has the following files:</p>
|
||||
<dl>
|
||||
<dt>pvDatabase.h</dt>
|
||||
<dd>
|
||||
This is what is described in this section.
|
||||
</dd>
|
||||
<dt>pvDatabase.cpp</dt>
|
||||
<dd>
|
||||
The implementation of PVDatabase.
|
||||
</dd>
|
||||
<dt>pvRecord.cpp</dt>
|
||||
<dd>
|
||||
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.
|
||||
The complete implementation is provided in the header file.
|
||||
Thus code will be generated only if other code includes the
|
||||
header file and creates a record instance.
|
||||
</dd>
|
||||
<dt>exampleCounter.h</dt>
|
||||
<dd>
|
||||
This was described in the introduction.
|
||||
</dd>
|
||||
<dt>powerSupplyRecordTest.h</dt>
|
||||
<dd>
|
||||
This provides code that simulates a power supply.
|
||||
It computes the current from the voltage and power.
|
||||
It is used for testing.
|
||||
The complete implementation is provided in the header file.
|
||||
Thus code will be generated only if other code includes the
|
||||
header file and creates a record instance.
|
||||
</dd>
|
||||
</dl>
|
||||
<p>The classes in pvDatabase.h describe a database of memory resident
|
||||
smart records.
|
||||
It describes the following classes:</p>
|
||||
<dl>
|
||||
<dt>PVRecord</dt>
|
||||
<dd>This provides the methods required by localChannelProvider to implement Channel.</dd>
|
||||
<dt>PVRecordField</dt>
|
||||
<dt>PVRecordStructure</dt>
|
||||
<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>
|
||||
<dt>PVListener</dt>
|
||||
<dd>This is implemented by anything that wants to trap calls to PVRecord::message.</dd>
|
||||
<dt>PVDatabase</dt>
|
||||
<dd>This is a database of PVRecords.</dd>
|
||||
</dl>
|
||||
<p>Each class is described in a separate subsection.</p>
|
||||
<h3>C++ namespace and typedefs</h3>
|
||||
<pre>
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
class PVRecord;
|
||||
typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
|
||||
typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
|
||||
|
||||
class PVRecordField;
|
||||
typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
|
||||
typedef std::vector<PVRecordFieldPtr> PVRecordFieldPtrArray;
|
||||
typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;
|
||||
|
||||
class PVRecordStructure;
|
||||
typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;
|
||||
|
||||
class PVRecordClient;
|
||||
typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
|
||||
|
||||
class PVListener;
|
||||
typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
|
||||
|
||||
class RecordProcessRequester;
|
||||
typedef std::tr1::shared_ptr<RecordProcessRequester> RecordProcessRequesterPtr;
|
||||
|
||||
class RecordPutRequester;
|
||||
typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;
|
||||
|
||||
class PVDatabase;
|
||||
typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
|
||||
</pre>
|
||||
|
||||
<h3>class PVRecord</h3>
|
||||
<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,
|
||||
i. e. via pvAccess.
|
||||
Thus this section is mainly of interest to
|
||||
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 init and process.
|
||||
These are described first.
|
||||
</ul>
|
||||
<hr>PVRecord Methods</h4>
|
||||
<pre>
|
||||
class PVRecord
|
||||
public epics::pvData::Requester,
|
||||
public std::tr1::enable_shared_from_this<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);
|
||||
virtual ~PVRecord();
|
||||
virtual void destroy();
|
||||
epics::pvData::String getRecordName();
|
||||
PVRecordStructurePtr getPVRecordStructure();
|
||||
PVRecordFieldPtr findPVRecordField(
|
||||
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);
|
||||
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();
|
||||
epics::pvData::String getRequesterName() {return getRecordName();}
|
||||
virtual void message(
|
||||
epics::pvData::String const & message,
|
||||
epics::pvData::MessageType messageType);
|
||||
void message(
|
||||
PVRecordFieldPtr const & pvRecordField,
|
||||
epics::pvData::String const & message,
|
||||
epics::pvData::MessageType messageType);
|
||||
void toString(epics::pvData::StringBuilder buf);
|
||||
void toString(epics::pvData::StringBuilder buf,int indentLevel);
|
||||
protected:
|
||||
PVRecord(
|
||||
epics::pvData::String const & recordName,
|
||||
epics::pvData::PVStructurePtr const & pvStructure);
|
||||
void initPVRecord();
|
||||
epics::pvData::PVStructurePtr getPVStructure();
|
||||
PVRecordPtr getPtrSelf()
|
||||
{
|
||||
return shared_from_this();
|
||||
}
|
||||
private:
|
||||
...
|
||||
}
|
||||
</pre>
|
||||
<p>The methods are:</h3>
|
||||
<dl>
|
||||
<dt>init</dt>
|
||||
<dd>Virtual method.
|
||||
<p>Derived classes must implement this method.
|
||||
This method Must call initPVRecord.</p>
|
||||
</dd>
|
||||
<dt>process</dt>
|
||||
<dd>Virtual method.
|
||||
<p>Derived classes must implement this method.
|
||||
The base implementation does nothing.</p>
|
||||
</dd>
|
||||
<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 destructor which must be virtual. A derived class must also have
|
||||
a virtual destructor.</dd>
|
||||
<dt>destroy</dt>
|
||||
<dd>This is a virtual method.
|
||||
</dd>
|
||||
<dt>getRecordName</dt>
|
||||
<dd>Return the recordName.</dd>
|
||||
<dt>getPVRecordStructure</dt>
|
||||
<dd>Get the top level PVStructure.</dd>
|
||||
<dt>findPVRecordField</dt>
|
||||
<dd>Given a PVFieldPtr return the PVRecordFieldPtr for the field.</dd>
|
||||
<dt>addRequester</dt>
|
||||
<dd>Add a requester to receive messages.</dd>
|
||||
<dt>removeRequester</dt>
|
||||
<dd>Remove a message requester.</dd>
|
||||
<dt>lock_guard</dt>
|
||||
<dd>This is an inline method that locks the record. The record will automatically
|
||||
be unlocked when control leaves the block that has the call.
|
||||
<dt>lock</dt>
|
||||
<dt>unlock</dt>
|
||||
<dd>Lock and Unlock the record.
|
||||
Any code accessing the data in the record or calling other PVRecord methods
|
||||
must have the record locked.</dd>
|
||||
<dt>tryLock</dt>
|
||||
<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 must not call this if the client already has the lock for
|
||||
more then one record.
|
||||
</dd>
|
||||
<dt>addPVRecordClient</dt>
|
||||
<dd>Every client that accesses the record must call this so that the client can be notified when the record is deleted.</dd>
|
||||
<dt>removePVRecordClient</dt>
|
||||
<dd>Client is no longer accessing the record.</dd>
|
||||
<dt>detachClients</dt>
|
||||
<dd>Ask all clients to detach from the record</dd>
|
||||
<dt>addListener</dt>
|
||||
<dd>Add a PVListener. This must be called before calling pvRecordField.addListener.</dd>
|
||||
<dt>removeListener</dt>
|
||||
<dd>Removes a listener. The listener will also be removed from all fields to which it is attached.</dd>
|
||||
<dt>beginGroupPut</dt>
|
||||
<dd>Begin a group of puts.
|
||||
This results in all registered PVListeners being called</dd>
|
||||
<dt>endGroupPut</dt>
|
||||
<dd>End a group of puts.
|
||||
This results in all registered PVListeners being called.</dd>
|
||||
<dt>getRequesterName</dt>
|
||||
<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>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>
|
||||
<h3>class PVRecordField</h3>
|
||||
<pre>
|
||||
class PVRecordField {
|
||||
public virtual epics::pvData::PostHandler,
|
||||
public std::tr1::enable_shared_from_this<PVRecordField>
|
||||
public:
|
||||
POINTER_DEFINITIONS(PVRecordField);
|
||||
PVRecordField(
|
||||
epics::pvData::PVFieldPtr const & pvField,
|
||||
PVRecordStructurePtr const &parent,
|
||||
PVRecordPtr const & pvRecord);
|
||||
virtual ~PVRecordField();
|
||||
virtual void destroy();
|
||||
PVRecordStructurePtr getParent();
|
||||
epics::pvData::PVFieldPtr getPVField();
|
||||
epics::pvData::String getFullFieldName();
|
||||
epics::pvData::String getFullName();
|
||||
PVRecordPtr getPVRecord();
|
||||
bool addListener(PVListenerPtr const & pvListener);
|
||||
virtual void removeListener(PVListenerPtr const & pvListener);
|
||||
virtual void postPut();
|
||||
virtual void message(
|
||||
epics::pvData::String const & message,
|
||||
epics::pvData::MessageType messageType);
|
||||
protected:
|
||||
PVRecordFieldPtr getPtrSelf()
|
||||
{
|
||||
return shared_from_this();
|
||||
}
|
||||
virtual void init();
|
||||
private:
|
||||
...
|
||||
};
|
||||
</pre>
|
||||
<p>When PVRecord is created it creates a PVRecordField for every field in the PVStructure
|
||||
that holds the data. It has the following methods:
|
||||
</p>
|
||||
|
||||
<dl>
|
||||
<dt>PVRecordField</dt>
|
||||
<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>
|
||||
<dd>Get the PVField associated with this PVRecordField.</dd>
|
||||
<dt>getFullFieldName</dt>
|
||||
<dd>This gets the full name of the field, i.e. field,field,..</dd>
|
||||
<dt>getFullName</dt>
|
||||
<dd>This gets recordName plus the full name of the field, i.e. recordName.field,field,..</dd>
|
||||
<dt>getPVRecord</dt>
|
||||
<dd>Returns the PVRecord to which this field belongs.</dd>
|
||||
<dt>addListener</dt>
|
||||
<dd>Add A PVListener to this field.
|
||||
Whenever this field or any subfield if this field is modified the listener will be notified.
|
||||
PVListener is described below.
|
||||
Before a listener can call addListener it must first call PVRecord.registerListener.</dd>
|
||||
<dt>removeListener</dt>
|
||||
<dd>Remove a PVListener.</dd>
|
||||
<dt>postPut</dt>
|
||||
<dd>This is called by the code that implements the data interface.
|
||||
It is called whenever the put method is called.</dd>
|
||||
<dt>message</dt>
|
||||
<dd>Called by implementation code. It calls PVRecord::message after prepending the full
|
||||
fieldname.</dd>
|
||||
</dl>
|
||||
<h3>class PVRecordStructure</h3>
|
||||
<pre>
|
||||
class PVRecordStructure : public PVRecordField {
|
||||
public:
|
||||
POINTER_DEFINITIONS(PVRecordStructure);
|
||||
PVRecordStructure(
|
||||
epics::pvData::PVStructurePtr const & pvStructure,
|
||||
PVRecordFieldPtrArrayPtr const & pvRecordField);
|
||||
virtual ~PVRecordStructure();
|
||||
virtual void destroy();
|
||||
PVRecordFieldPtrArrayPtr getPVRecordFields();
|
||||
epics::pvData::PVStructurePtr getPVStructure();
|
||||
virtual void removeListener(PVListenerPtr const & pvListener);
|
||||
virtual void postPut();
|
||||
protected:
|
||||
virtual void init();
|
||||
private:
|
||||
...
|
||||
};
|
||||
</pre>
|
||||
<p>When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure
|
||||
that holds the data. It has the following methods:
|
||||
</p>
|
||||
<dl>
|
||||
<dt>PVRecordStructure</dt>
|
||||
<dd>The constructor.</dd>
|
||||
<dt>~PVRecordStructure</dt>
|
||||
<dd>The destructor.</dd>
|
||||
<dt>getPVRecordFields</dt>
|
||||
<dd>Get the PVRecordField array for the subfields</dd>
|
||||
<dt>getPVStructure</dt>
|
||||
<dd>Get the PVStructure for this field.</dd>
|
||||
<dt>removeListener</dt>
|
||||
<dd>Remove a PVListener.</dd>
|
||||
<dt>postPut</dt>
|
||||
<dd>This is called by the code that implements the data interface.
|
||||
It is called whenever the put method is called.</dd>
|
||||
<h3>class PVRecordClient</h3>
|
||||
<pre>
|
||||
class PVRecordClient {
|
||||
POINTER_DEFINITIONS(PVRecordClient);
|
||||
virtual ~PVRecordClient();
|
||||
virtual void detach(PVRecordPtr const & pvRecord);
|
||||
};
|
||||
</pre>
|
||||
<p>where</p>
|
||||
<dl>
|
||||
<dt>~PVRecordClient</dt>
|
||||
<dd>The destructor.</dd>
|
||||
<dt>detach</dt>
|
||||
<dd>The record is being removed from the master database,</dd>
|
||||
</dl>
|
||||
</dl>
|
||||
<h3>class PVListener</h3>
|
||||
<pre>
|
||||
class PVListener {
|
||||
virtual public PVRecordClient
|
||||
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;
|
||||
};
|
||||
</pre>
|
||||
<p>where</p>
|
||||
<dl>
|
||||
<dt>~PVListener</dt>
|
||||
<dd>The destructor.</dd>
|
||||
<dt>dataPut(PVRecordFieldPtr const & pvRecordField)</dt>
|
||||
<dd>pvField has been modified.
|
||||
This is called if the listener has called PVRecordField::addListener for pvRecordField.</dd>
|
||||
<dt>dataPut(
|
||||
PVRecordStructurePtr const &
|
||||
requested,PVRecordFieldPtr const & pvRecordField)</dt>
|
||||
<dd>pvField has been modified.
|
||||
Requested is the field to which the requester issued a pvField-&addListener.
|
||||
This is called if the listener has called PVRecordField-&addListener for requested.</dd>
|
||||
<dt>beginGroupPut</dt>
|
||||
<dd>A related set of changes is being started.</dd>
|
||||
<dt>endGroupPut</dt>
|
||||
<dd>A related set of changes is done.</dd>
|
||||
</dl>
|
||||
<h3>class PVDatabase</h3>
|
||||
<pre>
|
||||
class PVDatabase : virtual public epics::pvData::Requester {
|
||||
public:
|
||||
POINTER_DEFINITIONS(PVDatabase);
|
||||
static PVDatabasePtr getMaster();
|
||||
virtual ~PVDatabase();
|
||||
virtual void destroy();
|
||||
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();
|
||||
};
|
||||
</pre>
|
||||
<p>where</p>
|
||||
<dl>
|
||||
<dt>getMaster</dt>
|
||||
<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>
|
||||
<dd>Add a record to the database.
|
||||
If the record already exists it is not modified and false is returned.</dd>
|
||||
<dt>removeRecord</dt>
|
||||
<dd>Remove a record from the database.
|
||||
If the record was not in the database false is returned.</dd>
|
||||
<dt>getRequesterName</dt>
|
||||
<dd>Virtual method of Requester</dd>
|
||||
<dt>message</dt>
|
||||
<dd>Virtual message of Requester.</dd>
|
||||
</dl>
|
||||
<h2>pvAccess</h2>
|
||||
<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 implements the following components of pvIOCJava:</p>
|
||||
<dl>
|
||||
<dt>pvCopy</dt>
|
||||
<dt>monitor</dt>
|
||||
<dt>local ChannelProvider and Channel</dt>
|
||||
</dl>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -11,8 +11,10 @@ SRC_DIRS += $(DATABASE)/database
|
||||
INC += pvDatabase.h
|
||||
INC += powerSupplyRecordTest.h
|
||||
INC += exampleCounter.h
|
||||
INC += recordList.h
|
||||
LIBSRCS += pvRecord.cpp
|
||||
LIBSRCS += pvDatabase.cpp
|
||||
LIBSRCS += recordList.cpp
|
||||
|
||||
SRC_DIRS += $(DATABASE)/pvAccess
|
||||
INC += channelProviderLocal.h
|
||||
|
@ -13,6 +13,8 @@
|
||||
|
||||
#include <pv/pvDatabase.h>
|
||||
#include <pv/standardPVField.h>
|
||||
#include <pv/timeStamp.h>
|
||||
#include <pv/pvTimeStamp.h>
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
@ -27,20 +29,23 @@ public:
|
||||
POINTER_DEFINITIONS(ExampleCounter);
|
||||
static ExampleCounterPtr create(
|
||||
epics::pvData::String const & recordName);
|
||||
virtual ~ExampleCounter(){}
|
||||
virtual ~ExampleCounter();
|
||||
virtual void destroy();
|
||||
virtual bool init();
|
||||
virtual void process();
|
||||
private:
|
||||
ExampleCounter(epics::pvData::String const & recordName,
|
||||
epics::pvData::PVStructurePtr const & pvStructure);
|
||||
epics::pvData::PVLongPtr pvValue;
|
||||
epics::pvData::PVTimeStamp pvTimeStamp;
|
||||
epics::pvData::TimeStamp timeStamp;
|
||||
};
|
||||
|
||||
ExampleCounterPtr ExampleCounter::create(
|
||||
epics::pvData::String const & recordName)
|
||||
{
|
||||
epics::pvData::PVStructurePtr pvStructure =
|
||||
epics::pvData::getStandardPVField()->scalar(epics::pvData::pvLong,"timeStamp");
|
||||
epics::pvData::getStandardPVField()->scalar(epics::pvData::pvLong,"timeStamp,alarm");
|
||||
ExampleCounterPtr pvRecord(
|
||||
new ExampleCounter(recordName,pvStructure));
|
||||
if(!pvRecord->init()) pvRecord.reset();
|
||||
@ -52,6 +57,17 @@ ExampleCounter::ExampleCounter(
|
||||
epics::pvData::PVStructurePtr const & pvStructure)
|
||||
: PVRecord(recordName,pvStructure)
|
||||
{
|
||||
pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
|
||||
}
|
||||
|
||||
ExampleCounter::~ExampleCounter()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
void ExampleCounter::destroy()
|
||||
{
|
||||
PVRecord::destroy();
|
||||
}
|
||||
|
||||
bool ExampleCounter::init()
|
||||
@ -67,6 +83,8 @@ bool ExampleCounter::init()
|
||||
void ExampleCounter::process()
|
||||
{
|
||||
pvValue->put(pvValue->get() + 1.0);
|
||||
timeStamp.getCurrent();
|
||||
pvTimeStamp.set(timeStamp);
|
||||
}
|
||||
|
||||
}}
|
||||
|
@ -32,6 +32,7 @@ public:
|
||||
epics::pvData::String const & recordName,
|
||||
epics::pvData::PVStructurePtr const & pvStructure);
|
||||
virtual ~PowerSupplyRecordTest();
|
||||
virtual void destroy();
|
||||
virtual bool init();
|
||||
virtual void process();
|
||||
void put(double power,double voltage);
|
||||
@ -72,6 +73,11 @@ PowerSupplyRecordTest::~PowerSupplyRecordTest()
|
||||
destroy();
|
||||
}
|
||||
|
||||
void PowerSupplyRecordTest::destroy()
|
||||
{
|
||||
PVRecord::destroy();
|
||||
}
|
||||
|
||||
bool PowerSupplyRecordTest::init()
|
||||
{
|
||||
initPVRecord();
|
||||
|
@ -18,8 +18,6 @@ using namespace std;
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
PVDatabase::~PVDatabase() {}
|
||||
|
||||
PVDatabasePtr PVDatabase::getMaster()
|
||||
{
|
||||
static PVDatabasePtr master;
|
||||
@ -31,35 +29,97 @@ PVDatabasePtr PVDatabase::getMaster()
|
||||
return master;
|
||||
}
|
||||
|
||||
PVDatabase::PVDatabase() {}
|
||||
PVDatabase::PVDatabase()
|
||||
: thelock(mutex),
|
||||
isDestroyed(false)
|
||||
{
|
||||
thelock.unlock();
|
||||
}
|
||||
|
||||
PVDatabase::~PVDatabase()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
void PVDatabase::destroy()
|
||||
{
|
||||
lock();
|
||||
if(isDestroyed) {
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
isDestroyed = true;
|
||||
PVRecordMap::iterator iter;
|
||||
while(true) {
|
||||
iter = recordMap.begin();
|
||||
if(iter==recordMap.end()) break;
|
||||
PVRecordPtr pvRecord = (*iter).second;
|
||||
recordMap.erase(iter);
|
||||
unlock();
|
||||
if(pvRecord.get()!=NULL) pvRecord->destroy();
|
||||
lock();
|
||||
}
|
||||
}
|
||||
|
||||
void PVDatabase::lock() {thelock.lock();}
|
||||
|
||||
void PVDatabase::unlock() {thelock.unlock();}
|
||||
|
||||
PVRecordPtr PVDatabase::findRecord(String const& recordName)
|
||||
{
|
||||
lock_guard();
|
||||
PVRecordPtr xxx;
|
||||
if(isDestroyed) return xxx;
|
||||
PVRecordMap::iterator iter = recordMap.find(recordName);
|
||||
if(iter!=recordMap.end()) {
|
||||
return (*iter).second;
|
||||
}
|
||||
PVRecordPtr xxx;
|
||||
return xxx;
|
||||
}
|
||||
|
||||
PVStringArrayPtr PVDatabase::getRecordNames()
|
||||
{
|
||||
lock_guard();
|
||||
PVStringArrayPtr pvStringArray = static_pointer_cast<PVStringArray>
|
||||
(getPVDataCreate()->createPVScalarArray(pvString));
|
||||
size_t len = recordMap.size();
|
||||
std::vector<String> names(len);
|
||||
PVRecordMap::iterator iter;
|
||||
size_t i = 0;
|
||||
for(iter = recordMap.begin(); iter!=recordMap.end(); ++iter) {
|
||||
names[i++] = (*iter).first;
|
||||
}
|
||||
pvStringArray->put(0,len,names,0);
|
||||
return pvStringArray;
|
||||
}
|
||||
|
||||
bool PVDatabase::addRecord(PVRecordPtr const & record)
|
||||
{
|
||||
lock_guard();
|
||||
if(isDestroyed) return false;
|
||||
String recordName = record->getRecordName();
|
||||
PVRecordMap::iterator iter = recordMap.find(recordName);
|
||||
if(iter!=recordMap.end()) return false;
|
||||
if(iter!=recordMap.end()) {
|
||||
return false;
|
||||
}
|
||||
recordMap.insert(PVRecordMap::value_type(recordName,record));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PVDatabase::removeRecord(PVRecordPtr const & record)
|
||||
{
|
||||
lock();
|
||||
if(isDestroyed) return false;
|
||||
String recordName = record->getRecordName();
|
||||
PVRecordMap::iterator iter = recordMap.find(recordName);
|
||||
if(iter!=recordMap.end()) {
|
||||
PVRecordPtr pvRecord = (*iter).second;
|
||||
recordMap.erase(iter);
|
||||
unlock();
|
||||
if(pvRecord.get()!=NULL) pvRecord->destroy();
|
||||
return true;
|
||||
}
|
||||
unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,7 @@ public:
|
||||
* The record will automatically
|
||||
* be unlocked when control leaves the block that has the call.
|
||||
*/
|
||||
inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
|
||||
inline void lock_guard() { epics::pvData::Lock thelock(mutex); }
|
||||
/**
|
||||
* Lock the record.
|
||||
* Any code must lock while accessing a record.
|
||||
@ -488,6 +488,10 @@ public:
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~PVDatabase();
|
||||
/**
|
||||
* Destroy the PVDatabase.
|
||||
*/
|
||||
virtual void destroy();
|
||||
/**
|
||||
* Find a record.
|
||||
* An empty pointer is returned if the record is not in the database.
|
||||
@ -507,6 +511,11 @@ public:
|
||||
* @return <b>true</b> if record was removed.
|
||||
*/
|
||||
bool removeRecord(PVRecordPtr const & record);
|
||||
/**
|
||||
* Get the names of all the records in the database.
|
||||
* @return The names.
|
||||
*/
|
||||
epics::pvData::PVStringArrayPtr getRecordNames();
|
||||
/**
|
||||
* Virtual method of Requester.
|
||||
* @return The name.
|
||||
@ -523,7 +532,13 @@ public:
|
||||
epics::pvData::MessageType messageType);
|
||||
private:
|
||||
PVDatabase();
|
||||
void lock();
|
||||
void unlock();
|
||||
inline void lock_guard() { epics::pvData::Lock thelock(mutex); }
|
||||
PVRecordMap recordMap;
|
||||
epics::pvData::Mutex mutex;
|
||||
epics::pvData::Lock thelock;
|
||||
bool isDestroyed;
|
||||
|
||||
};
|
||||
|
||||
|
@ -68,29 +68,29 @@ void PVRecord::destroy()
|
||||
isDestroyed = true;
|
||||
|
||||
std::list<RequesterPtr>::iterator requesterIter;
|
||||
for (
|
||||
while(true) {
|
||||
requesterIter = requesterList.begin();
|
||||
requesterIter!=requesterList.end();
|
||||
requesterIter++ ) {
|
||||
if(requesterIter==requesterList.end()) break;
|
||||
requesterList.erase(requesterIter);
|
||||
unlock();
|
||||
(*requesterIter)->message("record destroyed",fatalErrorMessage);
|
||||
lock();
|
||||
}
|
||||
requesterList.clear();
|
||||
|
||||
std::list<PVRecordClientPtr>::iterator clientIter;
|
||||
for (clientIter = pvRecordClientList.begin();
|
||||
clientIter!=pvRecordClientList.end();
|
||||
clientIter++ )
|
||||
{
|
||||
while(true) {
|
||||
clientIter = pvRecordClientList.begin();
|
||||
if(clientIter==pvRecordClientList.end()) break;
|
||||
pvRecordClientList.erase(clientIter);
|
||||
unlock();
|
||||
(*clientIter)->detach(getPtrSelf());
|
||||
lock();
|
||||
}
|
||||
pvRecordClientList.clear();
|
||||
|
||||
pvListenerList.clear();
|
||||
pvRecordStructure->destroy();
|
||||
pvRecordStructure.reset();
|
||||
convert.reset();
|
||||
pvStructure.reset();
|
||||
|
||||
unlock();
|
||||
}
|
||||
|
||||
@ -211,6 +211,10 @@ bool PVRecord::addPVRecordClient(PVRecordClientPtr const & pvRecordClient)
|
||||
bool PVRecord::removePVRecordClient(PVRecordClientPtr const & pvRecordClient)
|
||||
{
|
||||
lock();
|
||||
if(isDestroyed) {
|
||||
unlock();
|
||||
return false;
|
||||
}
|
||||
std::list<PVRecordClientPtr>::iterator iter;
|
||||
for (iter = pvRecordClientList.begin();
|
||||
iter!=pvRecordClientList.end();
|
||||
|
111
src/database/recordList.cpp
Normal file
111
src/database/recordList.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
/* recordListTest.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2013.04.18
|
||||
*/
|
||||
|
||||
#include <pv/recordList.h>
|
||||
|
||||
using std::tr1::static_pointer_cast;
|
||||
using namespace epics::pvData;
|
||||
using namespace epics::pvAccess;
|
||||
using namespace std;
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
RecordListRecordPtr RecordListRecord::create(
|
||||
epics::pvData::String const & recordName)
|
||||
{
|
||||
FieldCreatePtr fieldCreate = getFieldCreate();
|
||||
PVDataCreatePtr pvDataCreate = getPVDataCreate();
|
||||
StringArray argNames(2);
|
||||
FieldConstPtrArray argFields(2);
|
||||
argNames[0] = "database";
|
||||
argFields[0] = fieldCreate->createScalar(pvString);
|
||||
argNames[1] = "regularExpression";
|
||||
argFields[1] = fieldCreate->createScalar(pvString);
|
||||
StringArray resNames(2);
|
||||
FieldConstPtrArray resFields(2);
|
||||
resNames[0] = "status";
|
||||
resFields[0] = fieldCreate->createScalar(pvString);
|
||||
resNames[1] = "names";
|
||||
resFields[1] = fieldCreate->createScalarArray(pvString);
|
||||
StringArray topNames(2);
|
||||
FieldConstPtrArray topFields(2);
|
||||
topNames[0] = "arguments";
|
||||
topFields[0] = fieldCreate->createStructure(argNames,argFields);
|
||||
topNames[1] = "result";
|
||||
topFields[1] = fieldCreate->createStructure(resNames,resFields);
|
||||
StructureConstPtr topStructure =
|
||||
fieldCreate->createStructure(topNames,topFields);
|
||||
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure);
|
||||
RecordListRecordPtr pvRecord(
|
||||
new RecordListRecord(recordName,pvStructure));
|
||||
if(!pvRecord->init()) pvRecord.reset();
|
||||
return pvRecord;
|
||||
}
|
||||
|
||||
RecordListRecord::RecordListRecord(
|
||||
epics::pvData::String const & recordName,
|
||||
epics::pvData::PVStructurePtr const & pvStructure)
|
||||
: PVRecord(recordName,pvStructure)
|
||||
{
|
||||
}
|
||||
|
||||
RecordListRecord::~RecordListRecord()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
void RecordListRecord::destroy()
|
||||
{
|
||||
PVRecord::destroy();
|
||||
}
|
||||
|
||||
bool RecordListRecord::init()
|
||||
{
|
||||
initPVRecord();
|
||||
PVStructurePtr pvStructure = getPVStructure();
|
||||
database = pvStructure->getStringField("arguments.database");
|
||||
if(database.get()==NULL) return false;
|
||||
regularExpression = pvStructure->getStringField(
|
||||
"arguments.regularExpression");
|
||||
if(regularExpression.get()==NULL) return false;
|
||||
status = pvStructure->getStringField("result.status");
|
||||
if(status.get()==NULL) return false;
|
||||
PVFieldPtr pvField = pvStructure->getSubField("result.names");
|
||||
if(pvField.get()==NULL) {
|
||||
std::cerr << "no result.names" << std::endl;
|
||||
return false;
|
||||
}
|
||||
names = static_pointer_cast<PVStringArray>(
|
||||
pvStructure->getScalarArrayField("result.names",pvString));
|
||||
if(names.get()==NULL) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void RecordListRecord::process()
|
||||
{
|
||||
PVStringArrayPtr pvNames = PVDatabase::getMaster()->getRecordNames();
|
||||
std::vector<String> const & xxx = pvNames->getVector();
|
||||
size_t n = xxx.size();
|
||||
names->put(0,n,xxx,0);
|
||||
String message("");
|
||||
if(database->get().compare("master")!=0) {
|
||||
message += " can only access master ";
|
||||
}
|
||||
String regEx = regularExpression->get();
|
||||
if(regEx.compare("")!=0 && regEx.compare(".*")!=0) {
|
||||
message += " regularExpression not implemented ";
|
||||
}
|
||||
status->put(message);
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
|
44
src/database/recordList.h
Normal file
44
src/database/recordList.h
Normal file
@ -0,0 +1,44 @@
|
||||
/* recordListTest.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2013.04.18
|
||||
*/
|
||||
#ifndef RECORDLIST_H
|
||||
#define RECORDLIST_H
|
||||
|
||||
#include <pv/pvDatabase.h>
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
|
||||
class RecordListRecord;
|
||||
typedef std::tr1::shared_ptr<RecordListRecord> RecordListRecordPtr;
|
||||
|
||||
class RecordListRecord :
|
||||
public PVRecord
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(RecordListRecord);
|
||||
static RecordListRecordPtr create(
|
||||
epics::pvData::String const & recordName);
|
||||
virtual ~RecordListRecord();
|
||||
virtual void destroy();
|
||||
virtual bool init();
|
||||
virtual void process();
|
||||
private:
|
||||
RecordListRecord(epics::pvData::String const & recordName,
|
||||
epics::pvData::PVStructurePtr const & pvStructure);
|
||||
epics::pvData::PVStringPtr database;
|
||||
epics::pvData::PVStringPtr regularExpression;
|
||||
epics::pvData::PVStringPtr status;
|
||||
epics::pvData::PVStringArrayPtr names;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* RECORDLIST_H */
|
@ -55,45 +55,97 @@ static bool getProcess(PVStructurePtr pvRequest,bool processDefault)
|
||||
return processDefault;
|
||||
}
|
||||
|
||||
/*
|
||||
class ChannelRequestLocal :
|
||||
public ChannelRequest
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelRequestLocal)
|
||||
ChannelRequestLocal() :thelock(mutex) {}
|
||||
virtual ~ChannelRequestLocal() {}
|
||||
virtual void destroy(){}
|
||||
virtual void lock() {thelock.lock();}
|
||||
virtual void unlock() {thelock.unlock();}
|
||||
private:
|
||||
Mutex mutex;
|
||||
Lock thelock;
|
||||
};
|
||||
*/
|
||||
|
||||
|
||||
class ChannelProcessLocal :
|
||||
public ChannelProcess
|
||||
public ChannelProcess,
|
||||
public std::tr1::enable_shared_from_this<ChannelProcessLocal>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelProcessLocal);
|
||||
virtual ~ChannelProcessLocal();
|
||||
static ChannelProcess::shared_pointer create(
|
||||
ChannelProviderLocalPtr const &channelProvider,
|
||||
ChannelProcess::shared_pointer const & channelProcessRequester,
|
||||
virtual ~ChannelProcessLocal() {destroy();}
|
||||
static ChannelProcessLocalPtr create(
|
||||
ChannelLocalPtr const &channelLocal,
|
||||
ChannelProcessRequester::shared_pointer const & channelProcessRequester,
|
||||
PVStructurePtr const & pvRequest,
|
||||
PVRecordPtr const &pvRecord);
|
||||
virtual void process(bool lastRequest);
|
||||
virtual void destroy();
|
||||
virtual void lock() {thelock.lock();}
|
||||
virtual void unlock() {thelock.unlock();}
|
||||
private:
|
||||
shared_pointer getPtrSelf()
|
||||
{
|
||||
return shared_from_this();
|
||||
}
|
||||
ChannelProcessLocal(
|
||||
ChannelLocalPtr const &channelLocal,
|
||||
ChannelProcessRequester::shared_pointer const & channelProcessRequester,
|
||||
PVRecordPtr const &pvRecord)
|
||||
:
|
||||
isDestroyed(false),
|
||||
channelLocal(channelLocal),
|
||||
channelProcessRequester(channelProcessRequester),
|
||||
pvRecord(pvRecord),
|
||||
thelock(mutex)
|
||||
{
|
||||
thelock.unlock();
|
||||
}
|
||||
bool isDestroyed;
|
||||
bool callProcess;
|
||||
ChannelLocalPtr channelLocal;
|
||||
ChannelProcessRequester::shared_pointer channelProcessRequester,;
|
||||
PVRecordPtr pvRecord;
|
||||
Mutex mutex;
|
||||
Lock thelock;
|
||||
};
|
||||
|
||||
ChannelProcessLocalPtr ChannelProcessLocal::create(
|
||||
ChannelLocalPtr const &channelLocal,
|
||||
ChannelProcessRequester::shared_pointer const & channelProcessRequester,
|
||||
PVStructurePtr const & pvRequest,
|
||||
PVRecordPtr const &pvRecord)
|
||||
{
|
||||
ChannelProcessLocalPtr process(new ChannelProcessLocal(
|
||||
channelLocal,
|
||||
channelProcessRequester,
|
||||
pvRecord));
|
||||
channelLocal->addChannelProcess(process);
|
||||
channelProcessRequester->channelProcessConnect(Status::Ok, process);
|
||||
return process;
|
||||
}
|
||||
|
||||
|
||||
void ChannelProcessLocal::destroy()
|
||||
{
|
||||
if(isDestroyed) return;
|
||||
isDestroyed = true;
|
||||
channelLocal->removeChannelProcess(getPtrSelf());
|
||||
channelLocal.reset();
|
||||
channelProcessRequester.reset();
|
||||
pvRecord.reset();
|
||||
}
|
||||
|
||||
void ChannelProcessLocal::process(bool lastRequest)
|
||||
{
|
||||
pvRecord->lock();
|
||||
pvRecord->process();
|
||||
pvRecord->unlock();
|
||||
if(isDestroyed) {
|
||||
Status status(
|
||||
Status::Status::STATUSTYPE_ERROR,
|
||||
"was destroyed");
|
||||
channelProcessRequester->processDone(status);
|
||||
}
|
||||
channelProcessRequester->processDone(Status::Ok);
|
||||
if(lastRequest) destroy();
|
||||
}
|
||||
|
||||
class ChannelGetLocal :
|
||||
public ChannelGet,
|
||||
public std::tr1::enable_shared_from_this<ChannelGetLocal>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelGetLocal);
|
||||
virtual ~ChannelGetLocal(){}
|
||||
virtual ~ChannelGetLocal(){destroy();}
|
||||
static ChannelGetLocalPtr create(
|
||||
ChannelLocalPtr const &channelLocal,
|
||||
ChannelGetRequester::shared_pointer const & channelGetRequester,
|
||||
@ -199,14 +251,17 @@ void ChannelGetLocal::destroy()
|
||||
|
||||
void ChannelGetLocal::get(bool lastRequest)
|
||||
{
|
||||
if(callProcess) pvRecord->process();
|
||||
if(isDestroyed) {
|
||||
Status status(
|
||||
Status::Status::STATUSTYPE_ERROR,
|
||||
"was destroyed");
|
||||
channelGetRequester->getDone(status);
|
||||
}
|
||||
bitSet->clear();
|
||||
pvRecord->lock();
|
||||
if(callProcess) pvRecord->process();
|
||||
pvCopy->updateCopySetBitSet(pvStructure, bitSet, false);
|
||||
pvRecord->unlock();
|
||||
if(firstTime) {
|
||||
bitSet->clear();
|
||||
bitSet->set(0);
|
||||
@ -216,38 +271,332 @@ void ChannelGetLocal::get(bool lastRequest)
|
||||
if(lastRequest) destroy();
|
||||
}
|
||||
|
||||
|
||||
class ChannelLocalPut :
|
||||
public ChannelPut
|
||||
class ChannelPutLocal :
|
||||
public ChannelPut,
|
||||
public std::tr1::enable_shared_from_this<ChannelPutLocal>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelLocalPut);
|
||||
virtual ~ChannelLocalPut();
|
||||
static ChannelPut::shared_pointer create(
|
||||
ChannelProviderLocalPtr const &channelProvider,
|
||||
ChannelPut::shared_pointer const & channelPutRequester,
|
||||
POINTER_DEFINITIONS(ChannelPutLocal);
|
||||
virtual ~ChannelPutLocal(){destroy();}
|
||||
static ChannelPutLocalPtr create(
|
||||
ChannelLocalPtr const &channelLocal,
|
||||
ChannelPutRequester::shared_pointer const & channelPutRequester,
|
||||
PVStructurePtr const & pvRequest,
|
||||
PVRecordPtr const &pvRecord);
|
||||
virtual void put(bool lastRequest);
|
||||
virtual void get(bool lastRequest);
|
||||
virtual void get();
|
||||
virtual void destroy();
|
||||
virtual void lock() {thelock.lock();}
|
||||
virtual void unlock() {thelock.unlock();}
|
||||
private:
|
||||
shared_pointer getPtrSelf()
|
||||
{
|
||||
return shared_from_this();
|
||||
}
|
||||
ChannelPutLocal(
|
||||
bool callProcess,
|
||||
ChannelLocalPtr const &channelLocal,
|
||||
ChannelPutRequester::shared_pointer const & channelPutRequester,
|
||||
PVCopyPtr const &pvCopy,
|
||||
PVStructurePtr const&pvStructure,
|
||||
BitSetPtr const & bitSet,
|
||||
PVRecordPtr const &pvRecord)
|
||||
:
|
||||
isDestroyed(false),
|
||||
callProcess(callProcess),
|
||||
channelLocal(channelLocal),
|
||||
channelPutRequester(channelPutRequester),
|
||||
pvCopy(pvCopy),
|
||||
pvStructure(pvStructure),
|
||||
bitSet(bitSet),
|
||||
pvRecord(pvRecord),
|
||||
thelock(mutex)
|
||||
{
|
||||
thelock.unlock();
|
||||
}
|
||||
bool isDestroyed;
|
||||
bool callProcess;
|
||||
ChannelLocalPtr channelLocal;
|
||||
ChannelPutRequester::shared_pointer channelPutRequester,;
|
||||
PVCopyPtr pvCopy;
|
||||
PVStructurePtr pvStructure;
|
||||
BitSetPtr bitSet;
|
||||
PVRecordPtr pvRecord;
|
||||
Mutex mutex;
|
||||
Lock thelock;
|
||||
};
|
||||
|
||||
ChannelPutLocalPtr ChannelPutLocal::create(
|
||||
ChannelLocalPtr const &channelLocal,
|
||||
ChannelPutRequester::shared_pointer const & channelPutRequester,
|
||||
PVStructurePtr const & pvRequest,
|
||||
PVRecordPtr const &pvRecord)
|
||||
{
|
||||
PVCopyPtr pvCopy = PVCopy::create(
|
||||
pvRecord,
|
||||
pvRequest,
|
||||
"field");
|
||||
if(pvCopy.get()==NULL) {
|
||||
Status status(
|
||||
Status::Status::STATUSTYPE_ERROR,
|
||||
"invalid pvRequest");
|
||||
ChannelPut::shared_pointer channelPut;
|
||||
PVStructurePtr pvStructure;
|
||||
BitSetPtr bitSet;
|
||||
channelPutRequester->channelPutConnect(
|
||||
status,
|
||||
channelPut,
|
||||
pvStructure,
|
||||
bitSet);
|
||||
ChannelPutLocalPtr localPut;
|
||||
return localPut;
|
||||
}
|
||||
PVStructurePtr pvStructure = pvCopy->createPVStructure();
|
||||
BitSetPtr bitSet(new BitSet(pvStructure->getNumberFields()));
|
||||
ChannelPutLocalPtr put(new ChannelPutLocal(
|
||||
getProcess(pvRequest,true),
|
||||
channelLocal,
|
||||
channelPutRequester,
|
||||
pvCopy,
|
||||
pvStructure,
|
||||
bitSet,
|
||||
pvRecord));
|
||||
channelLocal->addChannelPut(put);
|
||||
channelPutRequester->channelPutConnect(Status::Ok, put, pvStructure,bitSet);
|
||||
return put;
|
||||
}
|
||||
|
||||
void ChannelPutLocal::destroy()
|
||||
{
|
||||
if(isDestroyed) return;
|
||||
isDestroyed = true;
|
||||
channelLocal->removeChannelPut(getPtrSelf());
|
||||
channelLocal.reset();
|
||||
channelPutRequester.reset();
|
||||
pvCopy.reset();
|
||||
pvStructure.reset();
|
||||
bitSet.reset();
|
||||
pvRecord.reset();
|
||||
}
|
||||
|
||||
void ChannelPutLocal::get()
|
||||
{
|
||||
if(isDestroyed) {
|
||||
Status status(
|
||||
Status::Status::STATUSTYPE_ERROR,
|
||||
"was destroyed");
|
||||
channelPutRequester->getDone(status);
|
||||
}
|
||||
bitSet->clear();
|
||||
bitSet->set(0);
|
||||
pvRecord->lock();
|
||||
pvCopy->updateCopyFromBitSet(pvStructure, bitSet, false);
|
||||
pvRecord->unlock();
|
||||
channelPutRequester->getDone(Status::Ok);
|
||||
}
|
||||
|
||||
void ChannelPutLocal::put(bool lastRequest)
|
||||
{
|
||||
if(isDestroyed) {
|
||||
Status status(
|
||||
Status::Status::STATUSTYPE_ERROR,
|
||||
"was destroyed");
|
||||
channelPutRequester->getDone(status);
|
||||
}
|
||||
pvRecord->lock();
|
||||
pvCopy->updateRecord(pvStructure, bitSet, false);
|
||||
if(callProcess) pvRecord->process();
|
||||
pvRecord->unlock();
|
||||
channelPutRequester->getDone(Status::Ok);
|
||||
if(lastRequest) destroy();
|
||||
}
|
||||
|
||||
|
||||
class ChannelPutGetLocal :
|
||||
public ChannelPutGet
|
||||
public ChannelPutGet,
|
||||
public std::tr1::enable_shared_from_this<ChannelPutGetLocal>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelPutGetLocal);
|
||||
virtual ~ChannelPutGetLocal();
|
||||
static ChannelPutGet::shared_pointer create(
|
||||
ChannelProviderLocalPtr const &channelProvider,
|
||||
ChannelPutGet::shared_pointer const & channelPutGetRequester,
|
||||
virtual ~ChannelPutGetLocal(){destroy();}
|
||||
static ChannelPutGetLocalPtr create(
|
||||
ChannelLocalPtr const &channelLocal,
|
||||
ChannelPutGetRequester::shared_pointer const & channelPutGetRequester,
|
||||
PVStructurePtr const & pvRequest,
|
||||
PVRecordPtr const &pvRecord);
|
||||
virtual void putGet(bool lastRequest);
|
||||
virtual void getPut();
|
||||
virtual void getGet();
|
||||
virtual void destroy();
|
||||
virtual void lock() {thelock.lock();}
|
||||
virtual void unlock() {thelock.unlock();}
|
||||
private:
|
||||
shared_pointer getPtrSelf()
|
||||
{
|
||||
return shared_from_this();
|
||||
}
|
||||
ChannelPutGetLocal(
|
||||
bool callProcess,
|
||||
ChannelLocalPtr const &channelLocal,
|
||||
ChannelPutGetRequester::shared_pointer const & channelPutGetRequester,
|
||||
PVCopyPtr const &pvPutCopy,
|
||||
PVCopyPtr const &pvGetCopy,
|
||||
PVStructurePtr const&pvPutStructure,
|
||||
PVStructurePtr const&pvGetStructure,
|
||||
BitSetPtr const & putBitSet,
|
||||
BitSetPtr const & getBitSet,
|
||||
PVRecordPtr const &pvRecord)
|
||||
:
|
||||
isDestroyed(false),
|
||||
callProcess(callProcess),
|
||||
channelLocal(channelLocal),
|
||||
channelPutGetRequester(channelPutGetRequester),
|
||||
pvPutCopy(pvPutCopy),
|
||||
pvGetCopy(pvGetCopy),
|
||||
pvPutStructure(pvPutStructure),
|
||||
pvGetStructure(pvGetStructure),
|
||||
putBitSet(putBitSet),
|
||||
getBitSet(getBitSet),
|
||||
pvRecord(pvRecord),
|
||||
thelock(mutex)
|
||||
{
|
||||
thelock.unlock();
|
||||
}
|
||||
bool isDestroyed;
|
||||
bool callProcess;
|
||||
ChannelLocalPtr channelLocal;
|
||||
ChannelPutGetRequester::shared_pointer channelPutGetRequester,;
|
||||
PVCopyPtr pvPutCopy;
|
||||
PVCopyPtr pvGetCopy;
|
||||
PVStructurePtr pvPutStructure;
|
||||
PVStructurePtr pvGetStructure;
|
||||
BitSetPtr putBitSet;
|
||||
BitSetPtr getBitSet;
|
||||
PVRecordPtr pvRecord;
|
||||
Mutex mutex;
|
||||
Lock thelock;
|
||||
};
|
||||
|
||||
ChannelPutGetLocalPtr ChannelPutGetLocal::create(
|
||||
ChannelLocalPtr const &channelLocal,
|
||||
ChannelPutGetRequester::shared_pointer const & channelPutGetRequester,
|
||||
PVStructurePtr const & pvRequest,
|
||||
PVRecordPtr const &pvRecord)
|
||||
{
|
||||
PVCopyPtr pvPutCopy = PVCopy::create(
|
||||
pvRecord,
|
||||
pvRequest,
|
||||
"putField");
|
||||
PVCopyPtr pvGetCopy = PVCopy::create(
|
||||
pvRecord,
|
||||
pvRequest,
|
||||
"getField");
|
||||
if(pvPutCopy.get()==NULL || pvGetCopy.get()==NULL) {
|
||||
Status status(
|
||||
Status::Status::STATUSTYPE_ERROR,
|
||||
"invalid pvRequest");
|
||||
ChannelPutGet::shared_pointer channelPutGet;
|
||||
PVStructurePtr pvStructure;
|
||||
BitSetPtr bitSet;
|
||||
channelPutGetRequester->channelPutGetConnect(
|
||||
status,
|
||||
channelPutGet,
|
||||
pvStructure,
|
||||
pvStructure);
|
||||
ChannelPutGetLocalPtr localPutGet;
|
||||
return localPutGet;
|
||||
}
|
||||
PVStructurePtr pvPutStructure = pvPutCopy->createPVStructure();
|
||||
PVStructurePtr pvGetStructure = pvGetCopy->createPVStructure();
|
||||
BitSetPtr putBitSet(new BitSet(pvPutStructure->getNumberFields()));
|
||||
BitSetPtr getBitSet(new BitSet(pvGetStructure->getNumberFields()));
|
||||
ChannelPutGetLocalPtr putGet(new ChannelPutGetLocal(
|
||||
getProcess(pvRequest,true),
|
||||
channelLocal,
|
||||
channelPutGetRequester,
|
||||
pvPutCopy,
|
||||
pvGetCopy,
|
||||
pvPutStructure,
|
||||
pvGetStructure,
|
||||
putBitSet,
|
||||
getBitSet,
|
||||
pvRecord));
|
||||
channelLocal->addChannelPutGet(putGet);
|
||||
channelPutGetRequester->channelPutGetConnect(
|
||||
Status::Ok, putGet, pvPutStructure,pvGetStructure);
|
||||
return putGet;
|
||||
}
|
||||
|
||||
|
||||
void ChannelPutGetLocal::destroy()
|
||||
{
|
||||
if(isDestroyed) return;
|
||||
isDestroyed = true;
|
||||
channelLocal->removeChannelPutGet(getPtrSelf());
|
||||
channelLocal.reset();
|
||||
channelPutGetRequester.reset();
|
||||
pvPutCopy.reset();
|
||||
pvGetCopy.reset();
|
||||
pvPutStructure.reset();
|
||||
pvGetStructure.reset();
|
||||
putBitSet.reset();
|
||||
getBitSet.reset();
|
||||
pvRecord.reset();
|
||||
}
|
||||
|
||||
void ChannelPutGetLocal::putGet(bool lastRequest)
|
||||
{
|
||||
if(isDestroyed) {
|
||||
Status status(
|
||||
Status::Status::STATUSTYPE_ERROR,
|
||||
"was destroyed");
|
||||
channelPutGetRequester->putGetDone(status);
|
||||
}
|
||||
putBitSet->clear();
|
||||
putBitSet->set(0);
|
||||
pvRecord->lock();
|
||||
pvPutCopy->updateRecord(pvPutStructure, putBitSet, false);
|
||||
if(callProcess) pvRecord->process();
|
||||
pvGetCopy->updateCopySetBitSet(pvGetStructure, getBitSet, false);
|
||||
pvRecord->unlock();
|
||||
getBitSet->clear();
|
||||
getBitSet->set(0);
|
||||
channelPutGetRequester->putGetDone(Status::Ok);
|
||||
if(lastRequest) destroy();
|
||||
}
|
||||
|
||||
void ChannelPutGetLocal::getPut()
|
||||
{
|
||||
if(isDestroyed) {
|
||||
Status status(
|
||||
Status::Status::STATUSTYPE_ERROR,
|
||||
"was destroyed");
|
||||
channelPutGetRequester->getPutDone(status);
|
||||
}
|
||||
pvRecord->lock();
|
||||
pvPutCopy->updateCopySetBitSet(pvPutStructure, putBitSet, false);
|
||||
pvRecord->unlock();
|
||||
putBitSet->clear();
|
||||
putBitSet->set(0);
|
||||
channelPutGetRequester->getPutDone(Status::Ok);
|
||||
}
|
||||
|
||||
void ChannelPutGetLocal::getGet()
|
||||
{
|
||||
if(isDestroyed) {
|
||||
Status status(
|
||||
Status::Status::STATUSTYPE_ERROR,
|
||||
"was destroyed");
|
||||
channelPutGetRequester->getGetDone(status);
|
||||
}
|
||||
pvRecord->lock();
|
||||
pvGetCopy->updateCopySetBitSet(pvGetStructure, getBitSet, false);
|
||||
pvRecord->unlock();
|
||||
getBitSet->clear();
|
||||
getBitSet->set(0);
|
||||
channelPutGetRequester->getGetDone(Status::Ok);
|
||||
}
|
||||
|
||||
class ChannelMonitorLocal :
|
||||
public Monitor
|
||||
{
|
||||
@ -573,6 +922,16 @@ bool ChannelLocal::isConnected()
|
||||
void ChannelLocal::getField(GetFieldRequester::shared_pointer const &requester,
|
||||
String const &subField)
|
||||
{
|
||||
if(subField.size()<1) {
|
||||
StructureConstPtr structure = pvRecord->getPVRecordStructure()->getPVStructure()->getStructure();
|
||||
requester->getDone(Status::Ok,structure);
|
||||
return;
|
||||
}
|
||||
PVFieldPtr pvField = pvRecord->getPVRecordStructure()->getPVStructure()->getSubField(subField);
|
||||
if(pvField.get()!=NULL) {
|
||||
requester->getDone(Status::Ok,pvField->getField());
|
||||
return;
|
||||
}
|
||||
Status status(Status::STATUSTYPE_ERROR,
|
||||
String("client asked for illegal field"));
|
||||
requester->getDone(status,FieldConstPtr());
|
||||
@ -588,12 +947,13 @@ ChannelProcess::shared_pointer ChannelLocal::createChannelProcess(
|
||||
ChannelProcessRequester::shared_pointer const & channelProcessRequester,
|
||||
PVStructure::shared_pointer const & pvRequest)
|
||||
{
|
||||
Status status(Status::STATUSTYPE_ERROR,
|
||||
String("ChannelProcess not supported"));
|
||||
channelProcessRequester->channelProcessConnect(
|
||||
status,
|
||||
ChannelProcess::shared_pointer());
|
||||
return ChannelProcess::shared_pointer();
|
||||
ChannelProcessLocalPtr channelProcess =
|
||||
ChannelProcessLocal::create(
|
||||
getPtrSelf(),
|
||||
channelProcessRequester,
|
||||
pvRequest,
|
||||
pvRecord);
|
||||
return channelProcess;
|
||||
}
|
||||
|
||||
ChannelGet::shared_pointer ChannelLocal::createChannelGet(
|
||||
@ -613,28 +973,26 @@ ChannelPut::shared_pointer ChannelLocal::createChannelPut(
|
||||
ChannelPutRequester::shared_pointer const &channelPutRequester,
|
||||
PVStructure::shared_pointer const &pvRequest)
|
||||
{
|
||||
Status status(Status::STATUSTYPE_ERROR,
|
||||
String("ChannelPut not supported"));
|
||||
channelPutRequester->channelPutConnect(
|
||||
status,
|
||||
ChannelPut::shared_pointer(),
|
||||
PVStructure::shared_pointer(),
|
||||
BitSet::shared_pointer());
|
||||
return ChannelPut::shared_pointer();
|
||||
ChannelPutLocalPtr channelPut =
|
||||
ChannelPutLocal::create(
|
||||
getPtrSelf(),
|
||||
channelPutRequester,
|
||||
pvRequest,
|
||||
pvRecord);
|
||||
return channelPut;
|
||||
}
|
||||
|
||||
ChannelPutGet::shared_pointer ChannelLocal::createChannelPutGet(
|
||||
ChannelPutGetRequester::shared_pointer const &channelPutGetRequester,
|
||||
PVStructure::shared_pointer const &pvRequest)
|
||||
{
|
||||
Status status(Status::STATUSTYPE_ERROR,
|
||||
String("ChannelPutGet not supported"));
|
||||
channelPutGetRequester->channelPutGetConnect(
|
||||
status,
|
||||
ChannelPutGet::shared_pointer(),
|
||||
PVStructure::shared_pointer(),
|
||||
PVStructure::shared_pointer());
|
||||
return ChannelPutGet::shared_pointer();
|
||||
ChannelPutGetLocalPtr channelPutGet =
|
||||
ChannelPutGetLocal::create(
|
||||
getPtrSelf(),
|
||||
channelPutGetRequester,
|
||||
pvRequest,
|
||||
pvRecord);
|
||||
return channelPutGet;
|
||||
}
|
||||
|
||||
ChannelRPC::shared_pointer ChannelLocal::createChannelRPC(
|
||||
|
@ -21,6 +21,7 @@ static String providerName("local");
|
||||
|
||||
class LocalChannelCTX;
|
||||
typedef std::tr1::shared_ptr<LocalChannelCTX> LocalChannelCTXPtr;
|
||||
|
||||
class LocalChannelCTX :
|
||||
public epics::pvData::Runnable,
|
||||
public std::tr1::enable_shared_from_this<LocalChannelCTX>
|
||||
@ -77,6 +78,7 @@ LocalChannelCTX::~LocalChannelCTX()
|
||||
// we need thead.waitForCompletion()
|
||||
event.wait();
|
||||
epicsThreadSleep(1.0);
|
||||
ctx.reset();
|
||||
delete thread;
|
||||
}
|
||||
void LocalChannelCTX::run()
|
||||
@ -87,7 +89,11 @@ void LocalChannelCTX::run()
|
||||
ctx->initialize(getChannelAccess());
|
||||
ctx->printInfo();
|
||||
ctx->run(0);
|
||||
ctx->destroy();
|
||||
// Matej if I switch which is commented then errors when exit
|
||||
// BUT this way causes lots of memory leaks
|
||||
channelProvider->destroy();
|
||||
//ctx->destroy();
|
||||
// Matej end of comments
|
||||
event.signal();
|
||||
}
|
||||
|
||||
@ -107,21 +113,23 @@ ChannelProviderLocal::ChannelProviderLocal()
|
||||
|
||||
ChannelProviderLocal::~ChannelProviderLocal()
|
||||
{
|
||||
pvDatabase.reset();
|
||||
destroy();
|
||||
}
|
||||
|
||||
void ChannelProviderLocal::destroy()
|
||||
{
|
||||
Lock xx(mutex);
|
||||
if(beingDestroyed) return;
|
||||
unregisterChannelProvider(getPtrSelf());
|
||||
beingDestroyed = true;
|
||||
ChannelLocalList::iterator iter;
|
||||
for(iter = channelList.begin(); iter!=channelList.end(); ++iter) {
|
||||
ChannelLocalPtr channel = *iter;
|
||||
channel->destroy();
|
||||
while(true) {
|
||||
iter = channelList.begin();
|
||||
if(iter==channelList.end()) break;
|
||||
(*iter)->destroy();
|
||||
channelList.erase(iter);
|
||||
}
|
||||
channelList.clear();
|
||||
|
||||
pvDatabase->destroy();
|
||||
}
|
||||
|
||||
String ChannelProviderLocal::getProviderName()
|
||||
|
@ -60,19 +60,17 @@ PVCopyPtr PVCopy::create(
|
||||
PVStructurePtr const &pvRequest,
|
||||
String const & structureName)
|
||||
{
|
||||
PVStructurePtr pvStructure(pvRequest);
|
||||
if(structureName.size()>0) {
|
||||
if(pvRequest->getStructure()->getNumberFields()>0) {
|
||||
PVStructurePtr pvStructure
|
||||
= pvRequest->getStructureField(structureName);
|
||||
pvStructure = pvRequest->getStructureField(structureName);
|
||||
if(pvStructure.get()==NULL) return NULLPVCopy;
|
||||
}
|
||||
} else if(pvStructure->getSubField("field")!=NULL) {
|
||||
pvStructure = pvRequest->getStructureField("field");
|
||||
}
|
||||
PVCopyPtr pvCopy = PVCopyPtr(new PVCopy(pvRecord));
|
||||
PVStructurePtr pvStruct = pvRequest;
|
||||
if(pvRequest->getSubField("field")!=NULL) {
|
||||
pvStruct = pvRequest->getStructureField("field");
|
||||
}
|
||||
bool result = pvCopy->init(pvStruct);
|
||||
bool result = pvCopy->init(pvStructure);
|
||||
if(!result) pvCopy.reset();
|
||||
return pvCopy;
|
||||
}
|
||||
|
@ -110,8 +110,8 @@ static void powerSupplyTest()
|
||||
int main(int argc,char *argv[])
|
||||
{
|
||||
scalarTest();
|
||||
//arrayTest();
|
||||
//powerSupplyTest();
|
||||
arrayTest();
|
||||
powerSupplyTest();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -81,6 +81,7 @@ int main(int argc,char *argv[])
|
||||
pvRecord->process();
|
||||
}
|
||||
cout << "processed exampleDouble " << endl;
|
||||
pvRecord->destroy();
|
||||
recordName = "powerSupplyExample";
|
||||
pvStructure.reset();
|
||||
pvStructure = createPowerSupply();
|
||||
@ -121,6 +122,7 @@ int main(int argc,char *argv[])
|
||||
cout << " current " << psr->getCurrent();
|
||||
cout << endl;
|
||||
}
|
||||
psr->destroy();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -20,8 +20,10 @@
|
||||
|
||||
#include <pv/standardField.h>
|
||||
#include <pv/standardPVField.h>
|
||||
#include <pv/exampleCounter.h>
|
||||
#include <pv/powerSupplyRecordTest.h>
|
||||
#include <pv/channelProviderLocal.h>
|
||||
#include <pv/recordList.h>
|
||||
|
||||
using namespace std;
|
||||
using std::tr1::static_pointer_cast;
|
||||
@ -63,21 +65,24 @@ int main(int argc,char *argv[])
|
||||
StandardPVFieldPtr standardPVField = getStandardPVField();
|
||||
String properties;
|
||||
ScalarType scalarType;
|
||||
PVRecordPtr pvRecord;
|
||||
String recordName;
|
||||
bool result(false);
|
||||
recordName = "exampleCounter";
|
||||
pvRecord = ExampleCounter::create(recordName);
|
||||
result = master->addRecord(pvRecord);
|
||||
properties = "alarm,timeStamp";
|
||||
scalarType = pvDouble;
|
||||
recordName = "exampleDouble";
|
||||
PVStructurePtr pvStructure;
|
||||
pvStructure = standardPVField->scalar(scalarType,properties);
|
||||
PVRecordPtr pvRecord = PVRecord::create(recordName,pvStructure);
|
||||
pvRecord = PVRecord::create(recordName,pvStructure);
|
||||
{
|
||||
pvRecord->lock_guard();
|
||||
pvRecord->process();
|
||||
}
|
||||
pvStructure.reset();
|
||||
bool result = master->addRecord(pvRecord);
|
||||
pvRecord.reset();
|
||||
recordName = "powerSupplyExample";
|
||||
result = master->addRecord(pvRecord);
|
||||
recordName = "examplePowerSupply";
|
||||
pvStructure = createPowerSupply();
|
||||
PowerSupplyRecordTestPtr psr =
|
||||
PowerSupplyRecordTest::create(recordName,pvStructure);
|
||||
@ -85,11 +90,15 @@ int main(int argc,char *argv[])
|
||||
cout << "PowerSupplyRecordTest::create failed" << endl;
|
||||
return 1;
|
||||
}
|
||||
pvStructure.reset();
|
||||
result = master->addRecord(psr);
|
||||
cout << "result of addRecord " << recordName << " " << result << endl;
|
||||
psr.reset();
|
||||
recordName = "laptoprecordListPGRPC";
|
||||
pvRecord = RecordListRecord::create(recordName);
|
||||
result = master->addRecord(pvRecord);
|
||||
cout << "exampleServer\n";
|
||||
PVStringArrayPtr pvNames = master->getRecordNames();
|
||||
String buffer;
|
||||
pvNames->toString(&buffer);
|
||||
cout << "recordNames" << endl << buffer << endl;
|
||||
string str;
|
||||
while(true) {
|
||||
cout << "Type exit to stop: \n";
|
||||
|
Reference in New Issue
Block a user