still early version

This commit is contained in:
Marty Kraimer
2012-12-12 08:49:06 -05:00
parent 179d45e264
commit 7a0fda88ef
10 changed files with 1767 additions and 522 deletions

View File

@ -38,18 +38,20 @@
<h1>pvDatabaseCPP</h1>
<!-- Maturity: Working Draft or Request for Comments, or Recommendation, and date. -->
<h2 class="nocount">EPICS v4 Working Group, Working Draft, 27-Nov-2012</h2>
<h2 class="nocount">EPICS v4 Working Group, Working Draft, 11-Dec-2012</h2>
<dl>
<dt>Latest version:</dt>
<dd><a
href="pvDatabaseCPP.html">pvDatabaseCPP.html</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="pvDatabaseCPP.html">pvDatabaseCPP.html</a>
href="http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvDatabaseCPP/raw-file/tip/documentation/pvDatabaseCPP_20121211.html">pvDatabaseCPP20121211.html</a>
</dd>
<dt>Previous version:</dt>
<dd>None</dd>
<dd><a
href="http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvDatabaseCPP/raw-file/tip/documentation/pvDatabaseCPP_20121127.html">pvDatabaseCPP_20121127.html</a>
</dd>
<dt>Editors:</dt>
<dd>Marty Kraimer, BNL</dd>
</dl>
@ -95,12 +97,12 @@ V4 control system programming environment:</p>
<h2 class="nocount">Status of this Document</h2>
<p>This is the 27-Nov-2012 version of the definition of pvDatabaseCPP.
This is the original version.
<p>This is the 11-Dec-2012 version of the definition of pvDatabaseCPP.
</p>
<p>This is the beginning of the implementation of pvDataBaseCPP.
It describes the features that will be provided.
The class definitions for PVRecord and PVDatabase are defined but not implemented.</p>
The class definitions for PVRecord are implemented.
The class definition for PVDatabase are defined but not implemented.</p>
<div id="toc">
@ -231,11 +233,14 @@ ExampleRecord::ExampleRecord(
bool ExampleRecord::isSynchronous() {return true;}
void ExampleRecord::process(
RecordProcessRequesterPtr const &amp;processRequester)
RecordProcessRequesterPtr const &amp;processRequester,bool alreadyLocked)
{
if(!alreadyLocked) lock();
pvValue-&gt;put(pvValue-&gt;get() + 1);
processRequester-&gt;recordProcessResult(Status::Ok);
unlock();
processRequester-&gt;recordProcessComplete();
dequeueProcessRequest(processRequester);
}
</pre>
<p>where</p>
@ -354,29 +359,72 @@ phase.</p>
<h2>database</h2>
<p>The classes in <b>pvDatabase.h</b> implement a database of memory resident
smart records. The next subsection has the definitions for all the classes
defined in this header file.
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 <b>wrap</b> PVField and PVStructure so that pvCopy and monitor can be implemented.</dd>
<dd>These <b>wrap</b> 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 the PVRecord::message.</dd>
<dt>RecordProcessRequester</dt>
<dd>This is implemented by anything that calls PVRecord::queueProcessRequest.</dd>
<dt>PVRecordClient</dt>
<dd>This is called by anything that acceses PVRecord.</dd>
<dt>RecordPutRequester</dt>
<dd>This is implemented by anything that calls PVRecord::queuePutRequest.</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&lt;PVRecord&gt; PVRecordPtr;
class PVRecordField;
typedef std::tr1::shared_ptr&lt;PVRecordField&gt; PVRecordFieldPtr;
typedef std::vector&lt;PVRecordFieldPtr&gt; PVRecordFieldPtrArray;
typedef std::tr1::shared_ptr&lt;PVRecordFieldPtrArray&gt; PVRecordFieldPtrArrayPtr;
class PVRecordStructure;
typedef std::tr1::shared_ptr&lt;PVRecordStructure&gt; PVRecordStructurePtr;
class PVRecordClient;
typedef std::tr1::shared_ptr&lt;PVRecordClient&gt; PVRecordClientPtr;
class PVListener;
typedef std::tr1::shared_ptr&lt;PVListener&gt; PVListenerPtr;
class RecordProcessRequester;
typedef std::tr1::shared_ptr&lt;RecordProcessRequester&gt; RecordProcessRequesterPtr;
class RecordPutRequester;
typedef std::tr1::shared_ptr&lt;RecordPutRequester&gt; RecordPutRequesterPtr;
class PVDatabase;
typedef std::tr1::shared_ptr&lt;PVDatabase&gt; PVDatabasePtr;
</pre>
<h3>class PVRecord</h3>
<p><b>NOTES:</b>
<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>
</ul>
<hr>PVRecord Methods</h4>
<pre>
class PVRecord
public epics::pvData::Requester,
public std::tr1::enable_shared_from_this&lt;PVRecord&gt;
{
public:
POINTER_DEFINITIONS(PVRecord);
@ -385,32 +433,48 @@ public:
epics::pvData::PVStructurePtr const &amp; pvStructure);
virtual ~PVRecord();
virtual void process(
RecordProcessRequesterPtr const &amp;recordProcessRequester) = 0;
RecordProcessRequesterPtr const &amp;recordProcessRequester,
bool alreadyLocked) = 0;
virtual bool isSynchronous() = 0;
virtual bool requestImmediatePut(epics::pvData::PVFieldPtr const &amp;pvField);
virtual void immediatePutDone();
virtual void destroy();
epics::pvData::String getRecordName();
PVRecordStructurePtr getPVRecordStructure();
PVRecordFieldPtr findPVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField);
bool addRequester(epics::pvData::RequesterPtr const &amp; requester);
bool removeRequester(epics::pvData::RequesterPtr const &amp; requester);
void lock();
void unlock();
void registerClient(PVRecordClientPtr const &amp; pvRecordClient);
void unregisterClient(PVRecordClientPtr const &amp; pvRecordClient);
bool tryLock();
void lockOtherRecord(PVRecordPtr const &amp; otherRecord);
void addPVRecordClient(PVRecordClientPtr const &amp; pvRecordClient);
void removePVRecordClient(PVRecordClientPtr const &amp; pvRecordClient);
void detachClients();
void beginGroupPut();
void endGroupPut();
void registerListener(PVListenerPtr const &amp; pvListener);
void unregisterListener(PVListenerPtr const &amp; pvListener);
void removeEveryListener();
epics::pvData::Status processRequest();
void queueProcessRequest(
RecordProcessRequesterPtr const &amp;recordProcessRequester);
void addRequester(epics::pvData::RequesterPtr const &amp; requester);
void removeRequester(epics::pvData::RequesterPtr const &amp; requester);
void dequeueProcessRequest(
RecordProcessRequesterPtr const &amp;recordProcessRequester);
void queuePutRequest(
RecordPutRequesterPtr const &amp;recordPutRequester);
void putDone(
RecordPutRequesterPtr const &amp;recordPutRequester);
virtual epics::pvData::String getRequesterName();
void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
epics::pvData::String toString();
epics::pvData::String toString(int indentLevel);
void message(
PVRecordFieldPtr const &amp; pvRecordField,
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
void toString(epics::pvData::StringBuilder buf);
void toString(epics::pvData::StringBuilder buf,int indentLevel);
//init MUST be called after derived class is constructed
void init();
};
</pre>
<p>The methods are:</h3>
@ -418,62 +482,132 @@ public:
<dt>PVRecord</dt>
<dd>The constructor. It requires a recordName and a top level PVStructure.</dd>
<dt>~PVRecord</dt>
<dd>The desctructor 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>
<dt>process</dt>
<dd>Pure virtual method. Derived classes must implement this method.</dd>
<dd>Pure virtual method.
<p>Derived classes must implement this method.</p>
<p>A client <b>must</b> only call this method when
<b>RecordProcessRequester::becomeProcessor</b> is called as a result
of a <b>queueProcessRequest</b>.
A client can either call lock before calling processs
or let process lock the record.
If a client wants to put data into the record it should lock, put, and then call
process.</p>
<p>If the record is synchronous, process will return only when all processing
is complete. If the record is asynchronous then process arranges for some
other thread to do the processing and returns.</p>
<p>When processing is done the record calls two client callbacks:</p>
<dl>
<dt>RecordProcessRequester::recordProcessResult</dt>
<dd>This is called with the record still locked.
The clients can get data from the record.</dd>
<dt>RecordProcessRequester::recordProcessComplete</dt>
<dd>This is called with the record unlocked.
The client can no longer access the record.</dd>
</dl>
</dd>
<dt>isSynchronous</dt>
<dd>Pure virtual method. Derived classes must implement this method.</dd>
<dt>requestImmediatePut</dt>
<dd>This is a virtual method.
<p> The purpose is to allow the implementation to provide fields
that allow a client to abort process.
For example a motor record might provide a field <b>stop</b></p>
<p>The default always returns <b>false</b>.</p>
<p>A record implementation can override the default and return <b>true</b>.
In it does requestImmediatePut it returns with the record locked.</p>
<p>The client can change the value of the associated field and then call
<b>immediatePutDone</b></p>
</dd>
<dt>immediatePutDone</dt>
<dd>This is a virtual method.
<p>The default does nothing.</p>
<p>Must be called by client as a result of a call to <b>requestImmediatePut</b>
that returns <b>true</b>.</p>
</dd>
<dt>destroy</dt>
<dd>This is a virtual method.
<p>The default does nothing.</p>
</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</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>registerClient</dt>
<dt>tryLock</dt>
<dd>If <b>true</b> then just like <b>lock</b>.
If <b>false</b>client can not access record.
A client can try to simultaneously hold the lock for more than two records
by calling this method. But must be willing to accept failure.
</dd>
<dt>lockOtherRecord</dt>
<dd>A client that holds the lock for one record can lock one other record.
A client <b>must</b> not call this if the client already has the lock for
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>unregisterClient</dt>
<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>
<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>registerListener</dt>
<dd>Register a PVListener. This must be called before calling pvRecordField.addListener.</dd>
<dt>unregisterListener</dt>
<dd>Unregister a listener. The listener will also be removed from all fields to which it is attached.</dd>
<dt>removeEveryListener</dt>
<dd>This must be called by any code that is deleting or changing the structure of a record.</dd>
<dt>processRequest</dt>
<dd>This is a convenience method for clients that are willing to block if
process is asynchronous. It implements RecordProcessRequester.
If process is synchronous it just calls process and returns the result
to the caller. If process is asynchronous it calls queueProcessRequest,
and process and waits for completion and then returns the result to the caller.</dd>
<dd>End a group of puts.
This results in all registered PVListeners being called.</dd>
<dt>queueProcessRequest</dt>
<dd>Queue a process request.</dd>
<dt>addRequester</dt>
<dd>Add a requester to receive messages.</dd>
<dt>removeRequester</dt>
<dd>Remove a message requester.</dd>
<dt>dequeueProcessRequest</dt>
<dd>This <b>must</b> be called by record implementation after it has
completed a process request.
</dd>
<dt>queuePutRequest</dt>
<dd>Queue a put request.
<p>This is for code that wants to change data in a record without processing.
If <b>RecordPutRequester::requestResult</b> is called with result <b>true</b>
then the record is locked and the client can make changes.
When done the client <b>must</b> call <b>putDone</b></p>
</dd>
<dt>putDone</dt>
<dd>Called by <b>RecordPutRequester</b> after changing values in record.
This method unlocks the record</dd>
<dt>getRequesterName</dt>
<dd>virtual method of <b>Requester</b>
</dd>
<dt>message</dt>
<dd>Can be called by implementation code.
The message will be sent to every requester.</dd>
<dt>init</dt>
<dd>This method <b>must</b> be called by derived class
<b>after</b> class is completely constructed.</dd>
</dl>
<h3>class PVRecordField</h3>
<pre>
class PVRecordField {
public virtual epics::pvData::PostHandler,
public std::tr1::enable_shared_from_this&lt;PVRecordField&gt;
public:
POINTER_DEFINITIONS(PVRecordField);
PVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField,
PVRecordStructurePtr const &amp;parent,
PVRecordPtr const &amp; pvRecord);
virtual ~PVRecordField();
PVRecordStructurePtr getParent();
@ -482,8 +616,8 @@ public:
epics::pvData::String getFullName();
PVRecordPtr getPVRecord();
bool addListener(PVListenerPtr const &amp; pvListener);
void removeListener(PVListenerPtr const &amp; pvListener);
void postPut();
virtual void removeListener(PVListenerPtr const &amp; pvListener);
virtual void postPut();
virtual void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
@ -533,9 +667,8 @@ public:
virtual ~PVRecordStructure();
PVRecordFieldPtrArrayPtr getPVRecordFields();
epics::pvData::PVStructurePtr getPVStructure();
virtual void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
virtual void removeListener(PVListenerPtr const &amp; pvListener);
virtual void postPut();
};
</pre>
<p>When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure
@ -550,13 +683,31 @@ that holds the data. It has the following methods:
<dd>Get the PVRecordField array for the subfields</dd>
<dt>getPVStructure</dt>
<dd>Get the PVStructure for this field.</dd>
<dt>message</dt>
<dd>Called by implementation code. It calls PVRecord::message after prepending the full
fieldname.</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 &amp; 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();
@ -566,7 +717,6 @@ public:
requested,PVRecordFieldPtr const &amp; pvRecordField) = 0;
virtual void beginGroupPut(PVRecordPtr const &amp; pvRecord) = 0;
virtual void endGroupPut(PVRecordPtr const &amp; pvRecord) = 0;
virtual void unlisten(PVRecordPtr const &amp; pvRecord) = 0;
};
</pre>
<p>where</p>
@ -586,19 +736,17 @@ public:
<dd>A related set of changes is being started.</dd>
<dt>endGroupPut</dt>
<dd>A related set of changes is done.</dd>
<dt>unlisten</dt>
<dd>The PVLister is being removed from the record.
This is called when the record is being destroyed or when the record structure
(not the data values) is being changed.</dd>
</dl>
<h3>class RecordProcessRequester</h3>
<pre>
class RecordProcessRequester :
virtual public PVRecordClient,
virtual public epics::pvData::Requester
{
public:
POINTER_DEFINITIONS(RecordProcessRequester);
virtual ~RecordProcessRequester();
virtual void recordDestroyed() = 0;
virtual void becomeProcessor() = 0;
virtual void recordProcessResult(epics::pvData::Status status) = 0;
virtual void recordProcessComplete() = 0;
@ -608,6 +756,8 @@ public:
<dl>
<dt>~RecordProcessRequester</dt>
<dd>The destructor.</dd>
<dt>recordDestroyed</dt>
<dd>Record is being destroyed.</dd>
<dt>becomeProcessor</dt>
<dd>Called as a result of queueRequeProcessst. The requester can the call process.</dd>
<dt>recordProcessResult</dt>
@ -620,20 +770,29 @@ public:
If the process requester called process with leaveActive true then the requester
must call setInactive.</dd>
</dl>
<h3>class PVRecordClient</h3>
<h3>class RecordPutRequester</h3>
<pre>
class PVRecordClient {
POINTER_DEFINITIONS(PVRecordClient);
virtual ~PVRecordClient();
virtual void detach(PVRecordPtr const &amp; pvRecord);
class RecordPutRequester :
virtual public PVRecordClient
{
public:
POINTER_DEFINITIONS(RecordPutRequester);
virtual ~RecordPutRequester();
virtual void requestResult(bool result) = 0;
};
</pre>
<p>where</p>
<dl>
<dt>~PVRecordClient</dt>
<dt>~RecordPutRequester</dt>
<dd>The destructor.</dd>
<dt>detach</dt>
<dd>The record is being removed from the master database,</dd>
<dt>requestResult</dt>
<dd>Result of a call to queuePutRequest. If <b>requestResult</b> is <b>false</b>
then the caller can not access the record.
If <b>requestResult</b> is <b>true</b>
then the record is locked and the caller can get and put data in the record.
When done the caller must call <b>PVRecord::putDone</b>, which will unlock the
record.
</dd>
</dl>
<h3>class PVDatabase</h3>
<pre>

View File

@ -0,0 +1,733 @@
<?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, 27-Nov-2012</h2>
<dl>
<dt>Latest version:</dt>
<dd><a
href="pvDatabaseCPP.html">pvDatabaseCPP.html</a>
</dd>
<dt>This version:</dt>
<dd><a
href="pvDatabaseCPP.html">pvDatabaseCPP.html</a>
</dd>
<dt>Previous version:</dt>
<dd>None</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 must be extended in order to create record instances.
The minimum that an extenson must provide is a top level PVStructure and a process method
but the framework provides for complex extensions.</p>
<p>EPICS version 4 is a set of related products in the EPICS
V4 control system programming environment:</p>
<dl>
<dt><a
href="http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvDataJava/raw-file/tip/documentation/pvDataJava.html">pvData</a></dt>
<dd>pvData (Process Variable Data) defines and implements an efficent way
to store, access, and communicate memory resident structured data</dd>
<dt><a
href="http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvAccessJava/raw-file/tip/documentation/pvAccessJava.html">pvAccess</a></dt>
<dd>pvAccess is a software library for high speed controls network communications,
optimized for pvData</dd>
<dt><a
href="http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvIOCJava/raw-file/tip/documentation/pvIOCJava.html">pvIOC</a></dt>
<dd>pvIOC is a software framework for building network accessable "smart" real time
databases, suitable for interfacing devices in a distributed control system,
that can exchange pvData over pvAccess.
</dd>
<dt><a
href="http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvServiceJava/raw-file/tip/documentation/pvAccessJava.html">pvService</a></dt>
<dd>A middle layer for implementing data services.</dd>
</dl>
<p>Each of these products has a Java and a C++ implementation.</p>
<h2 class="nocount">Status of this Document</h2>
<p>This is the 27-Nov-2012 version of the definition of pvDatabaseCPP.
This is the original version.
</p>
<p>This is the beginning of the implementation of pvDataBaseCPP.
It describes the features that will be provided.
The class definitions for PVRecord and PVDatabase are defined but not implemented.</p>
<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>This document descibes a C++ implementation of some of the components in pvIOCJava.
It extracts the core components required to create a network accessible database of smart
memory resident records.
pvDatabaseCPP does not and will not implement any of the specialized support that pvIOCJava
provides. Instead other projects will implement the specialized support.
It is expected that many services will be created that do not require the full features provided
by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of
them named pvDatabaseJava.
</p>
<p>A brief description of a pvDatase is that it is a network accessible set of smart memory resident
records. Each record has data composed of a top level PVStructure. Each record has a name which is
the channelName for pvAccess. A local Channel Provider implements the complete ChannelProvider and
Channel interfaces as defined by pvAccess.
This local provider is accessed by the remote pvAccess server.
A record is smart because code can be attached to a record.</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>localChannelProvider</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><b>database</b> does not itself implement pvRecord instances.
Instead it provides a base classes that make it easy to create record instances.
What does have to be implemented is a top
level PVStructure and the following two methods:</p>
<dl>
<dt>process</dt>
<dd>This is what makes a record <b>smart</b>.
What process does is up to the implementation except that it must decide if
it's execution model is synchronous or asynchronous.
Synchronous means that when process returns the processing is complete.
Asynchronous means that when process returns the processing is <b>not</b> complete.
Instead process invokes other threads that will complete the processing at a later time.</dd>
<dt>isSynchronous</dt>
<dd>Which execution model is being implemented.</dd>
</dl>
<h3>Example PVRecord Extension</h3>
<p>Directory <b>example/record</b> has an example PVRecord implementation.
It implements a counter.
The top level structure is:</p>
<pre>
structure
long value
</pre>
<p><b>NOTE:</b> The example compiles but does not build because nothing
is implemented.</p>
<h4>exampleRecord.h</h4>
<p>This is the class description.
The example extends PVRecord.</p>
<pre>
class ExampleRecord :
public virtual PVRecord
{
public:
POINTER_DEFINITIONS(ExampleRecord);
static PVRecordPtr create(epics::pvData::String const &amp; recordName);
virtual ~ExampleRecord();
virtual bool isSynchronous();
virtual void process(
epics::pvDatabase::RecordProcessRequesterPtr const &amp;processRequester);
private:
ExampleRecord(epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure,
epics::pvData::PVLongPtr const &amp;pvValue);
epics::pvData::PVLongPtr pvValue;
};
</pre>
<p>where</p>
<dl>
<dt>create<dt>
<dd>This is example specific. See the implemention for details.</dd>
<dt>~ExampleRecord<dt>
<dd>The destructor must be declared virtual.</dd>
<dt>isSynchronous<dt>
<dd>The implementation must say if process is synchronous or asynchronous.</dd>
<dt>process<dt>
<dd><b>The</b> implementation.</dd>
<dt>ExampleRecord<dt>
<dd>For the example this is private.</dd>
<dl>
<h4>exampleRecord.cpp</h4>
<p>This is the class implementation.</p>
<pre>
ExampleRecord::~ExampleRecord(){}
PVRecordPtr ExampleRecord::create(String const &amp; recordName)
{
String properties;
PVStructurePtr pvStructure = getStandardPVField()-&gt;scalar(pvLong,properties);
PVLongPtr pvValue = pvStructure-&gt;getLongField("value");
PVRecordPtr pvRecord(new ExampleRecord(recordName,pvStructure,pvValue));
return pvRecord;
}
ExampleRecord::ExampleRecord(
String const &amp; recordName,
PVStructurePtr const &amp; pvStructure,
PVLongPtr const &amp;pvValue)
: PVRecord(recordName,pvStructure),
pvValue(pvValue)
{}
bool ExampleRecord::isSynchronous() {return true;}
void ExampleRecord::process(
RecordProcessRequesterPtr const &amp;processRequester)
{
pvValue-&gt;put(pvValue-&gt;get() + 1);
processRequester-&gt;recordProcessResult(Status::Ok);
processRequester-&gt;recordProcessComplete();
}
</pre>
<p>where</p>
<dl>
<dt>create<dt>
<dd>Creates a PVStructure with a single subfield named value.
It gets the interface to the value field.
It then creates an ExampleRecord and returns it.
</dd>
<dt>~ExampleRecord<dt>
<dd>Does not have to do anything because of shared pointers.</dd>
<dt>ExampleRecord<dt>
<dd>Calls the base class constructor and sets pvValue.</dd>
<dt>isSynchronous<dt>
<dd>The example is synchronous.</dd>
<dt>process<dt>
<dd>Gets the curent value, increments it, and puts the new value.
It than calls two processRequester callbacks.</dd>
<dl>
<h4>exampleRecordMain.cpp</h4>
<p>This is a main for creating and running the example.</p>
<pre>
int main(int argc,char *argv[])
{
String recordName("exampleRecord");
PVRecordPtr pvRecord = ExampleRecord::create(recordName);
PVDatabasePtr pvDatabase = PVDatabase::getMaster();
pvDatabase-&gt;addRecord(pvRecord);
cout &lt;&lt; recordName &lt;&lt; "\n";
string str;
while(true) {
cout &lt;&lt; "Type exit to stop: \n";
getline(cin,str);
if(str.compare("exit")==0) break;
}
return 0;
}
</pre>
<p>The main program creates an example record and adds it to the database.
It then runs until the process is stopped by typing <b>exit</b>.
<p>Until the process is stopped,
pvAccess clients can put and get the value field.
For example</p>
<pre>
pvget exampleRecord
pvput exampleRecord 5
</pre>
<p>Will both work.</p>
<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>These two features will be the first phase.
But only synchronous record processing will be supported.</dd>
</dl>
<p>Future phases of pvDatabaseCPP should include:</p>
<dl>
<dt>Install</dt>
<dd>This provides on-line add and delete.</dd>
<dt>Field support</dt>
<dd>Add ability to optionally add support to fields.
In addition some of the basic support defined in pvIOCJava will 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>local ChannelProvider</dt>
<dd>This is the pvAccess package in pvIOCJava.
The localChannelProvider will access data from PVRecords.
It will implement all channel methods except channelRPC.</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, provide the following:
<dl>
<dt>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 provide a first attempt to describe the classes required for the first
phase.</p>
<p>The last section gives a brief overview of the features provided by pvIOCJava.</p>
<h2>database</h2>
<p>The classes in <b>pvDatabase.h</b> implement a database of memory resident
smart records. The next subsection has the definitions for all the classes
defined in this header file.
It describes the following classes:</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 <b>wrap</b> PVField and PVStructure so that pvCopy and monitor can be implemented.</dd>
<dt>PVListener</dt>
<dd>This is implemented by anything that wants to trap calls to the PVRecord::message.</dd>
<dt>RecordProcessRequester</dt>
<dd>This is implemented by anything that calls PVRecord::queueProcessRequest.</dd>
<dt>PVRecordClient</dt>
<dd>This is called by anything that acceses PVRecord.</dd>
<dt>PVDatabase</dt>
<dd>This is a database of PVRecords.</dd>
</dl>
<p>Each class is described in a separate subsection.</p>
<h3>class PVRecord</h3>
<pre>
class PVRecord
{
public:
POINTER_DEFINITIONS(PVRecord);
PVRecord(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
virtual ~PVRecord();
virtual void process(
RecordProcessRequesterPtr const &amp;recordProcessRequester) = 0;
virtual bool isSynchronous() = 0;
epics::pvData::String getRecordName();
PVRecordStructurePtr getPVRecordStructure();
PVRecordFieldPtr findPVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField);
void lock();
void unlock();
void registerClient(PVRecordClientPtr const &amp; pvRecordClient);
void unregisterClient(PVRecordClientPtr const &amp; pvRecordClient);
void detachClients();
void beginGroupPut();
void endGroupPut();
void registerListener(PVListenerPtr const &amp; pvListener);
void unregisterListener(PVListenerPtr const &amp; pvListener);
void removeEveryListener();
epics::pvData::Status processRequest();
void queueProcessRequest(
RecordProcessRequesterPtr const &amp;recordProcessRequester);
void addRequester(epics::pvData::RequesterPtr const &amp; requester);
void removeRequester(epics::pvData::RequesterPtr const &amp; requester);
void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
epics::pvData::String toString();
epics::pvData::String toString(int indentLevel);
};
</pre>
<p>The methods are:</h3>
<dl>
<dt>PVRecord</dt>
<dd>The constructor. It requires a recordName and a top level PVStructure.</dd>
<dt>~PVRecord</dt>
<dd>The desctructor which must be virtual. A derived class must also have
a virtual destructor.</dd>
<dt>process</dt>
<dd>Pure virtual method. Derived classes must implement this method.</dd>
<dt>isSynchronous</dt>
<dd>Pure virtual method. Derived classes must implement this 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>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>registerClient</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>unregisterClient</dt>
<dd>Client is no longer accessing the record.</dd>
<dt>detachClients</dt>
<dd>Ask all clients to detach from the record</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>registerListener</dt>
<dd>Register a PVListener. This must be called before calling pvRecordField.addListener.</dd>
<dt>unregisterListener</dt>
<dd>Unregister a listener. The listener will also be removed from all fields to which it is attached.</dd>
<dt>removeEveryListener</dt>
<dd>This must be called by any code that is deleting or changing the structure of a record.</dd>
<dt>processRequest</dt>
<dd>This is a convenience method for clients that are willing to block if
process is asynchronous. It implements RecordProcessRequester.
If process is synchronous it just calls process and returns the result
to the caller. If process is asynchronous it calls queueProcessRequest,
and process and waits for completion and then returns the result to the caller.</dd>
<dt>queueProcessRequest</dt>
<dd>Queue a process request.</dd>
<dt>addRequester</dt>
<dd>Add a requester to receive messages.</dd>
<dt>removeRequester</dt>
<dd>Remove a message requester.</dd>
<dt>message</dt>
<dd>Can be called by implementation code.
The message will be sent to every requester.</dd>
</dl>
<h3>class PVRecordField</h3>
<pre>
class PVRecordField {
public:
POINTER_DEFINITIONS(PVRecordField);
PVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField,
PVRecordPtr const &amp; pvRecord);
virtual ~PVRecordField();
PVRecordStructurePtr getParent();
epics::pvData::PVFieldPtr getPVField();
epics::pvData::String getFullFieldName();
epics::pvData::String getFullName();
PVRecordPtr getPVRecord();
bool addListener(PVListenerPtr const &amp; pvListener);
void removeListener(PVListenerPtr const &amp; pvListener);
void postPut();
virtual void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
};
</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>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 &amp; pvStructure,
PVRecordFieldPtrArrayPtr const &amp; pvRecordField);
virtual ~PVRecordStructure();
PVRecordFieldPtrArrayPtr getPVRecordFields();
epics::pvData::PVStructurePtr getPVStructure();
virtual void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
};
</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>message</dt>
<dd>Called by implementation code. It calls PVRecord::message after prepending the full
fieldname.</dd>
</dl>
<h3>class PVListener</h3>
<pre>
class PVListener {
public:
POINTER_DEFINITIONS(PVListener);
virtual ~PVListener();
virtual void dataPut(PVRecordFieldPtr const &amp; pvRecordField) = 0;
virtual void dataPut(
PVRecordStructurePtr const &amp;
requested,PVRecordFieldPtr const &amp; pvRecordField) = 0;
virtual void beginGroupPut(PVRecordPtr const &amp; pvRecord) = 0;
virtual void endGroupPut(PVRecordPtr const &amp; pvRecord) = 0;
virtual void unlisten(PVRecordPtr const &amp; pvRecord) = 0;
};
</pre>
<p>where</p>
<dl>
<dt>~PVListener</dt>
<dd>The destructor.</dd>
<dt>dataPut(PVRecordFieldPtr const &amp; pvRecordField)</dt>
<dd>pvField has been modified.
This is called if the listener has called PVRecordField::addListener for pvRecordField.</dd>
<dt>dataPut(
PVRecordStructurePtr const &amp;
requested,PVRecordFieldPtr const &amp; pvRecordField)</dt>
<dd>pvField has been modified.
Requested is the field to which the requester issued a pvField-&amp;addListener.
This is called if the listener has called PVRecordField-&amp;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>
<dt>unlisten</dt>
<dd>The PVLister is being removed from the record.
This is called when the record is being destroyed or when the record structure
(not the data values) is being changed.</dd>
</dl>
<h3>class RecordProcessRequester</h3>
<pre>
class RecordProcessRequester :
virtual public epics::pvData::Requester
{
public:
POINTER_DEFINITIONS(RecordProcessRequester);
virtual ~RecordProcessRequester();
virtual void becomeProcessor() = 0;
virtual void recordProcessResult(epics::pvData::Status status) = 0;
virtual void recordProcessComplete() = 0;
};
</pre>
<p>where</p>
<dl>
<dt>~RecordProcessRequester</dt>
<dd>The destructor.</dd>
<dt>becomeProcessor</dt>
<dd>Called as a result of queueRequeProcessst. The requester can the call process.</dd>
<dt>recordProcessResult</dt>
<dd>The results of record processing.
This is called with the record locked so that the process requester
can access data from the record.</dd>
<dt>recordProcessComplete</dt>
<dd>Processing is complete.
This is called with the record unlocked.
If the process requester called process with leaveActive true then the requester
must call setInactive.</dd>
</dl>
<h3>class PVRecordClient</h3>
<pre>
class PVRecordClient {
POINTER_DEFINITIONS(PVRecordClient);
virtual ~PVRecordClient();
virtual void detach(PVRecordPtr const &amp; 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>
<h3>class PVDatabase</h3>
<pre>
class PVDatabase : virtual public epics::pvData::Requester {
public:
POINTER_DEFINITIONS(PVDatabase);
static PVDatabasePtr getMaster();
virtual ~PVDatabase();
PVRecordPtr findRecord(epics::pvData::String const&amp; recordName);
bool addRecord(PVRecordPtr const &amp; record);
bool removeRecord(PVRecordPtr const &amp; record);
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>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>
</dl>
<h2>Local Channel Provider</h2>
<p>Not yet described.</p>
<p>A brief description is that it must implement the following components of pvIOCJava:</p>
<dl>
<dt>pvCopy</dt>
<dt>monitor</dt>
<dt>pvAccess</dt>
<dd>See the next section for a description</dd>
</dl>
<h2>Summary of Packages in pvIOCJAVA</h2>
<p>The following are the direct sub packages of <b>pvIOCJava/src/org/epics/pvioc</b>:</p>
<dl>
<dt>pvCopy</dt>
<dd>This provides a copy of an arbitrary subset of the fields in a PVRecord.
It also provides the ability to detect and report changes to fields.
It is required for pvAccess.</dd>
<dt>monitor</dt>
<dd>This provides the ability to monitor changes to a PVRecord. It is required for pvAccess monitors.</dd>
<dt>pvAccess</dt>
<dd>The local implementation of Channel Provider and Channel.
It is accessed by the remote pvAccess server and can also be accessed by code in the same IOC.</dd>
<dt>database</dt>
<dd>This defines and implements PVRecord, PVDatabase , and PVListener.
It supports the basic feature required the implement a local Channel Provider.</dd>
<dt>support</dt>
<dd>This provides the ability to optionally attach code to any field of a pvRecord.
It and several sub packages provide a set of standard support modules.</dd>
<dt>install</dt>
<dd>This provides the ability to dynamically initialize and add new PVRecords. It also provides
the ability to dynamicall delete PVRecords.</d>
<dt>xml</dt>
<dd>This provides the ability to configure record instances without writing code.</dd>
<dt>util</dt>
<dd>This is misnamed since it is code related to scanning.</dd>
<dt>pdrv</dt>
<dd>This is portDriver, which is a proposed sucessor to the asynManager component of asynDriver.</dd>
<dt>swtshell</dt>
<dd>This is shell that is can either run under the same process as a JavaIOC or as a remote shell.
It is like a version of probe but for pvData/pvAccess.
Almost all of it's features work in either local or remote mode.
With a little more work all or it's features could work remotely.
This should be done and then only remote mode should be supported.
It can then be rewritten in a completely different language and using a complely different GUI
framework.
</dd>
<dt>caV3</dt>
<dd>This has two components:
<dl>
<dt>ClientFactory</dt>
<dd>This is a small wrapper on top of the caV3 client support implemented by pvAccess.
It allows code in the pvIOC to access V3Records via pvAccess.</dd>
<dt>ServerFactory</dt>
<dd>This is a caV3 server that allows a caV3 client to access a PVRecord.
The Java implementation uses CAJ, which does most of the work.
For now it will not be discussed in this document.</dd>
</dl>
</dd>
<dt>v3a</dt>
<dd>I do not know what this is.</dd>
</dl>
<p>In addition there is one class file <b>JavaIOC.java</b>.
This is starting a IOC instance.
This is not required for pvIOCCPP which is either a main or runs as part of a V3 IOC.</p>
</div>
</body>
</html>

View File

@ -41,7 +41,8 @@ PVRecordPtr ExampleRecord::create(String const & recordName)
pvRecord->init();
return pvRecord;
}
ExampleRecord::ExampleRecord(
ExampleRecord::ExampleRecord(
String const & recordName,
PVStructurePtr const & pvStructure,
PVLongPtr const &pvValue)

View File

@ -11,8 +11,8 @@ SRC_DIRS += $(DATABASE)/database
INC += pvDatabase.h
#INC += pvCopy.h
#INC += monitor.h
LIBSRCS += pvDatabase.cpp
#LIBSRCS += pvRecord.cpp
#LIBSRCS += pvDatabase.cpp
LIBSRCS += pvRecord.cpp
#LIBSRCS += recordProcess.cpp
#LIBSRCS += pvCopy.cpp
#LIBSRCS += monitor.cpp

View File

@ -17,426 +17,6 @@ using namespace epics::pvAccess;
namespace epics { namespace pvDatabase {
PVRecord::PVRecord(
String const & recordName,
PVStructurePtr const & pvStructure)
: recordName(recordName),
pvStructure(pvStructure),
convert(getConvert()),
thelock(mutex),
depthGroupPut(0)
{
thelock.unlock();
}
void PVRecord::init()
{
PVRecordStructurePtr parent;
pvRecordStructure = PVRecordStructurePtr(
new PVRecordStructure(pvStructure,parent,getPtrSelf()));
pvRecordStructure->init();
pvStructure->setRequester(getPtrSelf());
}
PVRecord::~PVRecord() {}
String PVRecord::getRecordName() {return recordName;}
PVRecordStructurePtr PVRecord::getPVRecordStructure() {return pvRecordStructure;}
PVRecordFieldPtr PVRecord::findPVRecordField(PVFieldPtr const & pvField)
{
return findPVRecordField(pvRecordStructure,pvField);
}
PVRecordFieldPtr PVRecord::findPVRecordField(
PVRecordStructurePtr const & pvrs,
PVFieldPtr const & pvField)
{
size_t desiredOffset = pvField->getFieldOffset();
PVFieldPtr pvf = pvrs->getPVField();
size_t offset = pvf->getFieldOffset();
if(offset==desiredOffset) return pvrs;
PVRecordFieldPtrArrayPtr pvrfpap = pvrs->getPVRecordFields();
PVRecordFieldPtrArray::iterator iter;
for (iter = pvrfpap.get()->begin(); iter!=pvrfpap.get()->end(); iter++ ) {
PVRecordFieldPtr pvrf = *iter;
pvf = pvrf->getPVField();
offset = pvf->getFieldOffset();
if(offset==desiredOffset) return pvrf;
size_t nextOffset = pvf->getNextFieldOffset();
if(nextOffset<=desiredOffset) continue;
return findPVRecordField(
static_pointer_cast<PVRecordStructure>(pvrf),
pvField);
}
throw std::logic_error(
recordName + " pvField "
+ pvField->getFieldName() + " not in PVRecord");
}
void PVRecord::lock() {thelock.lock();}
void PVRecord::unlock() {thelock.unlock();}
bool PVRecord::tryLock() {return thelock.tryLock();}
bool PVRecord::addPVRecordClient(PVRecordClientPtr const & pvRecordClient)
{
lock();
std::list<PVRecordClientPtr>::iterator iter;
for (iter = pvRecordClientList.begin();
iter!=pvRecordClientList.end();
iter++ )
{
if((*iter).get()==pvRecordClient.get()) {
unlock();
return false;
}
}
pvRecordClientList.push_back(pvRecordClient);
unlock();
return true;
}
bool PVRecord::removePVRecordClient(PVRecordClientPtr const & pvRecordClient)
{
lock();
std::list<PVRecordClientPtr>::iterator iter;
for (iter = pvRecordClientList.begin();
iter!=pvRecordClientList.end();
iter++ )
{
if((*iter).get()==pvRecordClient.get()) {
pvRecordClientList.erase(iter);
unlock();
return true;
}
}
unlock();
return false;
}
void PVRecord::detachClients()
{
lock();
std::list<PVRecordClientPtr>::iterator iter;
for (iter = pvRecordClientList.begin();
iter!=pvRecordClientList.end();
iter++ )
{
(*iter)->detach(getPtrSelf());
}
pvRecordClientList.clear();
unlock();
}
void PVRecord::beginGroupPut()
{
if(++depthGroupPut>1) return;
std::list<PVListenerPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++)
{
(*iter).get()->beginGroupPut(getPtrSelf());
}
}
void PVRecord::endGroupPut()
{
if(--depthGroupPut>0) return;
std::list<PVListenerPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++)
{
(*iter).get()->endGroupPut(getPtrSelf());
}
}
bool PVRecord::addListener(PVListenerPtr const & pvListener)
{
lock();
std::list<PVListenerPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ )
{
if((*iter).get()==pvListener.get()) {
unlock();
return false;
}
}
pvListenerList.push_back(pvListener);
unlock();
return true;
}
bool PVRecord::removeListener(PVListenerPtr const & pvListener)
{
lock();
std::list<PVListenerPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ )
{
if((*iter).get()==pvListener.get()) {
pvListenerList.erase(iter);
unlock();
return true;
}
}
unlock();
return false;
}
void PVRecord::queueProcessRequest(
RecordProcessRequesterPtr const &recordProcessRequester)
{
bool isFirst = false;
lock();
processRequesterQueue.push_back(recordProcessRequester);
if(processRequesterQueue.size()==1) isFirst = true;
unlock();
if(isFirst) recordProcessRequester->becomeProcessor();
}
void PVRecord::dequeueProcessRequest(
RecordProcessRequesterPtr const &recordProcessRequester)
{
RecordProcessRequesterPtr next;
lock();
RecordProcessRequesterPtr processRequester =
processRequesterQueue[0];
if(processRequester.get()!=recordProcessRequester.get()) {
message(
"PVRecord::dequeueProcessRequest illegal requester",
errorMessage);
return;
}
processRequesterQueue.pop_front();
if(processRequesterQueue.size()>0) next = processRequesterQueue[0];
unlock();
if(next.get()!=NULL) next->becomeProcessor();
}
bool PVRecord::addRequester(epics::pvData::RequesterPtr const & requester)
{
lock();
std::list<RequesterPtr>::iterator iter;
for (iter = requesterList.begin(); iter!=requesterList.end(); iter++ ) {
if((*iter).get()==requester.get()) {
unlock();
return false;
}
}
requesterList.push_back(requester);
unlock();
return true;
}
bool PVRecord::removeRequester(epics::pvData::RequesterPtr const & requester)
{
lock();
std::list<RequesterPtr>::iterator iter;
for (iter = requesterList.begin(); iter!=requesterList.end(); iter++ ) {
if((*iter).get()==requester.get()) {
requesterList.erase(iter);
unlock();
return true;
}
}
unlock();
return false;
}
void PVRecord::message(String const & message,MessageType messageType)
{
if(requesterList.size()==0 ) {
String xxx(getMessageTypeName(messageType) + " " + message);
printf("%s\n",xxx.c_str());
return;
}
std::list<epics::pvData::RequesterPtr>::iterator iter;
for(iter = requesterList.begin(); iter != requesterList.end(); ++iter) {
(*iter)->message(message,messageType);
}
}
void PVRecord::message(
PVRecordFieldPtr const & pvRecordField,
String const & message,
MessageType messageType)
{
this->message(pvRecordField->getFullName() + " " + message,messageType);
}
void PVRecord::toString(StringBuilder buf)
{
toString(buf,0);
}
void PVRecord::toString(StringBuilder buf,int indentLevel)
{
*buf += "\nrecord " + recordName + " ";
pvRecordStructure->getPVStructure()->toString(buf, indentLevel);
}
PVRecordField::PVRecordField(
PVFieldPtr const & pvField,
PVRecordStructurePtr const &parent,
PVRecordPtr const & pvRecord)
: pvField(pvField),
parent(parent),
pvRecord(pvRecord)
{
}
void PVRecordField::init()
{
fullFieldName = pvField->getFieldName();
PVRecordStructurePtr pvParent = parent;
while(pvParent.get()!= NULL) {
String parentName = pvParent->getPVField()->getFieldName();
if(parentName.size()>0) {
fullFieldName = pvParent->getPVField()->getFieldName()
+ '.' + fullFieldName;
}
pvParent = pvParent->getParent();
}
if(fullFieldName.size()>0) {
fullName = pvRecord->getRecordName() + '.' + fullFieldName;
} else {
fullName = pvRecord->getRecordName();
}
pvField->setPostHandler(getPtrSelf());
}
PVRecordField::~PVRecordField() {}
PVRecordStructurePtr PVRecordField::getParent() {return parent;}
PVFieldPtr PVRecordField::getPVField() {return pvField;}
String PVRecordField::getFullFieldName() {return fullFieldName; }
String PVRecordField::getFullName() {return fullName; }
PVRecordPtr PVRecordField::getPVRecord() {return pvRecord;}
bool PVRecordField::addListener(PVListenerPtr const & pvListener)
{
std::list<PVListenerPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ ) {
if((*iter).get()==pvListener.get()) {
return false;
}
}
pvListenerList.push_back(pvListener);
return true;
}
void PVRecordField::removeListener(PVListenerPtr const & pvListener)
{
std::list<PVListenerPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ ) {
if((*iter).get()==pvListener.get()) {
pvListenerList.erase(iter);
return;
}
}
}
void PVRecordField::postPut()
{
callListener();
std::list<PVListenerPtr>::iterator iter;
PVRecordStructurePtr pvParent = getParent();
while(pvParent.get()!=NULL) {
std::list<PVListenerPtr> list = pvParent->pvListenerList;
for (iter = list.begin(); iter!=list.end(); iter++ ) {
(*iter)->dataPut(pvParent,getPtrSelf());
}
pvParent = pvParent->getParent();
}
}
void PVRecordField::message(String const & message,MessageType messageType)
{
pvRecord->message(getPtrSelf(),message,messageType);
}
void PVRecordField::callListener()
{
std::list<PVListenerPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ ) {
(*iter)->dataPut(getPtrSelf());
}
}
PVRecordStructure::PVRecordStructure(
PVStructurePtr const &pvStructure,
PVRecordStructurePtr const &parent,
PVRecordPtr const & pvRecord)
:
PVRecordField(pvStructure,parent,pvRecord),
pvStructure(pvStructure),
pvRecordFields(new PVRecordFieldPtrArray)
{
}
PVRecordStructure::~PVRecordStructure() {}
void PVRecordStructure::init()
{
PVRecordField::init();
const PVFieldPtrArray & pvFields = pvStructure->getPVFields();
size_t numFields = pvFields.size();
pvRecordFields->reserve( numFields);
PVRecordStructurePtr self =
static_pointer_cast<PVRecordStructure>(getPtrSelf());
PVRecordPtr pvRecord = getPVRecord();
for(size_t i=0; i<numFields; i++) {
PVFieldPtr pvField = pvFields[i];
if(pvField->getField()->getType()==structure) {
PVStructurePtr xxx = static_pointer_cast<PVStructure>(pvField);
PVRecordStructurePtr pvRecordStructure(
new PVRecordStructure(xxx,self,pvRecord));
pvRecordFields->push_back(pvRecordStructure);
pvRecordStructure->init();
} else {
PVRecordFieldPtr pvRecordField(
new PVRecordField(pvField,self,pvRecord));
pvRecordFields->push_back(pvRecordField);
pvRecordField->init();
}
}
}
PVRecordFieldPtrArrayPtr PVRecordStructure::getPVRecordFields()
{
return pvRecordFields;
}
PVStructurePtr PVRecordStructure::getPVStructure() {return pvStructure;}
void PVRecordStructure::removeListener(PVListenerPtr const & pvListener)
{
PVRecordField::removeListener(pvListener);
size_t numFields = pvRecordFields->size();
for(size_t i=0; i<numFields; i++) {
PVRecordFieldPtr pvRecordField = (*pvRecordFields.get())[i];
pvRecordField->removeListener(pvListener);
}
}
void PVRecordStructure::postPut()
{
PVRecordField::postPut();
size_t numFields = pvRecordFields->size();
for(size_t i=0; i<numFields; i++) {
PVRecordFieldPtr pvRecordField = (*pvRecordFields.get())[i];
pvRecordField->callListener();
}
}
PVDatabase::PVDatabase()
{
}
PVDatabase::~PVDatabase() {}
PVDatabasePtr PVDatabase::getMaster()

View File

@ -30,14 +30,17 @@ 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 PVRecordClient;
typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
class RecordPutRequester;
typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;
class PVDatabase;
typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
@ -57,26 +60,35 @@ public:
RecordProcessRequesterPtr const &recordProcessRequester,
bool alreadyLocked) = 0;
virtual bool isSynchronous() = 0;
virtual bool requestImmediatePut(
epics::pvData::PVFieldPtr const &pvField);
virtual void immediatePutDone();
virtual void destroy();
epics::pvData::String getRecordName();
PVRecordStructurePtr getPVRecordStructure();
PVRecordFieldPtr findPVRecordField(
epics::pvData::PVFieldPtr const & pvField);
bool addRequester(epics::pvData::RequesterPtr const & requester);
bool removeRequester(epics::pvData::RequesterPtr const & requester);
void lock();
void unlock();
bool tryLock();
void lockOtherRecord(PVRecordPtr const & otherRecord);
bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
void detachClients();
void beginGroupPut();
void endGroupPut();
bool addListener(PVListenerPtr const & pvListener);
bool removeListener(PVListenerPtr const & pvListener);
void beginGroupPut();
void endGroupPut();
void queueProcessRequest(
RecordProcessRequesterPtr const &recordProcessRequester);
void dequeueProcessRequest(
RecordProcessRequesterPtr const &recordProcessRequester);
bool addRequester(epics::pvData::RequesterPtr const & requester);
bool removeRequester(epics::pvData::RequesterPtr const & requester);
void queuePutRequest(
RecordPutRequesterPtr const &recordPutRequester);
void putDone(
RecordPutRequesterPtr const &recordPutRequester);
virtual epics::pvData::String getRequesterName() {return getRecordName();}
virtual void message(
epics::pvData::String const & message,
@ -103,12 +115,16 @@ private:
epics::pvData::ConvertPtr convert;
PVRecordStructurePtr pvRecordStructure;
std::deque<RecordProcessRequesterPtr> processRequesterQueue;
std::deque<RecordPutRequesterPtr> putRequesterQueue;
std::list<PVListenerPtr> pvListenerList;
std::list<PVRecordClientPtr> pvRecordClientList;
std::list<epics::pvData::RequesterPtr> requesterList;
epics::pvData::Mutex mutex;
epics::pvData::Lock thelock;
int depthGroupPut;
bool processActive;
bool putActive;
bool isDestroyed;
};
class PVRecordField :
@ -172,7 +188,16 @@ private:
friend class PVRecord;
};
class PVListener {
class PVRecordClient {
public:
POINTER_DEFINITIONS(PVRecordClient);
virtual ~PVRecordClient() {}
virtual void detach(PVRecordPtr const & pvRecord) = 0;
};
class PVListener :
virtual public PVRecordClient
{
public:
POINTER_DEFINITIONS(PVListener);
virtual ~PVListener() {}
@ -182,25 +207,27 @@ public:
PVRecordFieldPtr const & pvRecordField) = 0;
virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
virtual void unlisten(PVRecordPtr const & pvRecord) = 0;
};
class RecordProcessRequester :
virtual public PVRecordClient,
virtual public epics::pvData::Requester
{
public:
POINTER_DEFINITIONS(RecordProcessRequester);
virtual ~RecordProcessRequester() {}
virtual void recordDestroyed() = 0;
virtual void becomeProcessor() = 0;
virtual void recordProcessResult(epics::pvData::Status status) = 0;
virtual void recordProcessComplete() = 0;
};
class PVRecordClient {
class RecordPutRequester
{
public:
POINTER_DEFINITIONS(PVRecordClient);
virtual ~PVRecordClient() {}
virtual void detach(PVRecordPtr const & pvRecord) = 0;
POINTER_DEFINITIONS(RecordPutRequester);
virtual ~RecordPutRequester() {}
virtual void requestResult(bool result) = 0;
};
class PVDatabase : virtual public epics::pvData::Requester {

634
src/database/pvRecord.cpp Normal file
View File

@ -0,0 +1,634 @@
/* pvRecord.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
* @data 2012.11.21
*/
#include <pv/pvDatabase.h>
using std::tr1::static_pointer_cast;
using namespace epics::pvData;
using namespace epics::pvAccess;
namespace epics { namespace pvDatabase {
PVRecord::PVRecord(
String const & recordName,
PVStructurePtr const & pvStructure)
: recordName(recordName),
pvStructure(pvStructure),
convert(getConvert()),
thelock(mutex),
depthGroupPut(0),
processActive(false),
putActive(false),
isDestroyed(false)
{
thelock.unlock();
}
void PVRecord::init()
{
PVRecordStructurePtr parent;
pvRecordStructure = PVRecordStructurePtr(
new PVRecordStructure(pvStructure,parent,getPtrSelf()));
pvRecordStructure->init();
pvStructure->setRequester(getPtrSelf());
}
PVRecord::~PVRecord() {}
bool PVRecord::requestImmediatePut(PVFieldPtr const &pvField)
{
return false;
}
void PVRecord::immediatePutDone() {}
void PVRecord::destroy()
{
lock();
isDestroyed = true;
std::list<RequesterPtr>::iterator requesterIter;
for (
requesterIter = requesterList.begin();
requesterIter!=requesterList.end();
requesterIter++ ) {
(*requesterIter)->message("record destroyed",fatalErrorMessage);
}
requesterList.clear();
std::list<PVRecordClientPtr>::iterator clientIter;
for (clientIter = pvRecordClientList.begin();
clientIter!=pvRecordClientList.end();
clientIter++ )
{
(*clientIter)->detach(getPtrSelf());
}
pvRecordClientList.clear();
pvListenerList.clear();
std::deque<RecordProcessRequesterPtr>::iterator processRequesterIter;
for (
processRequesterIter = processRequesterQueue.begin();
processRequesterIter != processRequesterQueue.end();
processRequesterIter++ )
{
(*processRequesterIter)->recordDestroyed();
}
processRequesterQueue.clear();
std::deque<RecordPutRequesterPtr>::iterator putRequesterIter;
for (
putRequesterIter = putRequesterQueue.begin();
putRequesterIter != putRequesterQueue.end();
putRequesterIter++ )
{
(*putRequesterIter)->requestResult(false);
}
putRequesterQueue.clear();
unlock();
}
String PVRecord::getRecordName() {return recordName;}
PVRecordStructurePtr PVRecord::getPVRecordStructure() {return pvRecordStructure;}
PVRecordFieldPtr PVRecord::findPVRecordField(PVFieldPtr const & pvField)
{
return findPVRecordField(pvRecordStructure,pvField);
}
bool PVRecord::addRequester(epics::pvData::RequesterPtr const & requester)
{
lock();
if(isDestroyed) {
unlock();
return false;
}
std::list<RequesterPtr>::iterator iter;
for (iter = requesterList.begin(); iter!=requesterList.end(); iter++ ) {
if((*iter).get()==requester.get()) {
unlock();
return false;
}
}
requesterList.push_back(requester);
unlock();
return true;
}
bool PVRecord::removeRequester(epics::pvData::RequesterPtr const & requester)
{
lock();
if(isDestroyed) {
unlock();
return false;
}
std::list<RequesterPtr>::iterator iter;
for (iter = requesterList.begin(); iter!=requesterList.end(); iter++ ) {
if((*iter).get()==requester.get()) {
requesterList.erase(iter);
unlock();
return true;
}
}
unlock();
return false;
}
PVRecordFieldPtr PVRecord::findPVRecordField(
PVRecordStructurePtr const & pvrs,
PVFieldPtr const & pvField)
{
size_t desiredOffset = pvField->getFieldOffset();
PVFieldPtr pvf = pvrs->getPVField();
size_t offset = pvf->getFieldOffset();
if(offset==desiredOffset) return pvrs;
PVRecordFieldPtrArrayPtr pvrfpap = pvrs->getPVRecordFields();
PVRecordFieldPtrArray::iterator iter;
for (iter = pvrfpap.get()->begin(); iter!=pvrfpap.get()->end(); iter++ ) {
PVRecordFieldPtr pvrf = *iter;
pvf = pvrf->getPVField();
offset = pvf->getFieldOffset();
if(offset==desiredOffset) return pvrf;
size_t nextOffset = pvf->getNextFieldOffset();
if(nextOffset<=desiredOffset) continue;
return findPVRecordField(
static_pointer_cast<PVRecordStructure>(pvrf),
pvField);
}
throw std::logic_error(
recordName + " pvField "
+ pvField->getFieldName() + " not in PVRecord");
}
void PVRecord::lock() {thelock.lock();}
void PVRecord::unlock() {thelock.unlock();}
bool PVRecord::tryLock() {return thelock.tryLock();}
void PVRecord::lockOtherRecord(PVRecordPtr const & otherRecord)
{
if(this<otherRecord.get()) {
otherRecord->lock();
return;
}
unlock();
otherRecord->lock();
lock();
}
bool PVRecord::addPVRecordClient(PVRecordClientPtr const & pvRecordClient)
{
lock();
if(isDestroyed) {
unlock();
return false;
}
std::list<PVRecordClientPtr>::iterator iter;
for (iter = pvRecordClientList.begin();
iter!=pvRecordClientList.end();
iter++ )
{
if((*iter).get()==pvRecordClient.get()) {
unlock();
return false;
}
}
pvRecordClientList.push_back(pvRecordClient);
unlock();
return true;
}
bool PVRecord::removePVRecordClient(PVRecordClientPtr const & pvRecordClient)
{
lock();
std::list<PVRecordClientPtr>::iterator iter;
for (iter = pvRecordClientList.begin();
iter!=pvRecordClientList.end();
iter++ )
{
if((*iter).get()==pvRecordClient.get()) {
pvRecordClientList.erase(iter);
unlock();
return true;
}
}
unlock();
return false;
}
void PVRecord::detachClients()
{
lock();
if(isDestroyed) {
unlock();
return;
}
std::list<PVRecordClientPtr>::iterator iter;
for (iter = pvRecordClientList.begin();
iter!=pvRecordClientList.end();
iter++ )
{
(*iter)->detach(getPtrSelf());
}
pvRecordClientList.clear();
unlock();
}
bool PVRecord::addListener(PVListenerPtr const & pvListener)
{
lock();
if(isDestroyed) {
unlock();
return false;
}
std::list<PVListenerPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ )
{
if((*iter).get()==pvListener.get()) {
unlock();
return false;
}
}
pvListenerList.push_back(pvListener);
unlock();
return true;
}
bool PVRecord::removeListener(PVListenerPtr const & pvListener)
{
lock();
if(isDestroyed) {
unlock();
return false;
}
std::list<PVListenerPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ )
{
if((*iter).get()==pvListener.get()) {
pvListenerList.erase(iter);
pvRecordStructure->removeListener(pvListener);
unlock();
return true;
}
}
unlock();
return false;
}
void PVRecord::beginGroupPut()
{
if(++depthGroupPut>1) return;
std::list<PVListenerPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++)
{
(*iter).get()->beginGroupPut(getPtrSelf());
}
}
void PVRecord::endGroupPut()
{
if(--depthGroupPut>0) return;
std::list<PVListenerPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++)
{
(*iter).get()->endGroupPut(getPtrSelf());
}
}
void PVRecord::queueProcessRequest(
RecordProcessRequesterPtr const &recordProcessRequester)
{
lock();
if(isDestroyed) {
unlock();
recordProcessRequester->recordDestroyed();
return;
}
bool isFirst = false;
processRequesterQueue.push_back(recordProcessRequester);
if(processRequesterQueue.size()==1 && !putActive) isFirst = true;
unlock();
if(isFirst) recordProcessRequester->becomeProcessor();
}
void PVRecord::dequeueProcessRequest(
RecordProcessRequesterPtr const &recordProcessRequester)
{
lock();
if(isDestroyed) {
unlock();
recordProcessRequester->recordDestroyed();
return;
}
RecordProcessRequesterPtr next;
RecordProcessRequesterPtr processRequester =
processRequesterQueue[0];
if(processRequester.get()!=recordProcessRequester.get()) {
message(
"PVRecord::dequeueProcessRequest illegal requester",
errorMessage);
return;
}
processRequesterQueue.pop_front();
if(putRequesterQueue.size() > 0) {
RecordPutRequesterPtr putRequester = putRequesterQueue[0];
processActive = false;
putActive = true;
putRequester->requestResult(true);
return; // WITH LOCK HELD
}
if(processRequesterQueue.size()>0) next = processRequesterQueue[0];
if(next.get()!=NULL) processActive = true;
unlock();
if(next.get()!=NULL) next->becomeProcessor();
}
void PVRecord::queuePutRequest(
RecordPutRequesterPtr const &recordPutRequester)
{
lock();
if(isDestroyed) {
unlock();
recordPutRequester->requestResult(false);
return;
}
if(processRequesterQueue.size()==0 && !processActive) {
putRequesterQueue.push_back(recordPutRequester);
putActive = true;
recordPutRequester->requestResult(true);
return; // WITH lock held
}
putRequesterQueue.push_back(recordPutRequester);
unlock();
}
void PVRecord::putDone(
RecordPutRequesterPtr const &recordPutRequester)
{
// Note that this is called with lock held
RecordPutRequesterPtr putRequester =
putRequesterQueue[0];
if(putRequester.get()!=recordPutRequester.get()) {
message(
"PVRecord::putDone illegal requester",
errorMessage);
return;
}
putRequesterQueue.pop_front();
if(putRequesterQueue.size()>0){
RecordPutRequesterPtr next = putRequesterQueue[0];
next->requestResult(true);
return; // WITH lock held
}
putActive = false;
RecordProcessRequesterPtr next = processRequesterQueue[0];
if(next.get()!=NULL) processActive = true;
unlock();
if(next.get()!=NULL) next->becomeProcessor();
}
void PVRecord::message(String const & message,MessageType messageType)
{
if(isDestroyed) return;
if(requesterList.size()==0 ) {
String xxx(getMessageTypeName(messageType) + " " + message);
printf("%s\n",xxx.c_str());
return;
}
std::list<epics::pvData::RequesterPtr>::iterator iter;
for(iter = requesterList.begin(); iter != requesterList.end(); ++iter) {
(*iter)->message(message,messageType);
}
}
void PVRecord::message(
PVRecordFieldPtr const & pvRecordField,
String const & message,
MessageType messageType)
{
this->message(pvRecordField->getFullName() + " " + message,messageType);
}
void PVRecord::toString(StringBuilder buf)
{
toString(buf,0);
}
void PVRecord::toString(StringBuilder buf,int indentLevel)
{
*buf += "\nrecord " + recordName + " ";
pvRecordStructure->getPVStructure()->toString(buf, indentLevel);
}
PVRecordField::PVRecordField(
PVFieldPtr const & pvField,
PVRecordStructurePtr const &parent,
PVRecordPtr const & pvRecord)
: pvField(pvField),
parent(parent),
pvRecord(pvRecord)
{
}
void PVRecordField::init()
{
fullFieldName = pvField->getFieldName();
PVRecordStructurePtr pvParent = parent;
while(pvParent.get()!= NULL) {
String parentName = pvParent->getPVField()->getFieldName();
if(parentName.size()>0) {
fullFieldName = pvParent->getPVField()->getFieldName()
+ '.' + fullFieldName;
}
pvParent = pvParent->getParent();
}
if(fullFieldName.size()>0) {
fullName = pvRecord->getRecordName() + '.' + fullFieldName;
} else {
fullName = pvRecord->getRecordName();
}
pvField->setPostHandler(getPtrSelf());
}
PVRecordField::~PVRecordField() {}
PVRecordStructurePtr PVRecordField::getParent() {return parent;}
PVFieldPtr PVRecordField::getPVField() {return pvField;}
String PVRecordField::getFullFieldName() {return fullFieldName; }
String PVRecordField::getFullName() {return fullName; }
PVRecordPtr PVRecordField::getPVRecord() {return pvRecord;}
bool PVRecordField::addListener(PVListenerPtr const & pvListener)
{
std::list<PVListenerPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ ) {
if((*iter).get()==pvListener.get()) {
return false;
}
}
pvListenerList.push_back(pvListener);
return true;
}
void PVRecordField::removeListener(PVListenerPtr const & pvListener)
{
std::list<PVListenerPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ ) {
if((*iter).get()==pvListener.get()) {
pvListenerList.erase(iter);
return;
}
}
}
void PVRecordField::postPut()
{
callListener();
std::list<PVListenerPtr>::iterator iter;
PVRecordStructurePtr pvParent = getParent();
while(pvParent.get()!=NULL) {
std::list<PVListenerPtr> list = pvParent->pvListenerList;
for (iter = list.begin(); iter!=list.end(); iter++ ) {
(*iter)->dataPut(pvParent,getPtrSelf());
}
pvParent = pvParent->getParent();
}
}
void PVRecordField::message(String const & message,MessageType messageType)
{
pvRecord->message(getPtrSelf(),message,messageType);
}
void PVRecordField::callListener()
{
std::list<PVListenerPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ ) {
(*iter)->dataPut(getPtrSelf());
}
}
PVRecordStructure::PVRecordStructure(
PVStructurePtr const &pvStructure,
PVRecordStructurePtr const &parent,
PVRecordPtr const & pvRecord)
:
PVRecordField(pvStructure,parent,pvRecord),
pvStructure(pvStructure),
pvRecordFields(new PVRecordFieldPtrArray)
{
}
PVRecordStructure::~PVRecordStructure() {}
void PVRecordStructure::init()
{
PVRecordField::init();
const PVFieldPtrArray & pvFields = pvStructure->getPVFields();
size_t numFields = pvFields.size();
pvRecordFields->reserve( numFields);
PVRecordStructurePtr self =
static_pointer_cast<PVRecordStructure>(getPtrSelf());
PVRecordPtr pvRecord = getPVRecord();
for(size_t i=0; i<numFields; i++) {
PVFieldPtr pvField = pvFields[i];
if(pvField->getField()->getType()==structure) {
PVStructurePtr xxx = static_pointer_cast<PVStructure>(pvField);
PVRecordStructurePtr pvRecordStructure(
new PVRecordStructure(xxx,self,pvRecord));
pvRecordFields->push_back(pvRecordStructure);
pvRecordStructure->init();
} else {
PVRecordFieldPtr pvRecordField(
new PVRecordField(pvField,self,pvRecord));
pvRecordFields->push_back(pvRecordField);
pvRecordField->init();
}
}
}
PVRecordFieldPtrArrayPtr PVRecordStructure::getPVRecordFields()
{
return pvRecordFields;
}
PVStructurePtr PVRecordStructure::getPVStructure() {return pvStructure;}
void PVRecordStructure::removeListener(PVListenerPtr const & pvListener)
{
PVRecordField::removeListener(pvListener);
size_t numFields = pvRecordFields->size();
for(size_t i=0; i<numFields; i++) {
PVRecordFieldPtr pvRecordField = (*pvRecordFields.get())[i];
pvRecordField->removeListener(pvListener);
}
}
void PVRecordStructure::postPut()
{
PVRecordField::postPut();
size_t numFields = pvRecordFields->size();
for(size_t i=0; i<numFields; i++) {
PVRecordFieldPtr pvRecordField = (*pvRecordFields.get())[i];
pvRecordField->callListener();
}
}
PVDatabase::PVDatabase()
{
}
PVDatabase::~PVDatabase() {}
PVDatabasePtr PVDatabase::getMaster()
{
PVDatabasePtr xxx;
return xxx;
}
PVRecordPtr PVDatabase::findRecord(String const& recordName)
{
PVRecordPtr xxx;
return xxx;
}
bool PVDatabase::addRecord(PVRecordPtr const & record)
{
return false;
}
bool PVDatabase::removeRecord(PVRecordPtr const & record)
{
return false;
}
String PVDatabase::getRequesterName()
{
String xxx;
return xxx;
}
void PVDatabase::message(String const & message,MessageType messageType)
{
}
}}

View File

@ -32,13 +32,17 @@ using std::tr1::static_pointer_cast;
TestRecord::~TestRecord(){}
PVRecordPtr TestRecord::create(String const & recordName)
PVRecordPtr TestRecord::create(
String const & recordName)
{
String properties("alarm,timeStamp,display,control,valueAlarm");
PVStructurePtr pvStructure = getStandardPVField()->scalar(pvLong,properties);
PVLongPtr pvValue = pvStructure->getLongField("value");
PVRecordPtr pvRecord(new TestRecord(recordName,pvStructure,pvValue));
TestRecordPtr pvRecord(
new TestRecord(recordName,pvStructure,pvValue));
pvRecord->init();
PVFieldPtr pvField = pvStructure->getSubField("display.description");
pvRecord->immediatePutOK = pvField;
return pvRecord;
}
@ -50,8 +54,6 @@ TestRecord::TestRecord(
pvValue(pvValue)
{}
bool TestRecord::isSynchronous() {return true;}
void TestRecord::process(
RecordProcessRequesterPtr const &processRequester,bool alreadyLocked)
{
@ -63,6 +65,26 @@ void TestRecord::process(
dequeueProcessRequest(processRequester);
}
bool TestRecord::isSynchronous() {return true;}
void TestRecord::destroy()
{
PVRecord::destroy();
pvValue.reset();
immediatePutOK.reset();
}
bool TestRecord::requestImmediatePut(PVFieldPtr const &pvField)
{
if(pvField!=immediatePutOK) return false;
lock();
return true;
}
void TestRecord::immediatePutDone()
{
unlock();
}
}}

View File

@ -19,23 +19,30 @@
namespace epics { namespace pvDatabase {
class TestRecord;
typedef std::tr1::shared_ptr<TestRecord> TestRecordPtr;
class TestRecord :
public virtual PVRecord
{
public:
POINTER_DEFINITIONS(TestRecord);
static PVRecordPtr create(epics::pvData::String const & recordName);
static PVRecordPtr create(
epics::pvData::String const & recordName);
virtual ~TestRecord();
virtual bool isSynchronous();
virtual void process(
epics::pvDatabase::RecordProcessRequesterPtr const &processRequester,
bool alreadyLocked);
virtual bool isSynchronous();
virtual void destroy();
virtual bool requestImmediatePut(epics::pvData::PVFieldPtr const &pvField);
virtual void immediatePutDone();
private:
TestRecord(epics::pvData::String const & recordName,
epics::pvData::PVStructurePtr const & pvStructure,
epics::pvData::PVLongPtr const &pvValue);
epics::pvData::PVLongPtr pvValue;
epics::pvData::PVFieldPtr immediatePutOK;
};
}}

View File

@ -44,6 +44,9 @@ typedef std::tr1::shared_ptr<MyPVListener> MyPVListenerPtr;
class MyProcessRequester;
typedef std::tr1::shared_ptr<MyProcessRequester> MyProcessRequesterPtr;
class MyPutRequester;
typedef std::tr1::shared_ptr<MyPutRequester> MyPutRequesterPtr;
class MyPVListener :
public PVListener,
public std::tr1::enable_shared_from_this<MyPVListener>
@ -61,6 +64,10 @@ public:
return pvPVListener;
}
virtual ~MyPVListener() {}
virtual void detach(PVRecordPtr const & pvRecord)
{
printf("%s MyPVListener::detach\n",requesterName.c_str());
}
virtual void dataPut(PVRecordFieldPtr const & pvRecordField)
{
String fieldName = pvRecordField->getFullFieldName();
@ -107,6 +114,7 @@ private:
pvRecordField(pvRecordField)
{}
void init() {
pvRecord->addPVRecordClient(getPtrSelf());
pvRecord->addListener(getPtrSelf());
pvRecordField->addListener(getPtrSelf());
}
@ -120,7 +128,6 @@ private:
class MyProcessRequester :
public virtual Requester,
public virtual RecordProcessRequester,
public PVRecordClient,
public std::tr1::enable_shared_from_this<MyProcessRequester>
{
public:
@ -160,6 +167,11 @@ public:
message.c_str(),
messageTypeName.c_str());
}
virtual void recordDestroyed()
{
printf("%s MyProcessRequester::recordDestroyed\n",
requesterName.c_str());
}
virtual void becomeProcessor()
{
pvRecord->process(getPtrSelf(),false);
@ -198,6 +210,37 @@ private:
PVRecordPtr pvRecord;
};
class MyPutRequester :
public virtual RecordPutRequester,
public std::tr1::enable_shared_from_this<MyPutRequester>
{
public:
MyPutRequester(PVRecordPtr const & pvRecord)
: pvRecord(pvRecord),
result(false)
{}
virtual ~MyPutRequester() {}
virtual void requestResult(bool result)
{
this->result = result;
event.signal();
}
bool makeRequest()
{
pvRecord->queuePutRequest(getPtrSelf());
event.wait();
return result;
}
private:
MyPutRequesterPtr getPtrSelf()
{
return shared_from_this();
}
Event event;
PVRecordPtr pvRecord;
bool result;
};
void dumpPVRecordField(PVRecordFieldPtr pvRecordField)
@ -253,17 +296,22 @@ int main(int argc,char *argv[])
"listenTop", pvRecord, pvRecord->getPVRecordStructure());
MyPVListenerPtr listenValue = MyPVListener::create(
"listenValue", pvRecord,recordFieldValue);
PVFieldPtr pvValueAlarm = pvStructure->getSubField("valueAlarm");
PVRecordFieldPtr recordFieldValueAlarm =
pvRecord->findPVRecordField(pvValueAlarm);
MyPVListenerPtr listenValueAlarm = MyPVListener::create(
"listenValueAlarm", pvRecord,recordFieldValueAlarm);
PVIntPtr pvHighAlarmSeverity =
pvStructure->getIntField("valueAlarm.highAlarmSeverity");
PVRecordFieldPtr recordFieldHighAlarmSeverity =
pvRecord->findPVRecordField(pvHighAlarmSeverity);
MyPVListenerPtr listenHighAlarmSeverity = MyPVListener::create(
"listenHighAlarmSeverity", pvRecord,recordFieldHighAlarmSeverity);
PVFieldPtr pvDisplay = pvStructure->getSubField("display");
PVRecordFieldPtr recordFieldDisplay =
pvRecord->findPVRecordField(pvDisplay);
MyPVListenerPtr listenDisplay = MyPVListener::create(
"listenDisplay", pvRecord,recordFieldDisplay);
PVStringPtr pvDisplayDescription =
pvStructure->getStringField("display.description");
PVRecordFieldPtr recordFieldDisplayDescription =
pvRecord->findPVRecordField(pvDisplayDescription);
MyPVListenerPtr listenDisplayDescription = MyPVListener::create(
"listenDisplayDescription", pvRecord,recordFieldDisplayDescription);
recordFieldDisplayDescription->message("test message",infoMessage);
MyProcessRequesterPtr process1 =
MyProcessRequester::create("process1",pvRecord);
MyProcessRequesterPtr process2 =
@ -276,8 +324,42 @@ int main(int argc,char *argv[])
builder.clear();
pvValue->toString(&builder);
printf("%s\n",builder.c_str());
pvHighAlarmSeverity->put(3);
recordFieldHighAlarmSeverity->message("test message",infoMessage);
bool requestResult;
requestResult = pvRecord->requestImmediatePut(pvValue);
if(requestResult) {
printf("error requestImmediatePut for pvValue returned true");
pvRecord->immediatePutDone();
}
requestResult = pvRecord->requestImmediatePut(pvDisplayDescription);
if(!requestResult) {
printf("error requestImmediatePut for pvDisplayDescription returned false");
} else {
pvDisplayDescription->put("this is description");
pvRecord->immediatePutDone();
}
MyPutRequesterPtr myPut(new MyPutRequester(pvRecord));
requestResult = myPut->makeRequest();
if(!requestResult) {
printf("error myPut->makeRequest() returned false");
} else {
pvDisplayDescription->put("this is new description");
pvValue->put(1000);
PVIntPtr pvSeverity = pvStructure->getIntField("alarm.severity");
pvSeverity->put(3);
PVIntPtr pvStatus = pvStructure->getIntField("alarm.status");
pvStatus->put(2);
PVStringPtr pvMessage = pvStructure->getStringField("alarm.message");
pvMessage->put("alarmMessage");
pvRecord->putDone(myPut);
}
builder.clear();
pvStructure->toString(&builder);
printf("pvStructure\n%s\n",builder.c_str());
builder.clear();
pvRecord->destroy();
printf("all done\n");
#ifdef XXXXXX
PVDatabasePtr pvDatabase = PVDatabase::getMaster();