updated pvDatabaseCPP.html and TODO

This commit is contained in:
Marty Kraimer
2014-10-09 09:33:13 -04:00
parent e81230dba5
commit 2bea54e218
3 changed files with 76 additions and 73 deletions

View File

@ -4,5 +4,5 @@
<h2>Must test record delete.</h2>
<p>Must test removing a record from the PVDatabase while a pvAccess client
is attached. Also why do both unlisten and detach exists?</p>
<h2>create more regresion tests</h2>
<h2>create more regression tests</h2>
<p>Currently only some simple tests exist. Most of the testing has been via the examples</p>

View File

@ -13,7 +13,7 @@ Must test removing a record from the PVDatabase while a pvAccess client
is attached. Also why do both unlisten and detach exists?
create more regresion tests
create more regression tests
----------------
Currently only some simple tests exist. Most of the testing has been via the examples

View File

@ -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
@ -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&gt; pwd
@ -270,13 +270,13 @@ It is expected that these threads will be sufficient to efficiently handle all c
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,7 +315,9 @@ 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>Minimum Features Required for pvRecord</h3>
<p>The first phase will only implement record processing, i. e.
@ -323,7 +326,8 @@ This will be sufficient for implementing many services.
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 minimum 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>
@ -530,15 +536,12 @@ typedef std::tr1::shared_ptr&lt;PVRecordClient&gt; PVRecordClientPtr;
class PVListener;
typedef std::tr1::shared_ptr&lt;PVListener&gt; PVListenerPtr;
class RecordPutRequester;
typedef std::tr1::shared_ptr&lt;RecordPutRequester&gt; RecordPutRequesterPtr;
class PVDatabase;
typedef std::tr1::shared_ptr&lt;PVDatabase&gt; PVDatabasePtr;
</pre>
<h3>class PVRecord</h3>
<p>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&lt;PVDatabase&gt; 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&lt;PVRecord&gt;
{
public:
POINTER_DEFINITIONS(PVRecord);
virtual bool init() {initPVRecord(); return true;}
virtual bool init() ;
virtual void start() {}
virtual void process() {}
virtual void destroy();
@ -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 &amp; record);
epics::pvData::PVStringArrayPtr getRecordNames();
bool removeRecord(PVRecordPtr const &amp; 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
@ -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-&gt;startMonitoring(activeElement)
<dd>
</dd>
<dt>stop</dt>
<dd>
Called by client.
@ -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
@ -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-&gt;addRecord(pvRecord);
if(!result) cout&lt;&lt; "record " &lt;&lt; recordName &lt;&lt; " not added" &lt;&lt; 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
@ -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-&gt;addRecord(pvRecord);
if(!result) cout&lt;&lt; "record " &lt;&lt; recordName &lt;&lt; " not added" &lt;&lt; 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&lt;ExampleServer&gt; 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&lt;&lt; "record " &lt;&lt; recordName &lt;&lt; " not added" &lt;&lt; endl;
recordName = "traceRecordPGRPC";
pvRecord = TraceRecord::create(recordName);
result = master-&gtr;addRecord(pvRecord);
result = master-&gt;addRecord(pvRecord);
if(!result) cout&lt;&lt; "record " &lt;&lt; recordName &lt;&lt; " not added" &lt;&lt; endl;
recordName = "recordListPGRPC";
pvRecord = RecordListRecord::create(recordName);
result = master-&gtr;addRecord(pvRecord);
result = master-&gt;addRecord(pvRecord);
if(!result) cout&lt;&lt; "record " &lt;&lt; recordName &lt;&lt; " not added" &lt;&lt; endl;
ServerContext::shared_pointer pvaServer =
startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
PVStringArrayPtr pvNames = master-&gtr;getRecordNames();
shared_vector&lt;const string&gtr; names = pvNames-&gtr;view();
PVStringArrayPtr pvNames = master-&gt;getRecordNames();
shared_vector&lt;const string&gt; names = pvNames-&gt;view();
for(size_t i=0; i&lt;names.size(); ++i) cout &lt;&lt; names[i] &lt;&lt; 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&gt; pwd
/home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
mrk&gt; ../../../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&gt; dbl
pvdouble
@ -1472,7 +1475,6 @@ epics&gt;
</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>
@ -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&gt; pwd
@ -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&gt; pwd
@ -1797,7 +1799,7 @@ mrk&gt; pwd
mrk&gt; 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&gt; bin/linux-x86_64/longArrayMonitorMain -help
longArrayMonitorMain channelName queueSize waitTime
@ -1826,7 +1828,7 @@ bin/linux/&lt;arch&gt;/longArrayGetMain
bin/linux/&lt;arch&gt;/longArrayPutMain
</pre>
<p>Each program generates a report every second when it has something to report.
Examples are:
Examples are:</p>
<pre>
mrk&gt; 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>
@ -1984,9 +1987,9 @@ Every second it produces a report.</p>
<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,7 +2011,7 @@ 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&gt; pwd
/home/hg/pvDatabaseCPP-md
@ -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
...