still early version
This commit is contained in:
@ -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 &processRequester)
|
||||
RecordProcessRequesterPtr const &processRequester,bool alreadyLocked)
|
||||
{
|
||||
if(!alreadyLocked) lock();
|
||||
pvValue->put(pvValue->get() + 1);
|
||||
processRequester->recordProcessResult(Status::Ok);
|
||||
unlock();
|
||||
processRequester->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<PVRecord> PVRecordPtr;
|
||||
|
||||
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><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<PVRecord>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(PVRecord);
|
||||
@ -385,32 +433,48 @@ public:
|
||||
epics::pvData::PVStructurePtr const & pvStructure);
|
||||
virtual ~PVRecord();
|
||||
virtual void process(
|
||||
RecordProcessRequesterPtr const &recordProcessRequester) = 0;
|
||||
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();
|
||||
void registerClient(PVRecordClientPtr const & pvRecordClient);
|
||||
void unregisterClient(PVRecordClientPtr const & pvRecordClient);
|
||||
bool tryLock();
|
||||
void lockOtherRecord(PVRecordPtr const & otherRecord);
|
||||
void addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
|
||||
void removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
|
||||
void detachClients();
|
||||
void beginGroupPut();
|
||||
void endGroupPut();
|
||||
void registerListener(PVListenerPtr const & pvListener);
|
||||
void unregisterListener(PVListenerPtr const & pvListener);
|
||||
void removeEveryListener();
|
||||
epics::pvData::Status processRequest();
|
||||
void queueProcessRequest(
|
||||
RecordProcessRequesterPtr const &recordProcessRequester);
|
||||
void addRequester(epics::pvData::RequesterPtr const & requester);
|
||||
void removeRequester(epics::pvData::RequesterPtr const & requester);
|
||||
void dequeueProcessRequest(
|
||||
RecordProcessRequesterPtr const &recordProcessRequester);
|
||||
void queuePutRequest(
|
||||
RecordPutRequesterPtr const &recordPutRequester);
|
||||
void putDone(
|
||||
RecordPutRequesterPtr const &recordPutRequester);
|
||||
virtual epics::pvData::String getRequesterName();
|
||||
void message(
|
||||
epics::pvData::String const & message,
|
||||
epics::pvData::MessageType messageType);
|
||||
epics::pvData::String toString();
|
||||
epics::pvData::String toString(int indentLevel);
|
||||
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);
|
||||
//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<PVRecordField>
|
||||
public:
|
||||
POINTER_DEFINITIONS(PVRecordField);
|
||||
PVRecordField(
|
||||
epics::pvData::PVFieldPtr const & pvField,
|
||||
PVRecordStructurePtr const &parent,
|
||||
PVRecordPtr const & pvRecord);
|
||||
virtual ~PVRecordField();
|
||||
PVRecordStructurePtr getParent();
|
||||
@ -482,8 +616,8 @@ public:
|
||||
epics::pvData::String getFullName();
|
||||
PVRecordPtr getPVRecord();
|
||||
bool addListener(PVListenerPtr const & pvListener);
|
||||
void removeListener(PVListenerPtr const & pvListener);
|
||||
void postPut();
|
||||
virtual void removeListener(PVListenerPtr const & pvListener);
|
||||
virtual void postPut();
|
||||
virtual void message(
|
||||
epics::pvData::String const & 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 & message,
|
||||
epics::pvData::MessageType messageType);
|
||||
virtual void removeListener(PVListenerPtr const & 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 & 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 & pvRecordField) = 0;
|
||||
virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
|
||||
virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
|
||||
virtual void unlisten(PVRecordPtr const & 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 & 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>
|
||||
|
733
documentation/pvDatabaseCPP_20121127.html
Normal file
733
documentation/pvDatabaseCPP_20121127.html
Normal 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 & recordName);
|
||||
virtual ~ExampleRecord();
|
||||
virtual bool isSynchronous();
|
||||
virtual void process(
|
||||
epics::pvDatabase::RecordProcessRequesterPtr const &processRequester);
|
||||
private:
|
||||
ExampleRecord(epics::pvData::String const & recordName,
|
||||
epics::pvData::PVStructurePtr const & pvStructure,
|
||||
epics::pvData::PVLongPtr const &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 & recordName)
|
||||
{
|
||||
String properties;
|
||||
PVStructurePtr pvStructure = getStandardPVField()->scalar(pvLong,properties);
|
||||
PVLongPtr pvValue = pvStructure->getLongField("value");
|
||||
PVRecordPtr pvRecord(new ExampleRecord(recordName,pvStructure,pvValue));
|
||||
return pvRecord;
|
||||
}
|
||||
|
||||
ExampleRecord::ExampleRecord(
|
||||
String const & recordName,
|
||||
PVStructurePtr const & pvStructure,
|
||||
PVLongPtr const &pvValue)
|
||||
: PVRecord(recordName,pvStructure),
|
||||
pvValue(pvValue)
|
||||
{}
|
||||
|
||||
bool ExampleRecord::isSynchronous() {return true;}
|
||||
|
||||
void ExampleRecord::process(
|
||||
RecordProcessRequesterPtr const &processRequester)
|
||||
{
|
||||
pvValue->put(pvValue->get() + 1);
|
||||
processRequester->recordProcessResult(Status::Ok);
|
||||
processRequester->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->addRecord(pvRecord);
|
||||
cout << recordName << "\n";
|
||||
string str;
|
||||
while(true) {
|
||||
cout << "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 & recordName,
|
||||
epics::pvData::PVStructurePtr const & pvStructure);
|
||||
virtual ~PVRecord();
|
||||
virtual void process(
|
||||
RecordProcessRequesterPtr const &recordProcessRequester) = 0;
|
||||
virtual bool isSynchronous() = 0;
|
||||
epics::pvData::String getRecordName();
|
||||
PVRecordStructurePtr getPVRecordStructure();
|
||||
PVRecordFieldPtr findPVRecordField(
|
||||
epics::pvData::PVFieldPtr const & pvField);
|
||||
void lock();
|
||||
void unlock();
|
||||
void registerClient(PVRecordClientPtr const & pvRecordClient);
|
||||
void unregisterClient(PVRecordClientPtr const & pvRecordClient);
|
||||
void detachClients();
|
||||
void beginGroupPut();
|
||||
void endGroupPut();
|
||||
void registerListener(PVListenerPtr const & pvListener);
|
||||
void unregisterListener(PVListenerPtr const & pvListener);
|
||||
void removeEveryListener();
|
||||
epics::pvData::Status processRequest();
|
||||
void queueProcessRequest(
|
||||
RecordProcessRequesterPtr const &recordProcessRequester);
|
||||
void addRequester(epics::pvData::RequesterPtr const & requester);
|
||||
void removeRequester(epics::pvData::RequesterPtr const & requester);
|
||||
void message(
|
||||
epics::pvData::String const & 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 & pvField,
|
||||
PVRecordPtr const & pvRecord);
|
||||
virtual ~PVRecordField();
|
||||
PVRecordStructurePtr getParent();
|
||||
epics::pvData::PVFieldPtr getPVField();
|
||||
epics::pvData::String getFullFieldName();
|
||||
epics::pvData::String getFullName();
|
||||
PVRecordPtr getPVRecord();
|
||||
bool addListener(PVListenerPtr const & pvListener);
|
||||
void removeListener(PVListenerPtr const & pvListener);
|
||||
void postPut();
|
||||
virtual void message(
|
||||
epics::pvData::String const & 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 & pvStructure,
|
||||
PVRecordFieldPtrArrayPtr const & pvRecordField);
|
||||
virtual ~PVRecordStructure();
|
||||
PVRecordFieldPtrArrayPtr getPVRecordFields();
|
||||
epics::pvData::PVStructurePtr getPVStructure();
|
||||
virtual void message(
|
||||
epics::pvData::String const & 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 & 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;
|
||||
virtual void unlisten(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>
|
||||
<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 & 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& recordName);
|
||||
bool addRecord(PVRecordPtr const & record);
|
||||
bool removeRecord(PVRecordPtr const & 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>
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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
634
src/database/pvRecord.cpp
Normal 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)
|
||||
{
|
||||
}
|
||||
|
||||
}}
|
@ -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();
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}}
|
||||
|
@ -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();
|
||||
|
Reference in New Issue
Block a user