|
|
|
@ -38,7 +38,7 @@
|
|
|
|
|
<h1>pvDatabaseCPP</h1>
|
|
|
|
|
<!-- Maturity: Working Draft or Request for Comments, or Recommendation, and date. -->
|
|
|
|
|
|
|
|
|
|
<h2 class="nocount">EPICS v4 Working Group, Working Draft, 11-August-2014</h2>
|
|
|
|
|
<h2 class="nocount">EPICS v4 Working Group, Working Draft, 09-Oct-2014</h2>
|
|
|
|
|
<dl>
|
|
|
|
|
<dt>Latest version:</dt>
|
|
|
|
|
<dd><a
|
|
|
|
@ -63,11 +63,11 @@ href="http://epics-pvdata.sourceforge.net/LICENSE.html">EPICS open source licens
|
|
|
|
|
<h2 class="nocount">Abstract</h2>
|
|
|
|
|
|
|
|
|
|
<p>This document describes pvDatabaseCPP,
|
|
|
|
|
which is a framework for implementing a network accessable database of smart memory resident
|
|
|
|
|
which is a framework for implementing a network accessible database of smart memory resident
|
|
|
|
|
records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by
|
|
|
|
|
pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess.
|
|
|
|
|
The framework can be extended in order to create record instances that implements services.
|
|
|
|
|
The minimum that an extenson must provide is a top level PVStructure and a process method.
|
|
|
|
|
The minimum that an extension must provide is a top level PVStructure and a process method.
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<p>EPICS version 4 is a set of related products in the EPICS
|
|
|
|
@ -78,9 +78,9 @@ V4 control system programming environment:<br />
|
|
|
|
|
|
|
|
|
|
<h2 class="nocount">Status of this Document</h2>
|
|
|
|
|
|
|
|
|
|
<p>This is the 11-August-2014 version of of pvDatabaseCPP.</p>
|
|
|
|
|
</p>
|
|
|
|
|
<p>This is the 09-Oct-2014 version of of pvDatabaseCPP.</p>
|
|
|
|
|
<p>This version is a complete implementation of what is described in this manual.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div id="toc">
|
|
|
|
@ -92,7 +92,7 @@ V4 control system programming environment:<br />
|
|
|
|
|
<h2>Introduction</h2>
|
|
|
|
|
<h3>Overview</h3>
|
|
|
|
|
<p>The main purpose of this project to make it easier to implement services that are accessed via pvAccess.
|
|
|
|
|
This project supplies is a complete implementation of the server side of pvAccess.
|
|
|
|
|
This project supplies a complete implementation of the server side of pvAccess.
|
|
|
|
|
All that a service has to provide is a top level PVStructure and a process method.
|
|
|
|
|
A service can be run as a main process or can be part of a V3 IOC.
|
|
|
|
|
Thus services can be developed that interact with V3 records, asynDriver,
|
|
|
|
@ -109,25 +109,25 @@ The local provider provides access to the records in the pvDatabase.
|
|
|
|
|
This local provider is accessed by the remote pvAccess server.
|
|
|
|
|
A record is smart because code can be attached to a record, which is accessed via a method named process.</p>
|
|
|
|
|
|
|
|
|
|
<p>This document describes components that provide the following features:
|
|
|
|
|
<p>This document describes components that provide the following features:</p>
|
|
|
|
|
<dl>
|
|
|
|
|
<dt>database<dt>
|
|
|
|
|
<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>
|
|
|
|
|
<dt>pvDatabase</dt>
|
|
|
|
|
<dd>This is a database of pvRecords.
|
|
|
|
|
Records can be added and removed from a database.</dd>
|
|
|
|
|
</dl>
|
|
|
|
|
</dd>
|
|
|
|
|
<dt>pvAccess</dt>
|
|
|
|
|
<dd>This is a complete implementation of ChannelProvider and Channel
|
|
|
|
|
as defined by pvAccess.
|
|
|
|
|
It is used by the server side of pvAccess to attach to pvRecords.
|
|
|
|
|
This component also provides a C++ implementation
|
|
|
|
|
of the monitor and pvCopy components from pvIOCJava</dd>
|
|
|
|
|
</dd>
|
|
|
|
|
<dt>Main and V3IOC</dt>
|
|
|
|
|
<dd>The pvDatabase can be provided via a Main program or can be part
|
|
|
|
|
of a V3IOC. In the later case the IOC has both a database of V3 Records
|
|
|
|
@ -153,7 +153,7 @@ href="./html/index.html">doxygenDoc</a></p>
|
|
|
|
|
<p>The first step is to build pvDatabaseCPP as described in the next section.</p>
|
|
|
|
|
<p>One of the examples is exampleServer.
|
|
|
|
|
It can be started either via a main program or as part of a V3 IOC.
|
|
|
|
|
<p>
|
|
|
|
|
</p>
|
|
|
|
|
<p>To start it as a main program do the following:</p>
|
|
|
|
|
<pre>
|
|
|
|
|
mrk> pwd
|
|
|
|
@ -251,7 +251,7 @@ arrays. It is also described in a later section.</p>
|
|
|
|
|
to learn how to implement a service.</p>
|
|
|
|
|
|
|
|
|
|
<h3>Relationship with pvIOCJava.</h3>
|
|
|
|
|
<p>This document descibes a C++ implementation of some of the components in pvIOCJava,
|
|
|
|
|
<p>This document describes a C++ implementation of some of the components in pvIOCJava,
|
|
|
|
|
which also implements a pvDatabase.
|
|
|
|
|
PVDatabaseCPP implements the core components required to create a network accessible database of smart
|
|
|
|
|
memory resident records.
|
|
|
|
@ -266,17 +266,17 @@ Until a need is demonstrated this will remain true.
|
|
|
|
|
The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess.
|
|
|
|
|
The server side of remote pvAccess creates two threads for each client and always accesses
|
|
|
|
|
a record via these threads.
|
|
|
|
|
It is expected that these threads will be sufficient to efficently handle all channel methods except
|
|
|
|
|
It is expected that these threads will be sufficient to efficiently handle all channel methods except
|
|
|
|
|
channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests.
|
|
|
|
|
If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility,
|
|
|
|
|
then the scanning facility will have to provide some way of handling process requests that block.</p>
|
|
|
|
|
</p>
|
|
|
|
|
<h3>Phased Development</h3>
|
|
|
|
|
<p>This documentation describes the first phase of a phased implementation of pvDatabaseCPP:</pp>
|
|
|
|
|
<p>This documentation describes the first phase of a phased implementation
|
|
|
|
|
of pvDatabaseCPP:</p>
|
|
|
|
|
<dl>
|
|
|
|
|
<dt>pvRecord</d>
|
|
|
|
|
<dt>pvRecord</dt>
|
|
|
|
|
<dd>Wrapper on PVStructure that implements methods required by Local Channel Provider.</dd>
|
|
|
|
|
<dt>pvDatabase</d>
|
|
|
|
|
<dt>pvDatabase</dt>
|
|
|
|
|
<dd>Database of PVRecords. Has methods find, add, and remove.</dd>
|
|
|
|
|
<dt>Local Channel Provider</dt>
|
|
|
|
|
<dd>Complete implementation of ChannelProvider and Channel.
|
|
|
|
@ -294,10 +294,11 @@ then the scanning facility will have to provide some way of handling process req
|
|
|
|
|
<dd>Add the ability to optionally add support to fields.
|
|
|
|
|
In addition some of the basic support defined in pvIOCJava could also be implemented.</dd>
|
|
|
|
|
<dt>XML parser</dt>
|
|
|
|
|
<dd>This provides the ability to create record instances without writing any code.</dd>
|
|
|
|
|
<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.
|
|
|
|
|
<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>
|
|
|
|
@ -314,16 +315,19 @@ The rest of this document discusses only the first phase.</p>
|
|
|
|
|
<dt>The localChannelProvider itself</dt>
|
|
|
|
|
<dd>This is the C++ implementation of package pvAccess in pvIOCJava.
|
|
|
|
|
The localChannelProvider accesses data from PVRecords.
|
|
|
|
|
It implements all channel methods except channelRPC, which is implemented by pvAccessCPP.</dd>
|
|
|
|
|
It implements all channel methods except channelRPC,
|
|
|
|
|
which is implemented by pvAccessCPP.
|
|
|
|
|
</dd>
|
|
|
|
|
</dl>
|
|
|
|
|
<h3>Minumum Features Required for pvRecord</h3>
|
|
|
|
|
<h3>Minimum 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 implementing many services.
|
|
|
|
|
The following are the minimium features required</p>
|
|
|
|
|
The following are the minimum features required</p>
|
|
|
|
|
<dl>
|
|
|
|
|
<dt>PVDatabase</dt>
|
|
|
|
|
<dd>This holds a set of PVRecords. It has methods to find, add, and remove records.</dd>
|
|
|
|
|
<dd>This holds a set of PVRecords.
|
|
|
|
|
It has methods to find, add, and remove records.</dd>
|
|
|
|
|
<dt>PVRecord</dt>
|
|
|
|
|
<dd>This, and a set of related interfaces, provides the following:
|
|
|
|
|
<dl>
|
|
|
|
@ -331,9 +335,11 @@ The following are the minimium features required</p>
|
|
|
|
|
<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>
|
|
|
|
|
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.
|
|
|
|
|
<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>
|
|
|
|
@ -421,11 +427,11 @@ of how an application can make the shell commands available.
|
|
|
|
|
<dt>stopPVAClient</dt>
|
|
|
|
|
<dd>Stops pvAccess.</dd>
|
|
|
|
|
<dt>startPVAServer</dt>
|
|
|
|
|
<dd>Starts the local channel provider</p>
|
|
|
|
|
<dd>Starts the local channel provider</dd>
|
|
|
|
|
<dt>stopPVAServer</dt>
|
|
|
|
|
<dd>Stop the local channel provider</dd>
|
|
|
|
|
</dl>
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<h3>Commands implemented by pvDatabaseCPP</h3>
|
|
|
|
|
<p>The following iocsh commands are provided for a V3IOC:</p>
|
|
|
|
|
<dl>
|
|
|
|
@ -433,16 +439,16 @@ of how an application can make the shell commands available.
|
|
|
|
|
<dd>Provides a list of all the pvRecords in database <b>master</b>
|
|
|
|
|
</dd>
|
|
|
|
|
</dl>
|
|
|
|
|
<p>In addition any code that implements a PVRecord must implement an ioc command.</p>
|
|
|
|
|
<p>Look at any of the examples to see how to implement shell commands.</p>
|
|
|
|
|
<p>In addition any code that implements a PVRecord must implement an ioc command.
|
|
|
|
|
Look at any of the examples to see how to implement shell commands.</p>
|
|
|
|
|
<h3>Commands implemented by pvaSrv</h3>
|
|
|
|
|
<p><b>pvaSrv</b> provides a pvAccess server that provides access to iocCore records.</p>
|
|
|
|
|
<p>pvaSrv does not provide any shell commands but it can be part of an IOC.
|
|
|
|
|
Just make sure your application configures pvaSrv and then include the following file:
|
|
|
|
|
</p>
|
|
|
|
|
<pre>
|
|
|
|
|
include "dbPv.dbd"
|
|
|
|
|
</pre>
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<h2>database</h2>
|
|
|
|
|
<h3>src/database</h3>
|
|
|
|
@ -501,7 +507,7 @@ It describes the following classes:</p>
|
|
|
|
|
<dd>These wrap PVField and PVStructure so that pvCopy and monitor
|
|
|
|
|
can be implemented.</dd>
|
|
|
|
|
<dt>PVRecordClient</dt>
|
|
|
|
|
<dd>This is called by anything that acceses PVRecord.</dd>
|
|
|
|
|
<dd>This is called by anything that accesses PVRecord.</dd>
|
|
|
|
|
<dt>PVListener</dt>
|
|
|
|
|
<dd>This is implemented by anything that wants to trap calls to PVRecord::message.</dd>
|
|
|
|
|
<dt>PVDatabase</dt>
|
|
|
|
@ -530,15 +536,12 @@ typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
|
|
|
|
|
class PVListener;
|
|
|
|
|
typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
|
|
|
|
|
|
|
|
|
|
class RecordPutRequester;
|
|
|
|
|
typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;
|
|
|
|
|
|
|
|
|
|
class PVDatabase;
|
|
|
|
|
typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
|
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
|
|
<h3>class PVRecord</h3>
|
|
|
|
|
<p>NOTES:
|
|
|
|
|
<p>NOTES:</p>
|
|
|
|
|
<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,
|
|
|
|
@ -549,17 +552,17 @@ typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
|
|
|
|
|
Most of the methods are used by the pvAccess code.
|
|
|
|
|
Service implementers will mostly be interested in methods init and process.
|
|
|
|
|
These are described first.
|
|
|
|
|
</li>
|
|
|
|
|
</ul>
|
|
|
|
|
<hr>PVRecord Methods</h4>
|
|
|
|
|
<h4>PVRecord Methods</h4>
|
|
|
|
|
<pre>
|
|
|
|
|
class PVRecord
|
|
|
|
|
public epics::pvData::Requester,
|
|
|
|
|
public std::tr1::enable_shared_from_this<PVRecord>
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
POINTER_DEFINITIONS(PVRecord);
|
|
|
|
|
|
|
|
|
|
virtual bool init() {initPVRecord(); return true;}
|
|
|
|
|
virtual bool init() ;
|
|
|
|
|
virtual void start() {}
|
|
|
|
|
virtual void process() {}
|
|
|
|
|
virtual void destroy();
|
|
|
|
@ -645,7 +648,7 @@ private:
|
|
|
|
|
must have the record locked.</dd>
|
|
|
|
|
<dt>tryLock</dt>
|
|
|
|
|
<dd>If true then just like lock.
|
|
|
|
|
If falseclient can not access record.
|
|
|
|
|
If false 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>
|
|
|
|
@ -801,6 +804,7 @@ that holds the data. It has the following methods:
|
|
|
|
|
<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>
|
|
|
|
|
</dl>
|
|
|
|
|
<h3>class PVRecordClient</h3>
|
|
|
|
|
<pre>
|
|
|
|
|
class PVRecordClient {
|
|
|
|
@ -816,7 +820,6 @@ class PVRecordClient {
|
|
|
|
|
<dt>detach</dt>
|
|
|
|
|
<dd>The record is being removed from the master database,</dd>
|
|
|
|
|
</dl>
|
|
|
|
|
</dl>
|
|
|
|
|
<h3>class PVListener</h3>
|
|
|
|
|
<pre>
|
|
|
|
|
class PVListener {
|
|
|
|
@ -866,7 +869,6 @@ public:
|
|
|
|
|
bool addRecord(PVRecordPtr const & record);
|
|
|
|
|
epics::pvData::PVStringArrayPtr getRecordNames();
|
|
|
|
|
bool removeRecord(PVRecordPtr const & record);
|
|
|
|
|
virtual std::string getRequesterName();
|
|
|
|
|
private:
|
|
|
|
|
PVDatabase();
|
|
|
|
|
};
|
|
|
|
@ -890,8 +892,6 @@ private:
|
|
|
|
|
<dt>removeRecord</dt>
|
|
|
|
|
<dd>Remove a record from the database.
|
|
|
|
|
If the record was not in the database false is returned.</dd>
|
|
|
|
|
<dt>getRequesterName</dt>
|
|
|
|
|
<dd>Virtual method of Requester</dd>
|
|
|
|
|
</dl>
|
|
|
|
|
<h2>pvAccess</h2>
|
|
|
|
|
<p>This is code that provides an implementation of channelProvider as
|
|
|
|
@ -919,7 +919,7 @@ is implemented.</p>
|
|
|
|
|
<p>This is the code that implements monitors on changes to fields of a PVRecord.
|
|
|
|
|
Because it is called by pvAccess client (monitor methods) and by
|
|
|
|
|
PVRecord (when postPut is called), it must be careful to prevent deadlocks.
|
|
|
|
|
The implementation is via class MonitorLocal (implmented in monitorFactory.cpp)
|
|
|
|
|
The implementation is via class MonitorLocal (implemented in monitorFactory.cpp)
|
|
|
|
|
and PVCopyMonitor.
|
|
|
|
|
MonitorLocal is the interface between pvAccess and PVCopyMonitor.
|
|
|
|
|
PVCopyMonitor is the interface between MonitorLocal and PVRecord.
|
|
|
|
@ -930,7 +930,7 @@ While monitoring is active PVCopyMonitor updates the active monitor element when
|
|
|
|
|
a postPut is issued to any field being monitored.
|
|
|
|
|
</p>
|
|
|
|
|
<p>The following two sections provide a few more details about MonitorLocal
|
|
|
|
|
and PVCopyMonitor.<p>
|
|
|
|
|
and PVCopyMonitor.</p>
|
|
|
|
|
<h4>MonitorLocal</h4>
|
|
|
|
|
<p>MonitorLocal implements the following abstract base classes:</p>
|
|
|
|
|
<dl>
|
|
|
|
@ -980,7 +980,7 @@ MonitorLocal manages the following:
|
|
|
|
|
With a lock held it clears the monitorElement queue
|
|
|
|
|
and allocates an active element.
|
|
|
|
|
With no lock held calls pvCopyMonitor->startMonitoring(activeElement)
|
|
|
|
|
<dd>
|
|
|
|
|
</dd>
|
|
|
|
|
<dt>stop</dt>
|
|
|
|
|
<dd>
|
|
|
|
|
Called by client.
|
|
|
|
@ -1028,7 +1028,7 @@ active element but never modifies the pvStructure.
|
|
|
|
|
<dl>
|
|
|
|
|
<dt>startMonitoring</dt>
|
|
|
|
|
<dd>With no lock held it sets its monitorElement to the
|
|
|
|
|
startElement pased by monitorLocal and calls pvRecord->addListener(getPtrSelf()).
|
|
|
|
|
startElement passed by monitorLocal and calls pvRecord->addListener(getPtrSelf()).
|
|
|
|
|
It locks the pvRecord.
|
|
|
|
|
It calls calls addListener for every field in the record that is being
|
|
|
|
|
monitored.
|
|
|
|
@ -1074,7 +1074,7 @@ and one that is used for testing.</p>
|
|
|
|
|
<h3>traceRecord</h3>
|
|
|
|
|
<p>This implements a PVRecord that allows a client to set
|
|
|
|
|
the trace level of a record. It follows the pattern of a channelPutGet
|
|
|
|
|
record:
|
|
|
|
|
record:</p>
|
|
|
|
|
<pre>
|
|
|
|
|
traceRecord
|
|
|
|
|
structure argument
|
|
|
|
@ -1096,7 +1096,7 @@ where:
|
|
|
|
|
<dd>Lifecycle messages will be generated.
|
|
|
|
|
This all channel create and destroy instances will be shown.</dd>
|
|
|
|
|
<dt>2</dt>
|
|
|
|
|
<dd>In addition to lifecycle messages a message will be generted
|
|
|
|
|
<dd>In addition to lifecycle messages a message will be generated
|
|
|
|
|
for each get and put request.</dd>
|
|
|
|
|
<dt>>2</dt>
|
|
|
|
|
<dd>Currently no definition</dd>
|
|
|
|
@ -1105,7 +1105,7 @@ where:
|
|
|
|
|
<dt>result</dt>
|
|
|
|
|
<dd>The result of a cannelPutGet request</dd>
|
|
|
|
|
</dl>
|
|
|
|
|
<p>testExampleServerMain.cpp has an example of how to create a traceRecord:
|
|
|
|
|
<p>testExampleServerMain.cpp has an example of how to create a traceRecord:</p>
|
|
|
|
|
<pre>
|
|
|
|
|
PVDatabasePtr master = PVDatabase::getMaster();
|
|
|
|
|
PVRecordPtr pvRecord;
|
|
|
|
@ -1116,12 +1116,12 @@ pvRecord = TraceRecord::create(recordName);
|
|
|
|
|
result = master->addRecord(pvRecord);
|
|
|
|
|
if(!result) cout<< "record " << recordName << " not added" << endl;
|
|
|
|
|
</pre>
|
|
|
|
|
</p>
|
|
|
|
|
<h3>recordList</h3>
|
|
|
|
|
<p>This implements a PVRecord that allows a client to
|
|
|
|
|
get the names of all the PVRecords in the PVDatabase.
|
|
|
|
|
It follows the pattern of a channelPutGet
|
|
|
|
|
record:
|
|
|
|
|
</p>
|
|
|
|
|
<pre>
|
|
|
|
|
traceRecord
|
|
|
|
|
structure argument
|
|
|
|
@ -1134,7 +1134,7 @@ traceRecord
|
|
|
|
|
where:
|
|
|
|
|
<dl>
|
|
|
|
|
<dt>database</dt>
|
|
|
|
|
<dd>The name of the datbase. The default is "master"</dd>
|
|
|
|
|
<dd>The name of the database. The default is "master"</dd>
|
|
|
|
|
<dt>regularExpression</dt>
|
|
|
|
|
<dd>For now this is ignored and the complete list of names is always
|
|
|
|
|
returned.</dd>
|
|
|
|
@ -1148,17 +1148,18 @@ requires that a record of this type is present and calls it.
|
|
|
|
|
Thus user code does not have to use a channelGetPut to get the list
|
|
|
|
|
of record names.</p>
|
|
|
|
|
<p>testExampleServerMain.cpp has an example of how to create a traceRecord:
|
|
|
|
|
</p>
|
|
|
|
|
<pre>
|
|
|
|
|
recordName = "recordListPGRPC";
|
|
|
|
|
pvRecord = RecordListRecord::create(recordName);
|
|
|
|
|
result = master->addRecord(pvRecord);
|
|
|
|
|
if(!result) cout<< "record " << recordName << " not added" << endl;
|
|
|
|
|
</pre>
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<h2>exampleServer</h2>
|
|
|
|
|
<h3>Overview</h3>
|
|
|
|
|
<p>The example implements a simple service that has a top level pvStructure:
|
|
|
|
|
</p>
|
|
|
|
|
<pre>
|
|
|
|
|
structure
|
|
|
|
|
structure argument
|
|
|
|
@ -1234,9 +1235,10 @@ where
|
|
|
|
|
<dt>exampleServerRegister.cpp</dt>
|
|
|
|
|
<dd>This has the code to start the service via the following iocsh
|
|
|
|
|
command.
|
|
|
|
|
</dd>
|
|
|
|
|
<dt>exampleServerRegister.dbd</dt>
|
|
|
|
|
<dd>This is the file that is used to create the shell command
|
|
|
|
|
exampleServerCreateRecord.</dd>
|
|
|
|
|
exampleServerCreateRecord.
|
|
|
|
|
<pre>
|
|
|
|
|
exampleServerCreateRecord exampleServer
|
|
|
|
|
</pre>
|
|
|
|
@ -1271,7 +1273,7 @@ separate from where the iocs are build.</p>
|
|
|
|
|
<p>The example resides in src
|
|
|
|
|
The implementation is in exampleServer.cpp.
|
|
|
|
|
</p>
|
|
|
|
|
</p>The description consists of</p>
|
|
|
|
|
<p>The description consists of</p>
|
|
|
|
|
<pre>
|
|
|
|
|
class ExampleServer;
|
|
|
|
|
typedef std::tr1::shared_ptr<ExampleServer> ExampleServerPtr;
|
|
|
|
@ -1299,30 +1301,31 @@ private:
|
|
|
|
|
</pre>
|
|
|
|
|
<p>where</p>
|
|
|
|
|
<dl>
|
|
|
|
|
<dt>create<dt>
|
|
|
|
|
<dt>create</dt>
|
|
|
|
|
<dd>This is example specific but each support could provide
|
|
|
|
|
a similar static method.
|
|
|
|
|
</dd>
|
|
|
|
|
<dt>~ExampleServer<dt>
|
|
|
|
|
<dt>~ExampleServer</dt>
|
|
|
|
|
<dd>The destructor must be declared virtual.</dd>
|
|
|
|
|
<dt>destroy</dt>
|
|
|
|
|
<dd>Called when the record is being destroyed.
|
|
|
|
|
This must call the base class destroy method.
|
|
|
|
|
<dt>init<dt>
|
|
|
|
|
</dd>
|
|
|
|
|
<dt>init</dt>
|
|
|
|
|
<dd>A method to initialize the support. It returns true
|
|
|
|
|
if initialization is successful and false if not.
|
|
|
|
|
NOTE that this is a virtual method of PVRecord itself.</dd>
|
|
|
|
|
<dt>process<dt>
|
|
|
|
|
<dt>process</dt>
|
|
|
|
|
<dd>
|
|
|
|
|
This again is a virtual method of PVRecord.
|
|
|
|
|
</dd>
|
|
|
|
|
<dt>ExampleServer<dt>
|
|
|
|
|
<dt>ExampleServer</dt>
|
|
|
|
|
<dd>For the example this is private.</dd>
|
|
|
|
|
<dt>pvValue</dt>
|
|
|
|
|
<dd>This is the field of the top level structure that process
|
|
|
|
|
accesses.
|
|
|
|
|
</dd>
|
|
|
|
|
<dl>
|
|
|
|
|
</dl>
|
|
|
|
|
<p>The implementation of create method is:</p>
|
|
|
|
|
<pre>
|
|
|
|
|
ExampleServerPtr ExampleServer::create(
|
|
|
|
@ -1423,16 +1426,16 @@ int main(int argc,char *argv[])
|
|
|
|
|
if(!result) cout<< "record " << recordName << " not added" << endl;
|
|
|
|
|
recordName = "traceRecordPGRPC";
|
|
|
|
|
pvRecord = TraceRecord::create(recordName);
|
|
|
|
|
result = master->r;addRecord(pvRecord);
|
|
|
|
|
result = master->addRecord(pvRecord);
|
|
|
|
|
if(!result) cout<< "record " << recordName << " not added" << endl;
|
|
|
|
|
recordName = "recordListPGRPC";
|
|
|
|
|
pvRecord = RecordListRecord::create(recordName);
|
|
|
|
|
result = master->r;addRecord(pvRecord);
|
|
|
|
|
result = master->addRecord(pvRecord);
|
|
|
|
|
if(!result) cout<< "record " << recordName << " not added" << endl;
|
|
|
|
|
ServerContext::shared_pointer pvaServer =
|
|
|
|
|
startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
|
|
|
|
|
PVStringArrayPtr pvNames = master->r;getRecordNames();
|
|
|
|
|
shared_vector<const string>r; names = pvNames->r;view();
|
|
|
|
|
PVStringArrayPtr pvNames = master->getRecordNames();
|
|
|
|
|
shared_vector<const string> names = pvNames->view();
|
|
|
|
|
for(size_t i=0; i<names.size(); ++i) cout << names[i] << endl;
|
|
|
|
|
string str;
|
|
|
|
|
while(true) {
|
|
|
|
@ -1452,13 +1455,13 @@ This:
|
|
|
|
|
<li>Runs forever until the user types exit on standard in.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
<h3>V3IOC exampleServer</h3>
|
|
|
|
|
<p>To start exampleServer as part of a V3IOC:
|
|
|
|
|
<p>To start exampleServer as part of a V3IOC:</p>
|
|
|
|
|
<pre>
|
|
|
|
|
mrk> pwd
|
|
|
|
|
/home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
|
|
|
|
|
mrk> ../../../bin/linux-x86_64/exampleServer st.cmd
|
|
|
|
|
</pre></p>
|
|
|
|
|
<p>You can then issue the commands dbl and pvdbl:
|
|
|
|
|
</pre>
|
|
|
|
|
<p>You can then issue the commands dbl and pvdbl:</p>
|
|
|
|
|
<pre>
|
|
|
|
|
epics> dbl
|
|
|
|
|
pvdouble
|
|
|
|
@ -1472,7 +1475,6 @@ epics>
|
|
|
|
|
</pre>
|
|
|
|
|
dbl shows the V3 records.
|
|
|
|
|
pvdbl shows the pvRecords.
|
|
|
|
|
</p>
|
|
|
|
|
<p>
|
|
|
|
|
It starts pvaSrv so that the V3 records can be accessed via Channel Access
|
|
|
|
|
or via PVAccess.</p>
|
|
|
|
@ -1516,7 +1518,6 @@ and 2) gives a brief description of an example that gets data for an array of do
|
|
|
|
|
If access is via pvAccess then locking is handled by pvAccess.</dd>
|
|
|
|
|
</dl>
|
|
|
|
|
<p>Access via pvAccess can be done either by local or remote channel provider.</p>
|
|
|
|
|
</dl>
|
|
|
|
|
<dl>
|
|
|
|
|
<dt>Access via channelProviderLocal</dt>
|
|
|
|
|
<dd>
|
|
|
|
@ -1538,7 +1539,7 @@ and 2) gives a brief description of an example that gets data for an array of do
|
|
|
|
|
</dl>
|
|
|
|
|
<h4>Data synchronization</h4>
|
|
|
|
|
<p>If pvAccess is used then it handles data synchronization.
|
|
|
|
|
This is done by making a copy of the data that is transfered between the two pvRecords.
|
|
|
|
|
This is done by making a copy of the data that is transferred between the two pvRecords.
|
|
|
|
|
This is true if either remote or local pvAccess is used.
|
|
|
|
|
Each get, put, etc request results in data being copied between the two records.</p>
|
|
|
|
|
<p>
|
|
|
|
@ -1558,7 +1559,8 @@ i. e. a main program, or can be part of a V3IOC.</p>
|
|
|
|
|
ClientFactory::start();
|
|
|
|
|
ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
|
|
|
|
|
...
|
|
|
|
|
ServerContext::shared_pointer serverContext = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
|
|
|
|
|
ServerContext::shared_pointer serverContext =
|
|
|
|
|
startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
|
|
|
|
|
</pre>
|
|
|
|
|
<p>The first call is only necessary if some of the pvRecords
|
|
|
|
|
have pvAccess links.
|
|
|
|
@ -1614,7 +1616,7 @@ exampleLink is a record that, when processed, gets the value from doubleArray an
|
|
|
|
|
to the value read.
|
|
|
|
|
st.local has both records.
|
|
|
|
|
st.remote has only one record named exampleLinkRemote.
|
|
|
|
|
<p>
|
|
|
|
|
</p>
|
|
|
|
|
<p>To start the example:</p>
|
|
|
|
|
<pre>
|
|
|
|
|
mrk> pwd
|
|
|
|
@ -1781,7 +1783,7 @@ or some other memory check program.
|
|
|
|
|
<dt>arrayPerformanceMain</dt>
|
|
|
|
|
<dd>This is server and also a configurable number of longArrayMonitor clients.
|
|
|
|
|
The clients can use either the local or
|
|
|
|
|
remote providers. The moitor code is the same code that is used by longArrayMonitorMain.
|
|
|
|
|
remote providers. The monitor code is the same code that is used by longArrayMonitorMain.
|
|
|
|
|
</dd>
|
|
|
|
|
<dt>longArrayMonitorMain</dt>
|
|
|
|
|
<dd>Remote client that monitors the array served by arrayPerformanceMain.</dd>
|
|
|
|
@ -1789,7 +1791,7 @@ or some other memory check program.
|
|
|
|
|
<dd>Remote client that uses channelGet to access the array served by arrayPerformanceMain.</dd>
|
|
|
|
|
<dt>longArrayPutMain</dt>
|
|
|
|
|
<dd>Remote client that uses channelPut to access the array served by arrayPerformanceMain.</dd>
|
|
|
|
|
<dl>
|
|
|
|
|
</dl>
|
|
|
|
|
<p>Each has support for <b>-help</b>.</p>
|
|
|
|
|
<pre>
|
|
|
|
|
mrk> pwd
|
|
|
|
@ -1797,7 +1799,7 @@ mrk> pwd
|
|
|
|
|
mrk> bin/linux-x86_64/arrayPerformanceMain -help
|
|
|
|
|
arrayPerformanceMain recordName size delay providerName nMonitor queueSize waitTime
|
|
|
|
|
default
|
|
|
|
|
arrayPerformance arrayPerformance 10000000 0.0001 local 1 2 0.0
|
|
|
|
|
arrayPerformanceMain arrayPerformance 10000000 0.0001 local 1 2 0.0
|
|
|
|
|
|
|
|
|
|
mrk> bin/linux-x86_64/longArrayMonitorMain -help
|
|
|
|
|
longArrayMonitorMain channelName queueSize waitTime
|
|
|
|
@ -1817,7 +1819,7 @@ longArrayPutMain arrayPerformance 10 0 0 1
|
|
|
|
|
mrk>
|
|
|
|
|
</pre>
|
|
|
|
|
<h3>Example output</h3>
|
|
|
|
|
<p><b>Note:</b> These may fail if run on a platform that does not have sufficent memory,</p>
|
|
|
|
|
<p><b>Note:</b> These may fail if run on a platform that does not have sufficient memory,</p>
|
|
|
|
|
<p>To see an example just execute the following commands in four different terminal windows:</p>
|
|
|
|
|
<pre>
|
|
|
|
|
bin/linux/<arch>/arrayPerformanceMain
|
|
|
|
@ -1825,8 +1827,8 @@ bin/linux/<arch>/longArrayMonitorMain
|
|
|
|
|
bin/linux/<arch>/longArrayGetMain
|
|
|
|
|
bin/linux/<arch>/longArrayPutMain
|
|
|
|
|
</pre>
|
|
|
|
|
<p>Each program generates a report every second when it has somthing to report.
|
|
|
|
|
Examples are:
|
|
|
|
|
<p>Each program generates a report every second when it has something to report.
|
|
|
|
|
Examples are:</p>
|
|
|
|
|
<pre>
|
|
|
|
|
mrk> bin/linux-x86_64/arrayPerformanceMain
|
|
|
|
|
arrayPerformance arrayPerformance 10000000 0.0001 local 1 2 0
|
|
|
|
@ -1889,13 +1891,14 @@ get kiloElements/sec 8726.34
|
|
|
|
|
</dl>
|
|
|
|
|
<p>
|
|
|
|
|
arrayPerformance creates a PVRecord that has the structure:.
|
|
|
|
|
</p>
|
|
|
|
|
<pre>
|
|
|
|
|
recordName
|
|
|
|
|
long[] value
|
|
|
|
|
timeStamp timeStamp
|
|
|
|
|
alarm alarm
|
|
|
|
|
</pre>
|
|
|
|
|
Thus it holds an array of 64 bit integers.</p>
|
|
|
|
|
Thus it holds an array of 64 bit integers.
|
|
|
|
|
<p>The record has support that consists of a separate thread that runs
|
|
|
|
|
until the record is destroyed executing the following algorithm:</p>
|
|
|
|
|
<dl>
|
|
|
|
@ -1906,7 +1909,7 @@ until the record is destroyed executing the following algorithm:</p>
|
|
|
|
|
</dd>
|
|
|
|
|
<dt>create array</dt>
|
|
|
|
|
<dd>A new shared_vector is created and each element is set equal
|
|
|
|
|
to the interation count.</dd>
|
|
|
|
|
to the iteration count.</dd>
|
|
|
|
|
<dt>lock</dt>
|
|
|
|
|
<dd>The arrayPerformance record is locked.</dd>
|
|
|
|
|
<dt>Begin group put</dt>
|
|
|
|
@ -1927,7 +1930,7 @@ until the record is destroyed executing the following algorithm:</p>
|
|
|
|
|
<h3>longArrayMonitor</h3>
|
|
|
|
|
<p>This is a pvAccess client that monitors an arrayPerformance record.
|
|
|
|
|
It generates a report every second showing how many elements has received.
|
|
|
|
|
For every monitor it also checks that the number of alements is >0 and the
|
|
|
|
|
For every monitor it also checks that the number of elements is >0 and the
|
|
|
|
|
the first element equals the last element. It reports an error if either
|
|
|
|
|
of these conditions is not true.</p>
|
|
|
|
|
<p>The arguments for longArrayMonitorMain are:</p>
|
|
|
|
@ -1958,7 +1961,7 @@ Every second it produces a report.</p>
|
|
|
|
|
A value of 0 means never destroy and recreate.
|
|
|
|
|
</dd>
|
|
|
|
|
<dt>delayTime</dt>
|
|
|
|
|
<dd>The time to dalay between gets.</dd>
|
|
|
|
|
<dd>The time to delay between gets.</dd>
|
|
|
|
|
</dl>
|
|
|
|
|
<h3>longArrayPut</h3>
|
|
|
|
|
<p>This is a pvAccess client that uses channelPut to access an arrayPerformance record.
|
|
|
|
@ -1979,14 +1982,14 @@ Every second it produces a report.</p>
|
|
|
|
|
A value of 0 means never destroy and recreate.
|
|
|
|
|
</dd>
|
|
|
|
|
<dt>delayTime</dt>
|
|
|
|
|
<dd>The time to dalay between gets.</dd>
|
|
|
|
|
<dd>The time to delay between gets.</dd>
|
|
|
|
|
</dl>
|
|
|
|
|
|
|
|
|
|
<h3>Some results</h3>
|
|
|
|
|
<h4>array performance</h4>
|
|
|
|
|
<p>The results were from my laptop.
|
|
|
|
|
It has a 2.2Ghz intel core i7 with 4Gbytes of memory.
|
|
|
|
|
The operating system is linux fedora 16.</p>
|
|
|
|
|
<p>The results were from my laptop in 2013
|
|
|
|
|
It had a 2.2Ghz intel core i7 with 4Gbytes of memory.
|
|
|
|
|
The operating system was linux fedora 16.</p>
|
|
|
|
|
<p>When test are performed with large arrays it is a good idea to also
|
|
|
|
|
run a system monitor facility and check memory and swap history.
|
|
|
|
|
If a test configuration causes physical memory to be exhausted
|
|
|
|
@ -2008,13 +2011,13 @@ connection.</p>
|
|
|
|
|
and is putting about 500million elements per second.
|
|
|
|
|
Since each element is an int64 this means about 4gigaBytes per second.
|
|
|
|
|
</p>
|
|
|
|
|
<p>When no monitors are requested and a remote longArrayMonitorMain is run:<p>
|
|
|
|
|
<p>When no monitors are requested and a remote longArrayMonitorMain is run:</p>
|
|
|
|
|
<pre>
|
|
|
|
|
mr> pwd
|
|
|
|
|
/home/hg/pvDatabaseCPP-md
|
|
|
|
|
mrk> bin/linux-x86_64/longArrayMonitorMain
|
|
|
|
|
</pre>
|
|
|
|
|
<p>The performance drops to about 25 interations per second and 250 million elements per second.
|
|
|
|
|
<p>The performance drops to about 25 iterations per second and 250 million elements per second.
|
|
|
|
|
The next section has an example that demonstrates what happens.
|
|
|
|
|
Note that if the array size is small enough to fit in the local cache then running longArrayMonitor
|
|
|
|
|
has almost no effect of arrayPerforance.
|
|
|
|
@ -2075,7 +2078,7 @@ thread1 value 10 time 1.04836 iterations/sec 4.76936 elements/sec 238.468million
|
|
|
|
|
thread0 value 11 time 1.01575 iterations/sec 4.92249 elements/sec 246.124million
|
|
|
|
|
</pre>
|
|
|
|
|
<p>As more threads are running the slower each thread runs.</p>
|
|
|
|
|
<p>But now consider a size that fits in a local cache.<p>
|
|
|
|
|
<p>But now consider a size that fits in a local cache.</p>
|
|
|
|
|
<pre>
|
|
|
|
|
bin/linux-x86_64/vectorPerformanceMain 5000 0.00n/linux-x86_64/vectorPerformanceMain 5000 0.00 1
|
|
|
|
|
...
|
|
|
|
|