more code implemented. See pvDatabaseCPP.html for details
This commit is contained in:
@ -38,7 +38,7 @@
|
|||||||
<h1>pvDatabaseCPP</h1>
|
<h1>pvDatabaseCPP</h1>
|
||||||
<!-- Maturity: Working Draft or Request for Comments, or Recommendation, and date. -->
|
<!-- 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>
|
<dl>
|
||||||
<dt>Latest version:</dt>
|
<dt>Latest version:</dt>
|
||||||
<dd><a
|
<dd><a
|
||||||
@ -46,11 +46,11 @@
|
|||||||
</dd>
|
</dd>
|
||||||
<dt>This version:</dt>
|
<dt>This version:</dt>
|
||||||
<dd><a
|
<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>
|
</dd>
|
||||||
<dt>Previous version:</dt>
|
<dt>Previous version:</dt>
|
||||||
<dd><a
|
<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>
|
</dd>
|
||||||
<dt>Editors:</dt>
|
<dt>Editors:</dt>
|
||||||
<dd>Marty Kraimer, BNL</dd>
|
<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
|
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.
|
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 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
|
The minimum that an extenson must provide is a top level PVStructure and a process method.
|
||||||
but the framework provides for complex extensions.</p>
|
</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>
|
<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>
|
||||||
<p>This is the beginning of the implementation of pvDataBaseCPP.
|
<p>The following Channel methods are implemented and working: getField,i
|
||||||
It describes the features that will be provided.
|
channelProcess, channelGet, channelPut, and channelPutGet.
|
||||||
The class definitions for PVRecord are implemented.
|
But lots of work remains:</p>
|
||||||
The class definition for PVDatabase are defined but not implemented.</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">
|
<div id="toc">
|
||||||
<h2 class="nocount" style="page-break-before: always">Table of Contents</h2>
|
<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.
|
Channel interfaces as defined by pvAccess.
|
||||||
The local provider provides access to the records in the pvDatabase.
|
The local provider provides access to the records in the pvDatabase.
|
||||||
This local provider is accessed by the remote pvAccess server.
|
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:
|
<p>This document describes components that provides the following features:
|
||||||
<dl>
|
<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>
|
Records can be added and removed from a database.</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<dt>pvAccess</dt>
|
<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.
|
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>
|
This component also includes the monitor and pvCopy components from pvIOCJava</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<p><b>database</b>
|
<p>database provides base classes that make it easy to create record instances.
|
||||||
provides base classes that make it easy to create record instances.
|
|
||||||
The code attached to each record must create the top
|
The code attached to each record must create the top
|
||||||
level PVStructure and the following two methods:</p>
|
level PVStructure and the following two methods:</p>
|
||||||
<dl>
|
<dl>
|
||||||
<dt>init</dt>
|
<dt>init</dt>
|
||||||
<dd>This is a method for initializing the support.
|
<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>
|
</dd>
|
||||||
<dt>process</dt>
|
<dt>process</dt>
|
||||||
<dd>This is what makes a record <b>smart</b>.
|
<dd>This is what makes a record smart.
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<h3>Relationship with pvIOCJava.</h3>
|
<h3>Relationship with pvIOCJava.</h3>
|
||||||
<p>This document descibes a C++ implementation of some of the components in pvIOCJava,
|
<p>This document descibes a C++ implementation of some of the components in pvIOCJava,
|
||||||
which also implements a pvDatabase.
|
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.
|
memory resident records.
|
||||||
pvDatabaseCPP does not implement any of the specialized support that pvIOCJava
|
pvDatabaseCPP does not implement any of the specialized support that pvIOCJava
|
||||||
provides.
|
provides.
|
||||||
@ -190,10 +177,10 @@ mrk> bin/linux-x86_64/exampleCounter
|
|||||||
<dd>The source code for the counter.</dd>
|
<dd>The source code for the counter.</dd>
|
||||||
<dt>exampleCounterMain.cpp</dt>
|
<dt>exampleCounterMain.cpp</dt>
|
||||||
<dd>A main program that runs the example so that it can be accessed
|
<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>
|
</dl>
|
||||||
<h4>ExampleCounter.h</h4>
|
<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.
|
The complete implementation is in the header file.
|
||||||
A serious implementation would probably break the code into two files:
|
A serious implementation would probably break the code into two files:
|
||||||
1) a header, and 2) the implementation. The description consists of</p>
|
1) a header, and 2) the implementation. The description consists of</p>
|
||||||
@ -208,26 +195,32 @@ public:
|
|||||||
POINTER_DEFINITIONS(ExampleCounter);
|
POINTER_DEFINITIONS(ExampleCounter);
|
||||||
static ExampleCounterPtr create(
|
static ExampleCounterPtr create(
|
||||||
epics::pvData::String const & recordName);
|
epics::pvData::String const & recordName);
|
||||||
virtual ~ExampleCounter() {}
|
virtual ~ExampleCounter();
|
||||||
|
virtual void destroy();
|
||||||
virtual bool init();
|
virtual bool init();
|
||||||
virtual void process();
|
virtual void process();
|
||||||
private:
|
private:
|
||||||
ExampleCounter(epics::pvData::String const & recordName,
|
ExampleCounter(epics::pvData::String const & recordName,
|
||||||
epics::pvData::PVStructurePtr const & pvStructure);
|
epics::pvData::PVStructurePtr const & pvStructure);
|
||||||
epics::pvData::PVLongPtr pvValue;
|
epics::pvData::PVLongPtr pvValue;
|
||||||
|
epics::pvData::PVTimeStamp pvTimeStamp;
|
||||||
|
epics::pvData::TimeStamp timeStamp;
|
||||||
};
|
};
|
||||||
</pre>
|
</pre>
|
||||||
<p>where</p>
|
<p>where</p>
|
||||||
<dl>
|
<dl>
|
||||||
<dt>create<dt>
|
<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.
|
a similar static method.
|
||||||
</dd>
|
</dd>
|
||||||
<dt>~ExampleCounter<dt>
|
<dt>~ExampleCounter<dt>
|
||||||
<dd>The destructor must be declared virtual.</dd>
|
<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>
|
<dt>init<dt>
|
||||||
<dd>A method to initialize the support. It returns <b>true</b>
|
<dd>A method to initialize the support. It returns true
|
||||||
if initialization is successful and <b>false</b> if not.
|
if initialization is successful and false if not.
|
||||||
NOTE that this is a virtual method of PVRecord itself.</dd>
|
NOTE that this is a virtual method of PVRecord itself.</dd>
|
||||||
<dt>process<dt>
|
<dt>process<dt>
|
||||||
<dd>
|
<dd>
|
||||||
@ -236,17 +229,18 @@ private:
|
|||||||
<dt>ExampleCounter<dt>
|
<dt>ExampleCounter<dt>
|
||||||
<dd>For the example this is private.</dd>
|
<dd>For the example this is private.</dd>
|
||||||
<dt>pvValue</dt>
|
<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.
|
accesses.
|
||||||
</dd>
|
</dd>
|
||||||
<dl>
|
<dl>
|
||||||
<p>The implementation of <b>create</b> is:</p>
|
<p>The implementation of create method is:</p>
|
||||||
<pre>
|
<pre>
|
||||||
ExampleCounterPtr ExampleCounter::create(
|
ExampleCounterPtr ExampleCounter::create(
|
||||||
epics::pvData::String const & recordName)
|
epics::pvData::String const & recordName)
|
||||||
{
|
{
|
||||||
epics::pvData::PVStructurePtr pvStructure =
|
epics::pvData::PVStructurePtr pvStructure =
|
||||||
epics::pvData::getStandardPVField()->scalar(epics::pvData::pvDouble,"");
|
epics::pvData::getStandardPVField()->scalar(
|
||||||
|
epics::pvData::pvDouble,"timeStamp,alarm"");
|
||||||
ExampleCounterPtr pvRecord(
|
ExampleCounterPtr pvRecord(
|
||||||
new ExampleCounter(recordName,pvStructure));
|
new ExampleCounter(recordName,pvStructure));
|
||||||
if(!pvRecord->init()) pvRecord.reset();
|
if(!pvRecord->init()) pvRecord.reset();
|
||||||
@ -256,20 +250,38 @@ ExampleCounterPtr ExampleCounter::create(
|
|||||||
This:
|
This:
|
||||||
<ul>
|
<ul>
|
||||||
<li>Creates the top level structure.</li>
|
<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>Calls init and if it fails resets the shared pointer.</li>
|
||||||
<li>Returns the shared pointer to the newly created record.</li>
|
<li>Returns the shared pointer to the newly created record.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>The private constructor is just:</p>
|
<p>The private constructor method is:</p>
|
||||||
<pre>
|
<pre>
|
||||||
ExampleCounter::ExampleCounter(
|
ExampleCounter::ExampleCounter(
|
||||||
epics::pvData::String const & recordName,
|
epics::pvData::String const & recordName,
|
||||||
epics::pvData::PVStructurePtr const & pvStructure)
|
epics::pvData::PVStructurePtr const & pvStructure)
|
||||||
: PVRecord(recordName,pvStructure)
|
: PVRecord(recordName,pvStructure)
|
||||||
{ }
|
{
|
||||||
|
pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
|
||||||
|
}
|
||||||
</pre>
|
</pre>
|
||||||
The example is very simple. It just calls the base class constructor.
|
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>
|
<pre>
|
||||||
bool ExampleCounter::init()
|
bool ExampleCounter::init()
|
||||||
{
|
{
|
||||||
@ -283,23 +295,26 @@ bool ExampleCounter::init()
|
|||||||
</pre>
|
</pre>
|
||||||
This:
|
This:
|
||||||
<ul>
|
<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>
|
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>
|
which must be a scalar with type long.</li>
|
||||||
<li>If a long value field was not found it returns false.</li>
|
<li>If a long value field was not found it returns false.</li>
|
||||||
<li>Returns true</li>
|
<li>Returns true</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>The implementation of <b>process</b> is:</p>
|
<p>The implementation of process is:</p>
|
||||||
<pre>
|
<pre>
|
||||||
void ExampleCounter::process()
|
void ExampleCounter::process()
|
||||||
{
|
{
|
||||||
pvValue->put(pvValue->get() + 1.0);
|
pvValue->put(pvValue->get() + 1.0);
|
||||||
|
timeStamp.getCurrent();
|
||||||
|
pvTimeStamp.set(timeStamp);
|
||||||
}
|
}
|
||||||
</pre>
|
</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>
|
<h4>exampleCounterMain.cpp</h4>
|
||||||
<p>This is in <b>test/server</b>.
|
<p>This is in test/server.
|
||||||
The main program is:</p>
|
The main program is:</p>
|
||||||
<pre>
|
<pre>
|
||||||
int main(int argc,char *argv[])
|
int main(int argc,char *argv[])
|
||||||
@ -326,10 +341,10 @@ This:
|
|||||||
<ul>
|
<ul>
|
||||||
<li>Gets a pointer to the master database.</li>
|
<li>Gets a pointer to the master database.</li>
|
||||||
<li>Creates the local Channel Provider. This starts the pvAccess server.</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>
|
||||||
<li>Prints <b>exampleCounter</b> on standard out.</li>
|
<li>Prints exampleCounter on standard out.</li>
|
||||||
<li>Runs forever until the user types <b>exit</b> on standard in.</li>
|
<li>Runs forever until the user types exit on standard in.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h3>Phased Development</h3>
|
<h3>Phased Development</h3>
|
||||||
<p>This documentation describes the first phase of a phased implementation of pvDatabaseCPP:</pp>
|
<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>
|
</dd>
|
||||||
<dt>pvRecord.cpp</dt>
|
<dt>pvRecord.cpp</dt>
|
||||||
<dd>
|
<dd>
|
||||||
The implementation of the base class for <b>PVREcord</b>.
|
The implementation of the base class for PVREcord.
|
||||||
It can also implement record instances with a <b>process</b>
|
It can also implement record instances with a process
|
||||||
method does nothing.
|
method does nothing.
|
||||||
This can be used to create a "dumb" record where all changes are
|
This can be used to create a "dumb" record where all changes are
|
||||||
done by clients.
|
done by clients.
|
||||||
@ -435,7 +450,7 @@ The following are the minimium features required</p>
|
|||||||
header file and creates a record instance.
|
header file and creates a record instance.
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</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.
|
smart records.
|
||||||
It describes the following classes:</p>
|
It describes the following classes:</p>
|
||||||
<dl>
|
<dl>
|
||||||
@ -443,7 +458,7 @@ It describes the following classes:</p>
|
|||||||
<dd>This provides the methods required by localChannelProvider to implement Channel.</dd>
|
<dd>This provides the methods required by localChannelProvider to implement Channel.</dd>
|
||||||
<dt>PVRecordField</dt>
|
<dt>PVRecordField</dt>
|
||||||
<dt>PVRecordStructure</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>
|
can be implemented.</dd>
|
||||||
<dt>PVRecordClient</dt>
|
<dt>PVRecordClient</dt>
|
||||||
<dd>This is called by anything that acceses PVRecord.</dd>
|
<dd>This is called by anything that acceses PVRecord.</dd>
|
||||||
@ -459,6 +474,7 @@ namespace epics { namespace pvDatabase {
|
|||||||
|
|
||||||
class PVRecord;
|
class PVRecord;
|
||||||
typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
|
typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
|
||||||
|
typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
|
||||||
|
|
||||||
class PVRecordField;
|
class PVRecordField;
|
||||||
typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
|
typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
|
||||||
@ -485,7 +501,7 @@ typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
|
|||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<h3>class PVRecord</h3>
|
<h3>class PVRecord</h3>
|
||||||
<p><b>NOTES:</b>
|
<p>NOTES:
|
||||||
<ul>
|
<ul>
|
||||||
<li>This section uses the name record instead of "an instance of PVRecord".</li>
|
<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,
|
<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>
|
the local channel provider and record implementers.</li>
|
||||||
<li>Most readers will not care about most of the PVRecord methods.
|
<li>Most readers will not care about most of the PVRecord methods.
|
||||||
Most of the methods are used by the pvAccess code.
|
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.
|
These are described first.
|
||||||
</ul>
|
</ul>
|
||||||
<hr>PVRecord Methods</h4>
|
<hr>PVRecord Methods</h4>
|
||||||
@ -512,9 +528,6 @@ public:
|
|||||||
static PVRecordPtr create(
|
static PVRecordPtr create(
|
||||||
epics::pvData::String const & recordName,
|
epics::pvData::String const & recordName,
|
||||||
epics::pvData::PVStructurePtr const & pvStructure);
|
epics::pvData::PVStructurePtr const & pvStructure);
|
||||||
PVRecord(
|
|
||||||
epics::pvData::String const & recordName,
|
|
||||||
epics::pvData::PVStructurePtr const & pvStructure);
|
|
||||||
virtual ~PVRecord();
|
virtual ~PVRecord();
|
||||||
virtual void destroy();
|
virtual void destroy();
|
||||||
epics::pvData::String getRecordName();
|
epics::pvData::String getRecordName();
|
||||||
@ -546,6 +559,9 @@ public:
|
|||||||
void toString(epics::pvData::StringBuilder buf);
|
void toString(epics::pvData::StringBuilder buf);
|
||||||
void toString(epics::pvData::StringBuilder buf,int indentLevel);
|
void toString(epics::pvData::StringBuilder buf,int indentLevel);
|
||||||
protected:
|
protected:
|
||||||
|
PVRecord(
|
||||||
|
epics::pvData::String const & recordName,
|
||||||
|
epics::pvData::PVStructurePtr const & pvStructure);
|
||||||
void initPVRecord();
|
void initPVRecord();
|
||||||
epics::pvData::PVStructurePtr getPVStructure();
|
epics::pvData::PVStructurePtr getPVStructure();
|
||||||
PVRecordPtr getPtrSelf()
|
PVRecordPtr getPtrSelf()
|
||||||
@ -561,7 +577,7 @@ private:
|
|||||||
<dt>init</dt>
|
<dt>init</dt>
|
||||||
<dd>Virtual method.
|
<dd>Virtual method.
|
||||||
<p>Derived classes must implement this method.
|
<p>Derived classes must implement this method.
|
||||||
This method <b>Must</b> call initPVRecord.</p>
|
This method Must call initPVRecord.</p>
|
||||||
</dd>
|
</dd>
|
||||||
<dt>process</dt>
|
<dt>process</dt>
|
||||||
<dd>Virtual method.
|
<dd>Virtual method.
|
||||||
@ -571,8 +587,6 @@ private:
|
|||||||
<dt>create</dt>
|
<dt>create</dt>
|
||||||
<dd>Static method to create dumb records, i.e. records with a process method
|
<dd>Static method to create dumb records, i.e. records with a process method
|
||||||
that does nothing.</dd>
|
that does nothing.</dd>
|
||||||
<dt>PVRecord</dt>
|
|
||||||
<dd>The constructor. It requires a recordName and a top level PVStructure.</dd>
|
|
||||||
<dt>~PVRecord</dt>
|
<dt>~PVRecord</dt>
|
||||||
<dd>The destructor which must be virtual. A derived class must also have
|
<dd>The destructor which must be virtual. A derived class must also have
|
||||||
a virtual destructor.</dd>
|
a virtual destructor.</dd>
|
||||||
@ -598,14 +612,14 @@ private:
|
|||||||
Any code accessing the data in the record or calling other PVRecord methods
|
Any code accessing the data in the record or calling other PVRecord methods
|
||||||
must have the record locked.</dd>
|
must have the record locked.</dd>
|
||||||
<dt>tryLock</dt>
|
<dt>tryLock</dt>
|
||||||
<dd>If <b>true</b> then just like <b>lock</b>.
|
<dd>If true then just like lock.
|
||||||
If <b>false</b>client can not access record.
|
If falseclient can not access record.
|
||||||
A client can try to simultaneously hold the lock for more than two records
|
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.
|
by calling this method. But must be willing to accept failure.
|
||||||
</dd>
|
</dd>
|
||||||
<dt>lockOtherRecord</dt>
|
<dt>lockOtherRecord</dt>
|
||||||
<dd>A client that holds the lock for one record can lock one other record.
|
<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.
|
more then one record.
|
||||||
</dd>
|
</dd>
|
||||||
<dt>addPVRecordClient</dt>
|
<dt>addPVRecordClient</dt>
|
||||||
@ -625,15 +639,17 @@ private:
|
|||||||
<dd>End a group of puts.
|
<dd>End a group of puts.
|
||||||
This results in all registered PVListeners being called.</dd>
|
This results in all registered PVListeners being called.</dd>
|
||||||
<dt>getRequesterName</dt>
|
<dt>getRequesterName</dt>
|
||||||
<dd>virtual method of <b>Requester</b>
|
<dd>virtual method of Requester
|
||||||
</dd>
|
</dd>
|
||||||
<dt>message</dt>
|
<dt>message</dt>
|
||||||
<dd>Can be called by implementation code.
|
<dd>Can be called by implementation code.
|
||||||
The message will be sent to every requester.</dd>
|
The message will be sent to every requester.</dd>
|
||||||
<dt>toString</dt>
|
<dt>toString</dt>
|
||||||
<dd>Just calls the top level PVStructure toString method.</dd>
|
<dd>Just calls the top level PVStructure toString method.</dd>
|
||||||
<dt>initRecord</dt>
|
<dt>PVRecord</dt>
|
||||||
<dd>This method <b>must</b> be called by derived class.</dd>
|
<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>
|
<dt>getPVStructure</dt>
|
||||||
<dd>Called by derived class.</dd>
|
<dd>Called by derived class.</dd>
|
||||||
</dl>
|
</dl>
|
||||||
@ -649,6 +665,7 @@ public:
|
|||||||
PVRecordStructurePtr const &parent,
|
PVRecordStructurePtr const &parent,
|
||||||
PVRecordPtr const & pvRecord);
|
PVRecordPtr const & pvRecord);
|
||||||
virtual ~PVRecordField();
|
virtual ~PVRecordField();
|
||||||
|
virtual void destroy();
|
||||||
PVRecordStructurePtr getParent();
|
PVRecordStructurePtr getParent();
|
||||||
epics::pvData::PVFieldPtr getPVField();
|
epics::pvData::PVFieldPtr getPVField();
|
||||||
epics::pvData::String getFullFieldName();
|
epics::pvData::String getFullFieldName();
|
||||||
@ -679,6 +696,8 @@ that holds the data. It has the following methods:
|
|||||||
<dd>The constructor.</dd>
|
<dd>The constructor.</dd>
|
||||||
<dt>~PVRecordField</dt>
|
<dt>~PVRecordField</dt>
|
||||||
<dd>The destructor.</dd>
|
<dd>The destructor.</dd>
|
||||||
|
<dt>destroy</dt>
|
||||||
|
<dd>Called by PVRecordStructure when it's destroy method is called.</dd>
|
||||||
<dt>getParent</dt>
|
<dt>getParent</dt>
|
||||||
<dd>Get the parent PVRecordStructure for this field.</dd>
|
<dd>Get the parent PVRecordStructure for this field.</dd>
|
||||||
<dt>getPVField</dt>
|
<dt>getPVField</dt>
|
||||||
@ -712,6 +731,7 @@ public:
|
|||||||
epics::pvData::PVStructurePtr const & pvStructure,
|
epics::pvData::PVStructurePtr const & pvStructure,
|
||||||
PVRecordFieldPtrArrayPtr const & pvRecordField);
|
PVRecordFieldPtrArrayPtr const & pvRecordField);
|
||||||
virtual ~PVRecordStructure();
|
virtual ~PVRecordStructure();
|
||||||
|
virtual void destroy();
|
||||||
PVRecordFieldPtrArrayPtr getPVRecordFields();
|
PVRecordFieldPtrArrayPtr getPVRecordFields();
|
||||||
epics::pvData::PVStructurePtr getPVStructure();
|
epics::pvData::PVStructurePtr getPVStructure();
|
||||||
virtual void removeListener(PVListenerPtr const & pvListener);
|
virtual void removeListener(PVListenerPtr const & pvListener);
|
||||||
@ -795,6 +815,7 @@ public:
|
|||||||
POINTER_DEFINITIONS(PVDatabase);
|
POINTER_DEFINITIONS(PVDatabase);
|
||||||
static PVDatabasePtr getMaster();
|
static PVDatabasePtr getMaster();
|
||||||
virtual ~PVDatabase();
|
virtual ~PVDatabase();
|
||||||
|
virtual void destroy();
|
||||||
PVRecordPtr findRecord(epics::pvData::String const& recordName);
|
PVRecordPtr findRecord(epics::pvData::String const& recordName);
|
||||||
bool addRecord(PVRecordPtr const & record);
|
bool addRecord(PVRecordPtr const & record);
|
||||||
bool removeRecord(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>
|
<dd>Get the master database. This is the database that localChannelProvider access.</dd>
|
||||||
<dt>~PVDatabase</dt>
|
<dt>~PVDatabase</dt>
|
||||||
<dd>The destructor.</dd>
|
<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>
|
<dt>findRecord</dt>
|
||||||
<dd>Find a record. An empty pointer is returned if the record is not in the database.</dd>
|
<dd>Find a record. An empty pointer is returned if the record is not in the database.</dd>
|
||||||
<dt>addRecord</dt>
|
<dt>addRecord</dt>
|
||||||
@ -829,7 +853,7 @@ private:
|
|||||||
<p>Not yet described.
|
<p>Not yet described.
|
||||||
It is only of interest to someone who wants to understand how it works.
|
It is only of interest to someone who wants to understand how it works.
|
||||||
</p>
|
</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>
|
<dl>
|
||||||
<dt>pvCopy</dt>
|
<dt>pvCopy</dt>
|
||||||
<dt>monitor</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 += pvDatabase.h
|
||||||
INC += powerSupplyRecordTest.h
|
INC += powerSupplyRecordTest.h
|
||||||
INC += exampleCounter.h
|
INC += exampleCounter.h
|
||||||
|
INC += recordList.h
|
||||||
LIBSRCS += pvRecord.cpp
|
LIBSRCS += pvRecord.cpp
|
||||||
LIBSRCS += pvDatabase.cpp
|
LIBSRCS += pvDatabase.cpp
|
||||||
|
LIBSRCS += recordList.cpp
|
||||||
|
|
||||||
SRC_DIRS += $(DATABASE)/pvAccess
|
SRC_DIRS += $(DATABASE)/pvAccess
|
||||||
INC += channelProviderLocal.h
|
INC += channelProviderLocal.h
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
|
|
||||||
#include <pv/pvDatabase.h>
|
#include <pv/pvDatabase.h>
|
||||||
#include <pv/standardPVField.h>
|
#include <pv/standardPVField.h>
|
||||||
|
#include <pv/timeStamp.h>
|
||||||
|
#include <pv/pvTimeStamp.h>
|
||||||
|
|
||||||
namespace epics { namespace pvDatabase {
|
namespace epics { namespace pvDatabase {
|
||||||
|
|
||||||
@ -27,20 +29,23 @@ public:
|
|||||||
POINTER_DEFINITIONS(ExampleCounter);
|
POINTER_DEFINITIONS(ExampleCounter);
|
||||||
static ExampleCounterPtr create(
|
static ExampleCounterPtr create(
|
||||||
epics::pvData::String const & recordName);
|
epics::pvData::String const & recordName);
|
||||||
virtual ~ExampleCounter(){}
|
virtual ~ExampleCounter();
|
||||||
|
virtual void destroy();
|
||||||
virtual bool init();
|
virtual bool init();
|
||||||
virtual void process();
|
virtual void process();
|
||||||
private:
|
private:
|
||||||
ExampleCounter(epics::pvData::String const & recordName,
|
ExampleCounter(epics::pvData::String const & recordName,
|
||||||
epics::pvData::PVStructurePtr const & pvStructure);
|
epics::pvData::PVStructurePtr const & pvStructure);
|
||||||
epics::pvData::PVLongPtr pvValue;
|
epics::pvData::PVLongPtr pvValue;
|
||||||
|
epics::pvData::PVTimeStamp pvTimeStamp;
|
||||||
|
epics::pvData::TimeStamp timeStamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
ExampleCounterPtr ExampleCounter::create(
|
ExampleCounterPtr ExampleCounter::create(
|
||||||
epics::pvData::String const & recordName)
|
epics::pvData::String const & recordName)
|
||||||
{
|
{
|
||||||
epics::pvData::PVStructurePtr pvStructure =
|
epics::pvData::PVStructurePtr pvStructure =
|
||||||
epics::pvData::getStandardPVField()->scalar(epics::pvData::pvLong,"timeStamp");
|
epics::pvData::getStandardPVField()->scalar(epics::pvData::pvLong,"timeStamp,alarm");
|
||||||
ExampleCounterPtr pvRecord(
|
ExampleCounterPtr pvRecord(
|
||||||
new ExampleCounter(recordName,pvStructure));
|
new ExampleCounter(recordName,pvStructure));
|
||||||
if(!pvRecord->init()) pvRecord.reset();
|
if(!pvRecord->init()) pvRecord.reset();
|
||||||
@ -52,6 +57,17 @@ ExampleCounter::ExampleCounter(
|
|||||||
epics::pvData::PVStructurePtr const & pvStructure)
|
epics::pvData::PVStructurePtr const & pvStructure)
|
||||||
: PVRecord(recordName,pvStructure)
|
: PVRecord(recordName,pvStructure)
|
||||||
{
|
{
|
||||||
|
pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
|
||||||
|
}
|
||||||
|
|
||||||
|
ExampleCounter::~ExampleCounter()
|
||||||
|
{
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExampleCounter::destroy()
|
||||||
|
{
|
||||||
|
PVRecord::destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExampleCounter::init()
|
bool ExampleCounter::init()
|
||||||
@ -67,6 +83,8 @@ bool ExampleCounter::init()
|
|||||||
void ExampleCounter::process()
|
void ExampleCounter::process()
|
||||||
{
|
{
|
||||||
pvValue->put(pvValue->get() + 1.0);
|
pvValue->put(pvValue->get() + 1.0);
|
||||||
|
timeStamp.getCurrent();
|
||||||
|
pvTimeStamp.set(timeStamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
}}
|
}}
|
||||||
|
@ -32,6 +32,7 @@ public:
|
|||||||
epics::pvData::String const & recordName,
|
epics::pvData::String const & recordName,
|
||||||
epics::pvData::PVStructurePtr const & pvStructure);
|
epics::pvData::PVStructurePtr const & pvStructure);
|
||||||
virtual ~PowerSupplyRecordTest();
|
virtual ~PowerSupplyRecordTest();
|
||||||
|
virtual void destroy();
|
||||||
virtual bool init();
|
virtual bool init();
|
||||||
virtual void process();
|
virtual void process();
|
||||||
void put(double power,double voltage);
|
void put(double power,double voltage);
|
||||||
@ -72,6 +73,11 @@ PowerSupplyRecordTest::~PowerSupplyRecordTest()
|
|||||||
destroy();
|
destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PowerSupplyRecordTest::destroy()
|
||||||
|
{
|
||||||
|
PVRecord::destroy();
|
||||||
|
}
|
||||||
|
|
||||||
bool PowerSupplyRecordTest::init()
|
bool PowerSupplyRecordTest::init()
|
||||||
{
|
{
|
||||||
initPVRecord();
|
initPVRecord();
|
||||||
|
@ -18,8 +18,6 @@ using namespace std;
|
|||||||
|
|
||||||
namespace epics { namespace pvDatabase {
|
namespace epics { namespace pvDatabase {
|
||||||
|
|
||||||
PVDatabase::~PVDatabase() {}
|
|
||||||
|
|
||||||
PVDatabasePtr PVDatabase::getMaster()
|
PVDatabasePtr PVDatabase::getMaster()
|
||||||
{
|
{
|
||||||
static PVDatabasePtr master;
|
static PVDatabasePtr master;
|
||||||
@ -31,36 +29,98 @@ PVDatabasePtr PVDatabase::getMaster()
|
|||||||
return master;
|
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)
|
PVRecordPtr PVDatabase::findRecord(String const& recordName)
|
||||||
{
|
{
|
||||||
PVRecordMap::iterator iter = recordMap.find(recordName);
|
lock_guard();
|
||||||
if(iter!=recordMap.end()) {
|
PVRecordPtr xxx;
|
||||||
|
if(isDestroyed) return xxx;
|
||||||
|
PVRecordMap::iterator iter = recordMap.find(recordName);
|
||||||
|
if(iter!=recordMap.end()) {
|
||||||
return (*iter).second;
|
return (*iter).second;
|
||||||
}
|
}
|
||||||
PVRecordPtr xxx;
|
return 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)
|
bool PVDatabase::addRecord(PVRecordPtr const & record)
|
||||||
{
|
{
|
||||||
String recordName = record->getRecordName();
|
lock_guard();
|
||||||
PVRecordMap::iterator iter = recordMap.find(recordName);
|
if(isDestroyed) return false;
|
||||||
if(iter!=recordMap.end()) return false;
|
String recordName = record->getRecordName();
|
||||||
recordMap.insert(PVRecordMap::value_type(recordName,record));
|
PVRecordMap::iterator iter = recordMap.find(recordName);
|
||||||
return true;
|
if(iter!=recordMap.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
recordMap.insert(PVRecordMap::value_type(recordName,record));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PVDatabase::removeRecord(PVRecordPtr const & record)
|
bool PVDatabase::removeRecord(PVRecordPtr const & record)
|
||||||
{
|
{
|
||||||
String recordName = record->getRecordName();
|
lock();
|
||||||
PVRecordMap::iterator iter = recordMap.find(recordName);
|
if(isDestroyed) return false;
|
||||||
if(iter!=recordMap.end()) {
|
String recordName = record->getRecordName();
|
||||||
recordMap.erase(iter);
|
PVRecordMap::iterator iter = recordMap.find(recordName);
|
||||||
return true;
|
if(iter!=recordMap.end()) {
|
||||||
}
|
PVRecordPtr pvRecord = (*iter).second;
|
||||||
return false;
|
recordMap.erase(iter);
|
||||||
|
unlock();
|
||||||
|
if(pvRecord.get()!=NULL) pvRecord->destroy();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
unlock();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String PVDatabase::getRequesterName()
|
String PVDatabase::getRequesterName()
|
||||||
|
@ -118,7 +118,7 @@ public:
|
|||||||
* The record will automatically
|
* The record will automatically
|
||||||
* be unlocked when control leaves the block that has the call.
|
* 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.
|
* Lock the record.
|
||||||
* Any code must lock while accessing a record.
|
* Any code must lock while accessing a record.
|
||||||
@ -488,6 +488,10 @@ public:
|
|||||||
* Destructor
|
* Destructor
|
||||||
*/
|
*/
|
||||||
virtual ~PVDatabase();
|
virtual ~PVDatabase();
|
||||||
|
/**
|
||||||
|
* Destroy the PVDatabase.
|
||||||
|
*/
|
||||||
|
virtual void destroy();
|
||||||
/**
|
/**
|
||||||
* Find a record.
|
* Find a record.
|
||||||
* An empty pointer is returned if the record is not in the database.
|
* 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.
|
* @return <b>true</b> if record was removed.
|
||||||
*/
|
*/
|
||||||
bool removeRecord(PVRecordPtr const & record);
|
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.
|
* Virtual method of Requester.
|
||||||
* @return The name.
|
* @return The name.
|
||||||
@ -523,7 +532,13 @@ public:
|
|||||||
epics::pvData::MessageType messageType);
|
epics::pvData::MessageType messageType);
|
||||||
private:
|
private:
|
||||||
PVDatabase();
|
PVDatabase();
|
||||||
|
void lock();
|
||||||
|
void unlock();
|
||||||
|
inline void lock_guard() { epics::pvData::Lock thelock(mutex); }
|
||||||
PVRecordMap recordMap;
|
PVRecordMap recordMap;
|
||||||
|
epics::pvData::Mutex mutex;
|
||||||
|
epics::pvData::Lock thelock;
|
||||||
|
bool isDestroyed;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -68,29 +68,29 @@ void PVRecord::destroy()
|
|||||||
isDestroyed = true;
|
isDestroyed = true;
|
||||||
|
|
||||||
std::list<RequesterPtr>::iterator requesterIter;
|
std::list<RequesterPtr>::iterator requesterIter;
|
||||||
for (
|
while(true) {
|
||||||
requesterIter = requesterList.begin();
|
requesterIter = requesterList.begin();
|
||||||
requesterIter!=requesterList.end();
|
if(requesterIter==requesterList.end()) break;
|
||||||
requesterIter++ ) {
|
requesterList.erase(requesterIter);
|
||||||
|
unlock();
|
||||||
(*requesterIter)->message("record destroyed",fatalErrorMessage);
|
(*requesterIter)->message("record destroyed",fatalErrorMessage);
|
||||||
|
lock();
|
||||||
}
|
}
|
||||||
requesterList.clear();
|
|
||||||
|
|
||||||
std::list<PVRecordClientPtr>::iterator clientIter;
|
std::list<PVRecordClientPtr>::iterator clientIter;
|
||||||
for (clientIter = pvRecordClientList.begin();
|
while(true) {
|
||||||
clientIter!=pvRecordClientList.end();
|
clientIter = pvRecordClientList.begin();
|
||||||
clientIter++ )
|
if(clientIter==pvRecordClientList.end()) break;
|
||||||
{
|
pvRecordClientList.erase(clientIter);
|
||||||
|
unlock();
|
||||||
(*clientIter)->detach(getPtrSelf());
|
(*clientIter)->detach(getPtrSelf());
|
||||||
|
lock();
|
||||||
}
|
}
|
||||||
pvRecordClientList.clear();
|
|
||||||
|
|
||||||
pvListenerList.clear();
|
pvListenerList.clear();
|
||||||
pvRecordStructure->destroy();
|
pvRecordStructure->destroy();
|
||||||
pvRecordStructure.reset();
|
pvRecordStructure.reset();
|
||||||
convert.reset();
|
convert.reset();
|
||||||
pvStructure.reset();
|
pvStructure.reset();
|
||||||
|
|
||||||
unlock();
|
unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,6 +211,10 @@ bool PVRecord::addPVRecordClient(PVRecordClientPtr const & pvRecordClient)
|
|||||||
bool PVRecord::removePVRecordClient(PVRecordClientPtr const & pvRecordClient)
|
bool PVRecord::removePVRecordClient(PVRecordClientPtr const & pvRecordClient)
|
||||||
{
|
{
|
||||||
lock();
|
lock();
|
||||||
|
if(isDestroyed) {
|
||||||
|
unlock();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
std::list<PVRecordClientPtr>::iterator iter;
|
std::list<PVRecordClientPtr>::iterator iter;
|
||||||
for (iter = pvRecordClientList.begin();
|
for (iter = pvRecordClientList.begin();
|
||||||
iter!=pvRecordClientList.end();
|
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;
|
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 :
|
class ChannelProcessLocal :
|
||||||
public ChannelProcess
|
public ChannelProcess,
|
||||||
|
public std::tr1::enable_shared_from_this<ChannelProcessLocal>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
POINTER_DEFINITIONS(ChannelProcessLocal);
|
POINTER_DEFINITIONS(ChannelProcessLocal);
|
||||||
virtual ~ChannelProcessLocal();
|
virtual ~ChannelProcessLocal() {destroy();}
|
||||||
static ChannelProcess::shared_pointer create(
|
static ChannelProcessLocalPtr create(
|
||||||
ChannelProviderLocalPtr const &channelProvider,
|
ChannelLocalPtr const &channelLocal,
|
||||||
ChannelProcess::shared_pointer const & channelProcessRequester,
|
ChannelProcessRequester::shared_pointer const & channelProcessRequester,
|
||||||
PVStructurePtr const & pvRequest,
|
PVStructurePtr const & pvRequest,
|
||||||
PVRecordPtr const &pvRecord);
|
PVRecordPtr const &pvRecord);
|
||||||
virtual void process(bool lastRequest);
|
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 :
|
class ChannelGetLocal :
|
||||||
public ChannelGet,
|
public ChannelGet,
|
||||||
public std::tr1::enable_shared_from_this<ChannelGetLocal>
|
public std::tr1::enable_shared_from_this<ChannelGetLocal>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
POINTER_DEFINITIONS(ChannelGetLocal);
|
POINTER_DEFINITIONS(ChannelGetLocal);
|
||||||
virtual ~ChannelGetLocal(){}
|
virtual ~ChannelGetLocal(){destroy();}
|
||||||
static ChannelGetLocalPtr create(
|
static ChannelGetLocalPtr create(
|
||||||
ChannelLocalPtr const &channelLocal,
|
ChannelLocalPtr const &channelLocal,
|
||||||
ChannelGetRequester::shared_pointer const & channelGetRequester,
|
ChannelGetRequester::shared_pointer const & channelGetRequester,
|
||||||
@ -199,14 +251,17 @@ void ChannelGetLocal::destroy()
|
|||||||
|
|
||||||
void ChannelGetLocal::get(bool lastRequest)
|
void ChannelGetLocal::get(bool lastRequest)
|
||||||
{
|
{
|
||||||
if(callProcess) pvRecord->process();
|
|
||||||
if(isDestroyed) {
|
if(isDestroyed) {
|
||||||
Status status(
|
Status status(
|
||||||
Status::Status::STATUSTYPE_ERROR,
|
Status::Status::STATUSTYPE_ERROR,
|
||||||
"was destroyed");
|
"was destroyed");
|
||||||
channelGetRequester->getDone(status);
|
channelGetRequester->getDone(status);
|
||||||
}
|
}
|
||||||
|
bitSet->clear();
|
||||||
|
pvRecord->lock();
|
||||||
|
if(callProcess) pvRecord->process();
|
||||||
pvCopy->updateCopySetBitSet(pvStructure, bitSet, false);
|
pvCopy->updateCopySetBitSet(pvStructure, bitSet, false);
|
||||||
|
pvRecord->unlock();
|
||||||
if(firstTime) {
|
if(firstTime) {
|
||||||
bitSet->clear();
|
bitSet->clear();
|
||||||
bitSet->set(0);
|
bitSet->set(0);
|
||||||
@ -216,38 +271,332 @@ void ChannelGetLocal::get(bool lastRequest)
|
|||||||
if(lastRequest) destroy();
|
if(lastRequest) destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ChannelPutLocal :
|
||||||
class ChannelLocalPut :
|
public ChannelPut,
|
||||||
public ChannelPut
|
public std::tr1::enable_shared_from_this<ChannelPutLocal>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
POINTER_DEFINITIONS(ChannelLocalPut);
|
POINTER_DEFINITIONS(ChannelPutLocal);
|
||||||
virtual ~ChannelLocalPut();
|
virtual ~ChannelPutLocal(){destroy();}
|
||||||
static ChannelPut::shared_pointer create(
|
static ChannelPutLocalPtr create(
|
||||||
ChannelProviderLocalPtr const &channelProvider,
|
ChannelLocalPtr const &channelLocal,
|
||||||
ChannelPut::shared_pointer const & channelPutRequester,
|
ChannelPutRequester::shared_pointer const & channelPutRequester,
|
||||||
PVStructurePtr const & pvRequest,
|
PVStructurePtr const & pvRequest,
|
||||||
PVRecordPtr const &pvRecord);
|
PVRecordPtr const &pvRecord);
|
||||||
virtual void put(bool lastRequest);
|
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 :
|
class ChannelPutGetLocal :
|
||||||
public ChannelPutGet
|
public ChannelPutGet,
|
||||||
|
public std::tr1::enable_shared_from_this<ChannelPutGetLocal>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
POINTER_DEFINITIONS(ChannelPutGetLocal);
|
POINTER_DEFINITIONS(ChannelPutGetLocal);
|
||||||
virtual ~ChannelPutGetLocal();
|
virtual ~ChannelPutGetLocal(){destroy();}
|
||||||
static ChannelPutGet::shared_pointer create(
|
static ChannelPutGetLocalPtr create(
|
||||||
ChannelProviderLocalPtr const &channelProvider,
|
ChannelLocalPtr const &channelLocal,
|
||||||
ChannelPutGet::shared_pointer const & channelPutGetRequester,
|
ChannelPutGetRequester::shared_pointer const & channelPutGetRequester,
|
||||||
PVStructurePtr const & pvRequest,
|
PVStructurePtr const & pvRequest,
|
||||||
PVRecordPtr const &pvRecord);
|
PVRecordPtr const &pvRecord);
|
||||||
virtual void putGet(bool lastRequest);
|
virtual void putGet(bool lastRequest);
|
||||||
virtual void getPut();
|
virtual void getPut();
|
||||||
virtual void getGet();
|
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 :
|
class ChannelMonitorLocal :
|
||||||
public Monitor
|
public Monitor
|
||||||
{
|
{
|
||||||
@ -573,6 +922,16 @@ bool ChannelLocal::isConnected()
|
|||||||
void ChannelLocal::getField(GetFieldRequester::shared_pointer const &requester,
|
void ChannelLocal::getField(GetFieldRequester::shared_pointer const &requester,
|
||||||
String const &subField)
|
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,
|
Status status(Status::STATUSTYPE_ERROR,
|
||||||
String("client asked for illegal field"));
|
String("client asked for illegal field"));
|
||||||
requester->getDone(status,FieldConstPtr());
|
requester->getDone(status,FieldConstPtr());
|
||||||
@ -588,12 +947,13 @@ ChannelProcess::shared_pointer ChannelLocal::createChannelProcess(
|
|||||||
ChannelProcessRequester::shared_pointer const & channelProcessRequester,
|
ChannelProcessRequester::shared_pointer const & channelProcessRequester,
|
||||||
PVStructure::shared_pointer const & pvRequest)
|
PVStructure::shared_pointer const & pvRequest)
|
||||||
{
|
{
|
||||||
Status status(Status::STATUSTYPE_ERROR,
|
ChannelProcessLocalPtr channelProcess =
|
||||||
String("ChannelProcess not supported"));
|
ChannelProcessLocal::create(
|
||||||
channelProcessRequester->channelProcessConnect(
|
getPtrSelf(),
|
||||||
status,
|
channelProcessRequester,
|
||||||
ChannelProcess::shared_pointer());
|
pvRequest,
|
||||||
return ChannelProcess::shared_pointer();
|
pvRecord);
|
||||||
|
return channelProcess;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelGet::shared_pointer ChannelLocal::createChannelGet(
|
ChannelGet::shared_pointer ChannelLocal::createChannelGet(
|
||||||
@ -613,28 +973,26 @@ ChannelPut::shared_pointer ChannelLocal::createChannelPut(
|
|||||||
ChannelPutRequester::shared_pointer const &channelPutRequester,
|
ChannelPutRequester::shared_pointer const &channelPutRequester,
|
||||||
PVStructure::shared_pointer const &pvRequest)
|
PVStructure::shared_pointer const &pvRequest)
|
||||||
{
|
{
|
||||||
Status status(Status::STATUSTYPE_ERROR,
|
ChannelPutLocalPtr channelPut =
|
||||||
String("ChannelPut not supported"));
|
ChannelPutLocal::create(
|
||||||
channelPutRequester->channelPutConnect(
|
getPtrSelf(),
|
||||||
status,
|
channelPutRequester,
|
||||||
ChannelPut::shared_pointer(),
|
pvRequest,
|
||||||
PVStructure::shared_pointer(),
|
pvRecord);
|
||||||
BitSet::shared_pointer());
|
return channelPut;
|
||||||
return ChannelPut::shared_pointer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelPutGet::shared_pointer ChannelLocal::createChannelPutGet(
|
ChannelPutGet::shared_pointer ChannelLocal::createChannelPutGet(
|
||||||
ChannelPutGetRequester::shared_pointer const &channelPutGetRequester,
|
ChannelPutGetRequester::shared_pointer const &channelPutGetRequester,
|
||||||
PVStructure::shared_pointer const &pvRequest)
|
PVStructure::shared_pointer const &pvRequest)
|
||||||
{
|
{
|
||||||
Status status(Status::STATUSTYPE_ERROR,
|
ChannelPutGetLocalPtr channelPutGet =
|
||||||
String("ChannelPutGet not supported"));
|
ChannelPutGetLocal::create(
|
||||||
channelPutGetRequester->channelPutGetConnect(
|
getPtrSelf(),
|
||||||
status,
|
channelPutGetRequester,
|
||||||
ChannelPutGet::shared_pointer(),
|
pvRequest,
|
||||||
PVStructure::shared_pointer(),
|
pvRecord);
|
||||||
PVStructure::shared_pointer());
|
return channelPutGet;
|
||||||
return ChannelPutGet::shared_pointer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelRPC::shared_pointer ChannelLocal::createChannelRPC(
|
ChannelRPC::shared_pointer ChannelLocal::createChannelRPC(
|
||||||
|
@ -21,6 +21,7 @@ static String providerName("local");
|
|||||||
|
|
||||||
class LocalChannelCTX;
|
class LocalChannelCTX;
|
||||||
typedef std::tr1::shared_ptr<LocalChannelCTX> LocalChannelCTXPtr;
|
typedef std::tr1::shared_ptr<LocalChannelCTX> LocalChannelCTXPtr;
|
||||||
|
|
||||||
class LocalChannelCTX :
|
class LocalChannelCTX :
|
||||||
public epics::pvData::Runnable,
|
public epics::pvData::Runnable,
|
||||||
public std::tr1::enable_shared_from_this<LocalChannelCTX>
|
public std::tr1::enable_shared_from_this<LocalChannelCTX>
|
||||||
@ -77,6 +78,7 @@ LocalChannelCTX::~LocalChannelCTX()
|
|||||||
// we need thead.waitForCompletion()
|
// we need thead.waitForCompletion()
|
||||||
event.wait();
|
event.wait();
|
||||||
epicsThreadSleep(1.0);
|
epicsThreadSleep(1.0);
|
||||||
|
ctx.reset();
|
||||||
delete thread;
|
delete thread;
|
||||||
}
|
}
|
||||||
void LocalChannelCTX::run()
|
void LocalChannelCTX::run()
|
||||||
@ -87,7 +89,11 @@ void LocalChannelCTX::run()
|
|||||||
ctx->initialize(getChannelAccess());
|
ctx->initialize(getChannelAccess());
|
||||||
ctx->printInfo();
|
ctx->printInfo();
|
||||||
ctx->run(0);
|
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();
|
event.signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,21 +113,23 @@ ChannelProviderLocal::ChannelProviderLocal()
|
|||||||
|
|
||||||
ChannelProviderLocal::~ChannelProviderLocal()
|
ChannelProviderLocal::~ChannelProviderLocal()
|
||||||
{
|
{
|
||||||
pvDatabase.reset();
|
destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChannelProviderLocal::destroy()
|
void ChannelProviderLocal::destroy()
|
||||||
{
|
{
|
||||||
Lock xx(mutex);
|
Lock xx(mutex);
|
||||||
if(beingDestroyed) return;
|
if(beingDestroyed) return;
|
||||||
unregisterChannelProvider(getPtrSelf());
|
|
||||||
beingDestroyed = true;
|
beingDestroyed = true;
|
||||||
ChannelLocalList::iterator iter;
|
ChannelLocalList::iterator iter;
|
||||||
for(iter = channelList.begin(); iter!=channelList.end(); ++iter) {
|
while(true) {
|
||||||
ChannelLocalPtr channel = *iter;
|
iter = channelList.begin();
|
||||||
channel->destroy();
|
if(iter==channelList.end()) break;
|
||||||
|
(*iter)->destroy();
|
||||||
|
channelList.erase(iter);
|
||||||
}
|
}
|
||||||
channelList.clear();
|
|
||||||
|
pvDatabase->destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
String ChannelProviderLocal::getProviderName()
|
String ChannelProviderLocal::getProviderName()
|
||||||
|
@ -60,19 +60,17 @@ PVCopyPtr PVCopy::create(
|
|||||||
PVStructurePtr const &pvRequest,
|
PVStructurePtr const &pvRequest,
|
||||||
String const & structureName)
|
String const & structureName)
|
||||||
{
|
{
|
||||||
|
PVStructurePtr pvStructure(pvRequest);
|
||||||
if(structureName.size()>0) {
|
if(structureName.size()>0) {
|
||||||
if(pvRequest->getStructure()->getNumberFields()>0) {
|
if(pvRequest->getStructure()->getNumberFields()>0) {
|
||||||
PVStructurePtr pvStructure
|
pvStructure = pvRequest->getStructureField(structureName);
|
||||||
= pvRequest->getStructureField(structureName);
|
|
||||||
if(pvStructure.get()==NULL) return NULLPVCopy;
|
if(pvStructure.get()==NULL) return NULLPVCopy;
|
||||||
}
|
}
|
||||||
|
} else if(pvStructure->getSubField("field")!=NULL) {
|
||||||
|
pvStructure = pvRequest->getStructureField("field");
|
||||||
}
|
}
|
||||||
PVCopyPtr pvCopy = PVCopyPtr(new PVCopy(pvRecord));
|
PVCopyPtr pvCopy = PVCopyPtr(new PVCopy(pvRecord));
|
||||||
PVStructurePtr pvStruct = pvRequest;
|
bool result = pvCopy->init(pvStructure);
|
||||||
if(pvRequest->getSubField("field")!=NULL) {
|
|
||||||
pvStruct = pvRequest->getStructureField("field");
|
|
||||||
}
|
|
||||||
bool result = pvCopy->init(pvStruct);
|
|
||||||
if(!result) pvCopy.reset();
|
if(!result) pvCopy.reset();
|
||||||
return pvCopy;
|
return pvCopy;
|
||||||
}
|
}
|
||||||
|
@ -110,8 +110,8 @@ static void powerSupplyTest()
|
|||||||
int main(int argc,char *argv[])
|
int main(int argc,char *argv[])
|
||||||
{
|
{
|
||||||
scalarTest();
|
scalarTest();
|
||||||
//arrayTest();
|
arrayTest();
|
||||||
//powerSupplyTest();
|
powerSupplyTest();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +81,7 @@ int main(int argc,char *argv[])
|
|||||||
pvRecord->process();
|
pvRecord->process();
|
||||||
}
|
}
|
||||||
cout << "processed exampleDouble " << endl;
|
cout << "processed exampleDouble " << endl;
|
||||||
|
pvRecord->destroy();
|
||||||
recordName = "powerSupplyExample";
|
recordName = "powerSupplyExample";
|
||||||
pvStructure.reset();
|
pvStructure.reset();
|
||||||
pvStructure = createPowerSupply();
|
pvStructure = createPowerSupply();
|
||||||
@ -121,6 +122,7 @@ int main(int argc,char *argv[])
|
|||||||
cout << " current " << psr->getCurrent();
|
cout << " current " << psr->getCurrent();
|
||||||
cout << endl;
|
cout << endl;
|
||||||
}
|
}
|
||||||
|
psr->destroy();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,8 +20,10 @@
|
|||||||
|
|
||||||
#include <pv/standardField.h>
|
#include <pv/standardField.h>
|
||||||
#include <pv/standardPVField.h>
|
#include <pv/standardPVField.h>
|
||||||
|
#include <pv/exampleCounter.h>
|
||||||
#include <pv/powerSupplyRecordTest.h>
|
#include <pv/powerSupplyRecordTest.h>
|
||||||
#include <pv/channelProviderLocal.h>
|
#include <pv/channelProviderLocal.h>
|
||||||
|
#include <pv/recordList.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using std::tr1::static_pointer_cast;
|
using std::tr1::static_pointer_cast;
|
||||||
@ -63,21 +65,24 @@ int main(int argc,char *argv[])
|
|||||||
StandardPVFieldPtr standardPVField = getStandardPVField();
|
StandardPVFieldPtr standardPVField = getStandardPVField();
|
||||||
String properties;
|
String properties;
|
||||||
ScalarType scalarType;
|
ScalarType scalarType;
|
||||||
|
PVRecordPtr pvRecord;
|
||||||
String recordName;
|
String recordName;
|
||||||
|
bool result(false);
|
||||||
|
recordName = "exampleCounter";
|
||||||
|
pvRecord = ExampleCounter::create(recordName);
|
||||||
|
result = master->addRecord(pvRecord);
|
||||||
properties = "alarm,timeStamp";
|
properties = "alarm,timeStamp";
|
||||||
scalarType = pvDouble;
|
scalarType = pvDouble;
|
||||||
recordName = "exampleDouble";
|
recordName = "exampleDouble";
|
||||||
PVStructurePtr pvStructure;
|
PVStructurePtr pvStructure;
|
||||||
pvStructure = standardPVField->scalar(scalarType,properties);
|
pvStructure = standardPVField->scalar(scalarType,properties);
|
||||||
PVRecordPtr pvRecord = PVRecord::create(recordName,pvStructure);
|
pvRecord = PVRecord::create(recordName,pvStructure);
|
||||||
{
|
{
|
||||||
pvRecord->lock_guard();
|
pvRecord->lock_guard();
|
||||||
pvRecord->process();
|
pvRecord->process();
|
||||||
}
|
}
|
||||||
pvStructure.reset();
|
result = master->addRecord(pvRecord);
|
||||||
bool result = master->addRecord(pvRecord);
|
recordName = "examplePowerSupply";
|
||||||
pvRecord.reset();
|
|
||||||
recordName = "powerSupplyExample";
|
|
||||||
pvStructure = createPowerSupply();
|
pvStructure = createPowerSupply();
|
||||||
PowerSupplyRecordTestPtr psr =
|
PowerSupplyRecordTestPtr psr =
|
||||||
PowerSupplyRecordTest::create(recordName,pvStructure);
|
PowerSupplyRecordTest::create(recordName,pvStructure);
|
||||||
@ -85,11 +90,15 @@ int main(int argc,char *argv[])
|
|||||||
cout << "PowerSupplyRecordTest::create failed" << endl;
|
cout << "PowerSupplyRecordTest::create failed" << endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
pvStructure.reset();
|
|
||||||
result = master->addRecord(psr);
|
result = master->addRecord(psr);
|
||||||
cout << "result of addRecord " << recordName << " " << result << endl;
|
recordName = "laptoprecordListPGRPC";
|
||||||
psr.reset();
|
pvRecord = RecordListRecord::create(recordName);
|
||||||
|
result = master->addRecord(pvRecord);
|
||||||
cout << "exampleServer\n";
|
cout << "exampleServer\n";
|
||||||
|
PVStringArrayPtr pvNames = master->getRecordNames();
|
||||||
|
String buffer;
|
||||||
|
pvNames->toString(&buffer);
|
||||||
|
cout << "recordNames" << endl << buffer << endl;
|
||||||
string str;
|
string str;
|
||||||
while(true) {
|
while(true) {
|
||||||
cout << "Type exit to stop: \n";
|
cout << "Type exit to stop: \n";
|
||||||
|
Reference in New Issue
Block a user