1245 lines
46 KiB
HTML
1245 lines
46 KiB
HTML
<?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, 25-Jul-2013</h2>
|
|
<dl>
|
|
<dt>Latest version:</dt>
|
|
<dd><a
|
|
href="pvDatabaseCPP.html">pvDatabaseCPP.html</a>
|
|
</dd>
|
|
<dt>This version:</dt>
|
|
<dd><a
|
|
href= "pvDatabaseCPP_20130725.html">pvDatabase_CPP20130725.html</a>
|
|
</dd>
|
|
<dt>Previous version:</dt>
|
|
<dd><a
|
|
href="pvDatabaseCPP_20130627.html">pvDatabase_CPP20130627.html</a>
|
|
</dd>
|
|
<dt>Editors:</dt>
|
|
<dd>Marty Kraimer, BNL</dd>
|
|
</dl>
|
|
|
|
<p class="copyright">This product is made available subject to acceptance of the <a
|
|
href="http://epics-pvdata.sourceforge.net/LICENSE.html">EPICS open source license.</a></p>
|
|
<hr />
|
|
</div>
|
|
|
|
<h2 class="nocount">Abstract</h2>
|
|
|
|
<p>This document describes pvDatabaseCPP,
|
|
which is a framework for implementing a network accessable database of smart memory resident
|
|
records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by
|
|
pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess.
|
|
The framework can be extended in order to create record instances that implements services.
|
|
The minimum that an extenson must provide is a top level PVStructure and a process method.
|
|
</p>
|
|
|
|
<p>EPICS version 4 is a set of related products in the EPICS
|
|
V4 control system programming environment:<br />
|
|
<a href="http://epics-pvdata.sourceforge.net/relatedDocumentsV4.html">relatedDocumentsV4.html</a>
|
|
</p>
|
|
|
|
|
|
<h2 class="nocount">Status of this Document</h2>
|
|
|
|
<p>This is the 25-Jul-2013 version of the definition of pvDatabaseCPP.
|
|
<p><b>NOTE:</b>
|
|
This is built against pvDataCPP-md NOT against pvDataCPP.
|
|
To build you must also
|
|
checkout pvAccessCPP and build it against pvDataCPP-md.
|
|
</p>
|
|
</p>
|
|
<p>All channel methods except channelRPCi, which is implemented
|
|
by pvAccess, have been implemented.
|
|
This project is ready for alpha users.
|
|
</p>
|
|
<p>Future enhancements in priority order:</p>
|
|
<dl>
|
|
<dt>pvAccess directly to local channelProvider</dt>
|
|
<dd>Create an example record that communicates with another
|
|
record via pvAccess but directly connects to channelProviderLocal.
|
|
Thus channelGet and monitor of arrays will be done without
|
|
copying raw array data.</dd>
|
|
<dt>Array performance</dt>
|
|
<dd>Create an example record that continuously creates array data.
|
|
The idea is to compare performance allocating memory from the
|
|
heap vs using a free list.</dd>
|
|
<dt>Separate example that also has pvaSrv</dt>
|
|
<dd>Create a separate example that combines a V3IOC,
|
|
a pvDatabase, and pvaSrv.</dd>
|
|
<dt>Monitor Algorithms</dt>
|
|
<dd>Monitor algorithms have not been implemented.
|
|
Thus all monitors are onPut.</dd>
|
|
<dt>Memory leaks</dt>
|
|
<dd>I think all memory leaks have been fixed.</dd>
|
|
<dt>Testing</dt>
|
|
<dd>Needs more testing</dd>
|
|
</dl>
|
|
|
|
<div id="toc">
|
|
<h2 class="nocount" style="page-break-before: always">Table of Contents</h2>
|
|
</div>
|
|
<div id="contents" class="contents">
|
|
|
|
|
|
<h2>Introduction</h2>
|
|
<h3>Overview</h3>
|
|
<p>A brief description of a pvDatabase is that it is a set of network accessible, smart,
|
|
memory resident records.
|
|
Each record has data composed of a top level PVStructure.
|
|
Each record has a name which is the channelName for pvAccess.
|
|
A local Channel Provider implements the complete ChannelProvider and
|
|
Channel interfaces as defined by pvAccess.
|
|
The local provider provides access to the records in the pvDatabase.
|
|
This local provider is accessed by the remote pvAccess server.
|
|
A record is smart because code can be attached to a record, which is accessed via a method named process.</p>
|
|
|
|
<p>This document describes components that provide the following features:
|
|
<dl>
|
|
<dt>database<dt>
|
|
<dd>This encapsulates the concept of a database of memory resident smart records.
|
|
The two main components are:
|
|
<dl>
|
|
<dt>pvRecord</dt>
|
|
<dd>This encapsulates the concept of a smart record. It can be processed.
|
|
Changes to field values can be trapped. A record can be locked.</dd>
|
|
<dt>pvDatabase<dt>
|
|
<dd>This is a database of pvRecords.
|
|
Records can be added and removed from a database.</dd>
|
|
</dl>
|
|
<dt>pvAccess</dt>
|
|
<dd>This is a complete implementation of ChannelProvider and Channel
|
|
as defined by pvAccess.
|
|
It is used by the server side of pvAccess to attach to pvRecords.
|
|
This component also includes the monitor and pvCopy components from pvIOCJava</dd>
|
|
<dt>Main and V3IOC</dt>
|
|
<dd>The pvDatabase can be provided via a Main program or can be part
|
|
of a V3IOC. In tha later case the IOC has both a database of V3 Records
|
|
and a pvDatabase.</dd>
|
|
<dt>exampleCounter</dt>
|
|
<dd>This is a simple example showing how to create a PVRecord and
|
|
how to deploy it either as a standalone process or as part of a V3IOC.</dd>
|
|
<dt>exampleServer</dt>
|
|
<dd>This example has a set of PVRecords.
|
|
Again the records can be deployed either as a standalone process or
|
|
as part of a V3IOC.
|
|
</dl>
|
|
<p><b>database</b> provides base classes that make it easy to create record instances.
|
|
The code attached to each record must create the top
|
|
level PVStructure and the following three methods:</p>
|
|
<dl>
|
|
<dt>init</dt>
|
|
<dd>This is a method for initializing the support.
|
|
It returns true if successful and false otherwise.
|
|
</dd>
|
|
<dt>process</dt>
|
|
<dd>This is what makes a record smart.
|
|
</dd>
|
|
<dt>destroy</dt>
|
|
<dd>This releases and resorurces used by the impplementation.</dd>
|
|
</dl>
|
|
<h3>Getting started</h3>
|
|
<p>Included with this project are two examples that are useful for
|
|
seeing how pvDatabase can be used by clients.
|
|
Each can be deployed either as a standalone process or as part of a V3IOC.
|
|
The examples are:
|
|
<dl>
|
|
<dt>exampleCounterMain</dt>
|
|
<dd>This has a database consisting of a single record named exampleCounter:
|
|
The exampleCounter is discussed in a following section.</dd>
|
|
<dt>exampleCounter</dt>
|
|
<dd>This is exampleCounter as part of a V3IOC.</dd>
|
|
<dt>exampleServerMain</dt>
|
|
<dd>This has a database with several records.</dd>
|
|
<dt>exampleServer</dt>
|
|
<dd>This is exampleServer as part of a V3IOC</dd>
|
|
</dt>
|
|
</p>
|
|
<h3>exampleCounter</h3>
|
|
<p>To start exampleCounterMain:
|
|
<pre>
|
|
mrk> pwd
|
|
/home/hg/pvDatabaseCPP
|
|
mrk> bin/linux-x86_64/exampleCounterMain
|
|
</pre></p>
|
|
<p>To start exampleCounter as part of a V3IOC:
|
|
<pre>
|
|
mrk> pwd
|
|
/home/hg/pvDatabaseCPP/iocBoot/exampleCounter
|
|
mrk> ../../../bin/linux-x86_64/exampleCounter st.cmd
|
|
</pre></p>
|
|
<p>You can then issue the commands dbl and pvdbl:
|
|
<pre>
|
|
epics> dbl
|
|
double01
|
|
epics> pvdbl
|
|
exampleCounter
|
|
epics>
|
|
</pre>
|
|
double01 is a v3Record.
|
|
exampleCounter is a pvRecord.
|
|
</p>
|
|
<p>Starting exampleServer is similar.
|
|
After successfully running exampleCounterMain and exampleCounter then
|
|
try starting exampleServerMain and exampleServer.
|
|
</p>
|
|
<h3>swtshell</h3>
|
|
<p>The Java program
|
|
<a
|
|
href="http://epics-pvdata.sourceforge.net/docbuild/swtshellJava/tip/documentation/swtshellJava.html">
|
|
swtshell</a>
|
|
can be used to access pvDatabase.</p>
|
|
<p>In particular read the sections "Getting Started" and "Simple Example".
|
|
They will work on the exampleServer with the following differences:
|
|
<dl>
|
|
<dt>startExample.zip</dt>
|
|
<dd>Do NOT use this. Instead run exampleServer</dd>
|
|
<dt>channelList result</dt>
|
|
<dd>The result of channelList will show the list of records that
|
|
exampleServer has rather than the records from startExample.zip</dd>
|
|
</dl>
|
|
</p>
|
|
<h3>exampleServer</h3>
|
|
<p>The exampleServer pvDatabase includes the following records:
|
|
<dl>
|
|
<dt>exampleCounter</dt>
|
|
<dd>A record that is an instance of exampleCounter described below.
|
|
The most useful channel methods are channelGet, channelProcess,
|
|
and monitor.</dd>
|
|
<dt>exampleDouble</dt>
|
|
<dd>A record that is an instance of a record with a process method
|
|
that does nothing. To test it start a channelPut and a channelGet and/or monitor.</dd>
|
|
<dt>exampleDoubleArray</dt>
|
|
<dd>An array record that is an instance of a record with a process method
|
|
that does nothing. It can be tested like exampleDouble. In addition channelArray can
|
|
also be used.</dd>
|
|
<dt>examplePowerSupply</dt>
|
|
<dd>Can be used by channelGet, channelPut, channelPutGet, and monitor.</dd>
|
|
<dt>laptoprecordListPGRPC</dt>
|
|
<dd>Implements the record expected by swtshell channelList.
|
|
It can also be used via channelPutGet but unnecessary.</dd>
|
|
<dt>traceRecordPGRPC</dt>
|
|
<dd>This can be used via channelPutGet to set the trace level of another record.</dd>
|
|
</dl>
|
|
<p>It also has a number of other scalar and array records.</p>
|
|
|
|
<h3>Relationship with pvIOCJava.</h3>
|
|
<p>This document descibes a C++ implementation of some of the components in pvIOCJava,
|
|
which also implements a pvDatabase.
|
|
PVDatabaseCPP extracts the core components required to create a network accessible database of smart
|
|
memory resident records.
|
|
pvDatabaseCPP does not implement any of the specialized support that pvIOCJava
|
|
provides.
|
|
It is expected that many services will be created that do not require the full features provided
|
|
by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of
|
|
them named pvDatabaseJava.</p>
|
|
<p>Similar to epics base, pvIOCJava implements the concept of synchronous and asynchronous record processing.
|
|
For pvDatabaseCPP the process method is allowed to block.
|
|
Until a need is demonstrated this will remain true.
|
|
The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess.
|
|
The server side of remote pvAccess creates two threads for each client and always accesses
|
|
a record via these threads.
|
|
It is expected that these threads will be sufficient to efficently handle all channel methods except
|
|
channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests.
|
|
If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility,
|
|
then the scanning facility will have to provide some way of handling process requests that block.</p>
|
|
</p>
|
|
<h3>Example PVRecord Extension</h3>
|
|
<p>The example implements a simple counter.
|
|
The example can be run on linux as follows:</p>
|
|
<pre>
|
|
mrk> pwd
|
|
/home/hg/pvDatabaseCPP
|
|
mrk> bin/linux-x86_64/exampleCounter
|
|
|
|
</pre>
|
|
<p>The example consists of four components:</p>
|
|
<dl>
|
|
<dt>ExampleCounter.h</dt>
|
|
<dd>The source code for the counter.
|
|
It is located in directory pvDatabaseCPP/example/src/exampleCounter.
|
|
</dd>
|
|
<dt>exampleCounterMain.cpp</dt>
|
|
<dd>A main program that runs the example so that it can be accessed
|
|
by a pvAccess client.
|
|
It is located in directory pvDatabaseCPP/example/exampleCounter.
|
|
</dd>
|
|
<dt>v3IOC/exampleCounter</dt>
|
|
<dd>This is a directory that packages exampleCounter to make it
|
|
part of a v3IOC.</dd>
|
|
<dt>iocBoot/exampleCounter</dt>
|
|
<dd>A place to start exampleCounter as part of a v3IOC.
|
|
It follows the normal iocCore conventions.</dd>
|
|
</dl>
|
|
<h4>ExampleCounter.h</h4>
|
|
<p>The example resides in src/database.
|
|
The complete implementation is in the header file.
|
|
A serious implementation might break the code into a header and an
|
|
implementation file.<p>
|
|
</p>The description consists of</p>
|
|
<pre>
|
|
class ExampleCounter;
|
|
typedef std::tr1::shared_ptr<ExampleCounter> ExampleCounterPtr;
|
|
|
|
class ExampleCounter :
|
|
public PVRecord
|
|
{
|
|
public:
|
|
POINTER_DEFINITIONS(ExampleCounter);
|
|
static ExampleCounterPtr create(
|
|
epics::pvData::String const & recordName);
|
|
virtual ~ExampleCounter();
|
|
virtual void destroy();
|
|
virtual bool init();
|
|
virtual void process();
|
|
private:
|
|
ExampleCounter(epics::pvData::String const & recordName,
|
|
epics::pvData::PVStructurePtr const & pvStructure);
|
|
epics::pvData::PVLongPtr pvValue;
|
|
epics::pvData::PVTimeStamp pvTimeStamp;
|
|
epics::pvData::TimeStamp timeStamp;
|
|
};
|
|
</pre>
|
|
<p>where</p>
|
|
<dl>
|
|
<dt>create<dt>
|
|
<dd>This is example specific but each support could provide
|
|
a similar static method.
|
|
</dd>
|
|
<dt>~ExampleCounter<dt>
|
|
<dd>The destructor must be declared virtual.</dd>
|
|
<dt>destroy</dt>
|
|
<dd>Called when the record is being destroyed.
|
|
This must call the base class destroy method.
|
|
<dt>init<dt>
|
|
<dd>A method to initialize the support. It returns true
|
|
if initialization is successful and false if not.
|
|
NOTE that this is a virtual method of PVRecord itself.</dd>
|
|
<dt>process<dt>
|
|
<dd>
|
|
This again is a virtual method of PVRecord.
|
|
</dd>
|
|
<dt>ExampleCounter<dt>
|
|
<dd>For the example this is private.</dd>
|
|
<dt>pvValue</dt>
|
|
<dd>This is the field of the top level structure that process
|
|
accesses.
|
|
</dd>
|
|
<dl>
|
|
<p>The implementation of create method is:</p>
|
|
<pre>
|
|
ExampleCounterPtr ExampleCounter::create(
|
|
epics::pvData::String const & recordName)
|
|
{
|
|
epics::pvData::PVStructurePtr pvStructure =
|
|
epics::pvData::getStandardPVField()->scalar(
|
|
epics::pvData::pvDouble,"timeStamp,alarm"");
|
|
ExampleCounterPtr pvRecord(
|
|
new ExampleCounter(recordName,pvStructure));
|
|
if(!pvRecord->init()) pvRecord.reset();
|
|
return pvRecord;
|
|
}
|
|
</pre>
|
|
This:
|
|
<ul>
|
|
<li>Creates the top level structure.</li>
|
|
<li>Creates a ExampleCounterPtr via the constructor.</li>
|
|
<li>Calls init and if it fails resets the shared pointer.</li>
|
|
<li>Returns the shared pointer to the newly created record.</li>
|
|
</ul>
|
|
<p>The private constructor method is:</p>
|
|
<pre>
|
|
ExampleCounter::ExampleCounter(
|
|
epics::pvData::String const & recordName,
|
|
epics::pvData::PVStructurePtr const & pvStructure)
|
|
: PVRecord(recordName,pvStructure)
|
|
{
|
|
pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
|
|
}
|
|
</pre>
|
|
The example is very simple. Note that it calls the base class constructor.
|
|
<p>The destructor and destroy methods are:</p>
|
|
<pre>
|
|
ExampleCounter::~ExampleCounter()
|
|
{
|
|
}
|
|
|
|
void ExampleCounter::destroy()
|
|
{
|
|
PVRecord::destroy();
|
|
}
|
|
</pre>
|
|
The destructor has nothing to do.
|
|
The destroy method, which is virtual, just calls the destroy method of the base class.
|
|
A more complicated example can clean up any resources it used but must call the base
|
|
class destroy method.
|
|
<p>The implementation of init is:</p>
|
|
<pre>
|
|
bool ExampleCounter::init()
|
|
{
|
|
|
|
initPVRecord();
|
|
epics::pvData::PVFieldPtr pvField;
|
|
pvValue = getPVStructure()->getLongField("value");
|
|
if(pvValue.get()==NULL) return false;
|
|
return true;
|
|
}
|
|
</pre>
|
|
This:
|
|
<ul>
|
|
<li>Calls initRecord which is implemented by the base class.
|
|
It MUST be called.</li>
|
|
<li>Calls getLongField to get the interface to the value field,
|
|
which must be a scalar with type long.</li>
|
|
<li>If a long value field was not found it returns false.</li>
|
|
<li>Returns true</li>
|
|
</ul>
|
|
<p>The implementation of process is:</p>
|
|
<pre>
|
|
void ExampleCounter::process()
|
|
{
|
|
pvValue->put(pvValue->get() + 1.0);
|
|
timeStamp.getCurrent();
|
|
pvTimeStamp.set(timeStamp);
|
|
}
|
|
</pre>
|
|
It adds 1.0 to the current value.
|
|
It then sets the timeStamp to the current time.
|
|
<h4>exampleCounterMain.cpp</h4>
|
|
<p><b>NOTE:</b>
|
|
This is a shorter version of the actual code.
|
|
It shows the essential code.
|
|
The actual example shows how create an additional record.
|
|
</p>
|
|
<p>The main program is:</p>
|
|
<pre>
|
|
int main(int argc,char *argv[])
|
|
{
|
|
PVDatabasePtr master = PVDatabase::getMaster();
|
|
ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
|
|
String recordName("exampleCounter");
|
|
PVRecordPtr pvRecord = ExampleCounter::create(recordName);
|
|
bool result = master->addRecord(pvRecord);
|
|
cout << "result of addRecord " << recordName << " " << result << endl;
|
|
pvRecord.reset();
|
|
startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
|
|
cout << "exampleCounter\n";
|
|
string str;
|
|
while(true) {
|
|
cout << "Type exit to stop: \n";
|
|
getline(cin,str);
|
|
if(str.compare("exit")==0) break;
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
</pre>
|
|
This:
|
|
<ul>
|
|
<li>Gets a pointer to the master database.</li>
|
|
<li>Creates the local Channel Provider. This starts the pvAccess server.</li>
|
|
<li>Creates a ExampleCounter record with the name exampleCounter
|
|
</li>
|
|
<li>Prints exampleCounter on standard out.</li>
|
|
<li>Runs forever until the user types exit on standard in.</li>
|
|
</ul>
|
|
<h4>v3IOC exampleCounter</h4>
|
|
<p>This has two subdirectories:
|
|
<dl>
|
|
<dt>Db</dt>
|
|
<dd>This has a template for a single v3Record.</dd>
|
|
<dt>src</dt>
|
|
<dd>This has code to allow exampleCounter to reside in a v3IOC.</dd>
|
|
</dl>
|
|
</p>
|
|
<p>The src directory has the following components:</p>
|
|
<dl>
|
|
<dt>exampleCounterMain.cpp</dt>
|
|
<dd>This is just a standard Main for a v3IOC.</dd>
|
|
<dt>exampleCounterInclude.dbd</dt>
|
|
<dd>This is:
|
|
<pre>
|
|
include "base.dbd"
|
|
include "PVAServerRegister.dbd"
|
|
registrar("exampleCounterRegister")
|
|
</pre>
|
|
This includes the dbd components required from base,
|
|
the dbd file for starting pvAccess and the local channelProvider,
|
|
and the dbd file for the example. The later is for the code from the
|
|
next file.
|
|
</dd>
|
|
<dt>exampleCounter.cpp</dt>
|
|
<dd>This is the code that registers a command the can be issued
|
|
via the iocsh, which is the console for a v3IOC.
|
|
The example supports a single command:
|
|
<pre>
|
|
exampleCounterCreateRecord recordName
|
|
</pre>
|
|
</dd>
|
|
</dl>
|
|
<h3>Phased Development</h3>
|
|
<p>This documentation describes the first phase of a phased implementation of pvDatabaseCPP:</pp>
|
|
<dl>
|
|
<dt>pvRecord</d>
|
|
<dd>Wrapper on PVStructure that implements methods required by Local Channel Provider.</dd>
|
|
<dt>pvDatabase</d>
|
|
<dd>Database of PVRecords. Has methods find, add, and remove.</dd>
|
|
<dt>Local Channel Provider</dt>
|
|
<dd>Complete implementation of ChannelProvider and Channel.
|
|
This means that pvCopy and monitor are also implemented.</dd>
|
|
</dl>
|
|
<p>Future phases of pvDatabaseCPP might include:</p>
|
|
<dl>
|
|
<dt>Install</dt>
|
|
<dd>This provides complete support for on-line add and delete
|
|
of sets of records.
|
|
With the first phase each "service" is responsible for it's own implementation.
|
|
All that is provided is addRecord and removeRecord.
|
|
</dd>
|
|
<dt>Field support</dt>
|
|
<dd>Add the ability to optionally add support to fields.
|
|
In addition some of the basic support defined in pvIOCJava could also be implemented.</dd>
|
|
<dt>XML parser</dt>
|
|
<dd>This provides the ability to create record instances without writing any code.</dd>
|
|
</dl>
|
|
<p>The completion of each phase provides useful features that can be used without waiting for the
|
|
completion of later phases.
|
|
The rest of this document discusses only the first phase.</p>
|
|
<h3>Features Required for localChannelProvider</h3>
|
|
<dl>
|
|
<dt>pvCopy</dt>
|
|
<dd>Creates a PVStructure that contains a copy of an arbitary
|
|
subset of the fields of another top level PVStructure.
|
|
It can copy data between the two and maintains a bitSet that show
|
|
which fields are changed.<dd>
|
|
<dt>monitor</dt>
|
|
<dd>This provides the ability to monitor changes to fields of a record.</dd>
|
|
<dt>PVRecord and PVDatabase</dt>
|
|
<dd>Defined below.</dd>
|
|
<dt>The localChannelProvider itself</dt>
|
|
<dd>This is the pvAccess package in pvIOCJava.
|
|
The localChannelProvider will access data from PVRecords.
|
|
It will implement all channel methods.</dd>
|
|
</dl>
|
|
<h3>Minumum Features Required for pvRecord</h3>
|
|
<p>The first phase will only implement record processing, i. e.
|
|
the process method has to do everything itself without any generic field support.
|
|
This will be sufficient for starting to implement services.
|
|
The following are the minimium features required</p>
|
|
<dl>
|
|
<dt>PVDatabase</dt>
|
|
<dd>This holds a set of PVRecords. It has methods to find, add, and remove records.</dd>
|
|
<dt>PVRecord</dt>
|
|
<dd>This, and a set of related interfaces, provides the following:
|
|
<dl>
|
|
<dt>Access to top level PVStructure</dt>
|
|
<dd>PVRecord is a wrapper on a top level pvStructure.</dd>
|
|
<dt>Record locking</dt>
|
|
<dd>A record can be locked and unlocked.
|
|
A record must be locked whenever data in the pvStructure is accessed.</dd>
|
|
<dt>Trapping data changes</dt>
|
|
<dd>A client can request to be notified when data in the pvStructure is modified.
|
|
It can do this on a field by field basis.</dd>
|
|
</dl>
|
|
</dd>
|
|
</dl>
|
|
<p>The following sections describes the classes required for the first phase.</p>
|
|
|
|
<h2>database</h2>
|
|
<p>This directory has the following files:</p>
|
|
<dl>
|
|
<dt>pvDatabase.h</dt>
|
|
<dd>
|
|
This is what is described in this section.
|
|
</dd>
|
|
<dt>pvDatabase.cpp</dt>
|
|
<dd>
|
|
The implementation of PVDatabase.
|
|
</dd>
|
|
<dt>pvRecord.cpp</dt>
|
|
<dd>
|
|
The implementation of the base class for PVRecord.
|
|
It can also implement record instances with a process
|
|
method does nothing.
|
|
This can be used to create a "dumb" record where all changes are
|
|
done by clients.
|
|
</dd>
|
|
<dt>exampleCounter.h</dt>
|
|
<dd>
|
|
This was described in the introduction.
|
|
</dd>
|
|
<dt>powerSupplyRecordTest.h</dt>
|
|
<dd>
|
|
This provides code that simulates a power supply.
|
|
It computes the current from the voltage and power.
|
|
It is used for testing.
|
|
The complete implementation is provided in the header file.
|
|
Thus code will be generated only if other code includes the
|
|
header file and creates a record instance.
|
|
</dd>
|
|
<dt>recordList.h</dt>
|
|
<dd>This implements a PVRecord that provides a list of the names
|
|
of the records in the PVDatabase.
|
|
It also serves as an example of how to implement a service.
|
|
The testExampleServer creates an instance via the following code:
|
|
<pre>
|
|
recordName = "laptoprecordListPGRPC";
|
|
pvRecord = RecordListRecord::create(recordName);
|
|
result = master->addRecord(pvRecord);
|
|
</pre>
|
|
</dd>
|
|
<dt>traceRecord.h</dt>
|
|
<dd>This implements a PVRecord that can set the trace level for
|
|
another record. See below for a discussion of trace level.</dd>
|
|
|
|
</dl>
|
|
<p>The classes in pvDatabase.h describe a database of memory resident
|
|
smart records.
|
|
It describes the following classes:</p>
|
|
<dl>
|
|
<dt>PVRecord</dt>
|
|
<dd>This provides the methods required by localChannelProvider to implement Channel.</dd>
|
|
<dt>PVRecordField</dt>
|
|
<dt>PVRecordStructure</dt>
|
|
<dd>These wrap PVField and PVStructure so that pvCopy and monitor
|
|
can be implemented.</dd>
|
|
<dt>PVRecordClient</dt>
|
|
<dd>This is called by anything that acceses PVRecord.</dd>
|
|
<dt>PVListener</dt>
|
|
<dd>This is implemented by anything that wants to trap calls to PVRecord::message.</dd>
|
|
<dt>PVDatabase</dt>
|
|
<dd>This is a database of PVRecords.</dd>
|
|
</dl>
|
|
<p>Each class is described in a separate subsection.</p>
|
|
<h3>C++ namespace and typedefs</h3>
|
|
<pre>
|
|
namespace epics { namespace pvDatabase {
|
|
|
|
class PVRecord;
|
|
typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
|
|
typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
|
|
|
|
class PVRecordField;
|
|
typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
|
|
typedef std::vector<PVRecordFieldPtr> PVRecordFieldPtrArray;
|
|
typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;
|
|
|
|
class PVRecordStructure;
|
|
typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;
|
|
|
|
class PVRecordClient;
|
|
typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
|
|
|
|
class PVListener;
|
|
typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
|
|
|
|
class RecordPutRequester;
|
|
typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;
|
|
|
|
class PVDatabase;
|
|
typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
|
|
</pre>
|
|
|
|
<h3>class PVRecord</h3>
|
|
<p>NOTES:
|
|
<ul>
|
|
<li>This section uses the name record instead of "an instance of PVRecord".</li>
|
|
<li>Most clients will access a record via the local channel provider,
|
|
i. e. via pvAccess.
|
|
Thus this section is mainly of interest to
|
|
the local channel provider and record implementers.</li>
|
|
<li>Most readers will not care about most of the PVRecord methods.
|
|
Most of the methods are used by the pvAccess code.
|
|
Service implementers will mostly be interested in methods init and process.
|
|
These are described first.
|
|
</ul>
|
|
<hr>PVRecord Methods</h4>
|
|
<pre>
|
|
class PVRecord
|
|
public epics::pvData::Requester,
|
|
public std::tr1::enable_shared_from_this<PVRecord>
|
|
{
|
|
public:
|
|
POINTER_DEFINITIONS(PVRecord);
|
|
|
|
virtual bool init() {initPVRecord(); return true;}
|
|
virtual void process() {}
|
|
virtual void destroy();
|
|
|
|
static PVRecordPtr create(
|
|
epics::pvData::String const & recordName,
|
|
epics::pvData::PVStructurePtr const & pvStructure);
|
|
virtual ~PVRecord();
|
|
epics::pvData::String getRecordName();
|
|
PVRecordStructurePtr getPVRecordStructure();
|
|
PVRecordFieldPtr findPVRecordField(
|
|
epics::pvData::PVFieldPtr const & pvField);
|
|
bool addRequester(epics::pvData::RequesterPtr const & requester);
|
|
bool removeRequester(epics::pvData::RequesterPtr const & requester);
|
|
inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
|
|
void lock();
|
|
void unlock();
|
|
bool tryLock();
|
|
void lockOtherRecord(PVRecordPtr const & otherRecord);
|
|
bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
|
|
bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
|
|
void detachClients();
|
|
bool addListener(PVListenerPtr const & pvListener);
|
|
bool removeListener(PVListenerPtr const & pvListener);
|
|
void beginGroupPut();
|
|
void endGroupPut();
|
|
epics::pvData::String getRequesterName() {return getRecordName();}
|
|
virtual void message(
|
|
epics::pvData::String const & message,
|
|
epics::pvData::MessageType messageType);
|
|
void message(
|
|
PVRecordFieldPtr const & pvRecordField,
|
|
epics::pvData::String const & message,
|
|
epics::pvData::MessageType messageType);
|
|
void toString(epics::pvData::StringBuilder buf);
|
|
void toString(epics::pvData::StringBuilder buf,int indentLevel);
|
|
int getTraceLevel();
|
|
void setTraceLevel(int level);
|
|
protected:
|
|
PVRecord(
|
|
epics::pvData::String const & recordName,
|
|
epics::pvData::PVStructurePtr const & pvStructure);
|
|
void initPVRecord();
|
|
epics::pvData::PVStructurePtr getPVStructure();
|
|
PVRecordPtr getPtrSelf()
|
|
{
|
|
return shared_from_this();
|
|
}
|
|
private:
|
|
...
|
|
}
|
|
</pre>
|
|
<p>The methods are:</p>
|
|
<dl>
|
|
<dt>init</dt>
|
|
<dd>Virtual method.
|
|
Derived classes must implement this method.
|
|
This method Must call initPVRecord.
|
|
</dd>
|
|
<dt>process</dt>
|
|
<dd>Virtual method.
|
|
Derived classes must implement this method.
|
|
The base implementation does nothing.
|
|
</dd>
|
|
<dt>destroy</dt>
|
|
<dd>This is a virtual method.
|
|
A derived class must call the base class destroy method after it
|
|
has released any resources it uses.</dd>
|
|
<dt>create</dt>
|
|
<dd>Static method to create dumb records,
|
|
i.e. records with a process method that does nothing.
|
|
A derived class should have it';s own static create method.
|
|
</dd>
|
|
<dt>~PVRecord</dt>
|
|
<dd>The destructor which must be virtual. A derived class must also have
|
|
a virtual destructor.</dd>
|
|
<dt>getRecordName</dt>
|
|
<dd>Return the recordName.</dd>
|
|
<dt>getPVRecordStructure</dt>
|
|
<dd>Get the top level PVStructure.</dd>
|
|
<dt>findPVRecordField</dt>
|
|
<dd>Given a PVFieldPtr return the PVRecordFieldPtr for the field.</dd>
|
|
<dt>addRequester</dt>
|
|
<dd>Add a requester to receive messages.</dd>
|
|
<dt>removeRequester</dt>
|
|
<dd>Remove a message requester.</dd>
|
|
<dt>lock_guard</dt>
|
|
<dd>This is an inline method that locks the record. The record will automatically
|
|
be unlocked when control leaves the block that has the call.
|
|
<dt>lock</dt>
|
|
<dt>unlock</dt>
|
|
<dd>Lock and Unlock the record.
|
|
Any code accessing the data in the record or calling other PVRecord methods
|
|
must have the record locked.</dd>
|
|
<dt>tryLock</dt>
|
|
<dd>If true then just like lock.
|
|
If falseclient can not access record.
|
|
A client can try to simultaneously hold the lock for more than two records
|
|
by calling this method. But must be willing to accept failure.
|
|
</dd>
|
|
<dt>lockOtherRecord</dt>
|
|
<dd>A client that holds the lock for one record can lock one other record.
|
|
A client must not call this if the client already has the lock for
|
|
more then one record.
|
|
</dd>
|
|
<dt>addPVRecordClient</dt>
|
|
<dd>Every client that accesses the record must call this so that the client can be notified when the record is deleted.</dd>
|
|
<dt>removePVRecordClient</dt>
|
|
<dd>Client is no longer accessing the record.</dd>
|
|
<dt>detachClients</dt>
|
|
<dd>Ask all clients to detach from the record</dd>
|
|
<dt>addListener</dt>
|
|
<dd>Add a PVListener. This must be called before calling pvRecordField.addListener.</dd>
|
|
<dt>removeListener</dt>
|
|
<dd>Removes a listener. The listener will also be removed from all fields to which it is attached.</dd>
|
|
<dt>beginGroupPut</dt>
|
|
<dd>Begin a group of puts.
|
|
This results in all registered PVListeners being called</dd>
|
|
<dt>endGroupPut</dt>
|
|
<dd>End a group of puts.
|
|
This results in all registered PVListeners being called.</dd>
|
|
<dt>getRequesterName</dt>
|
|
<dd>virtual method of Requester
|
|
</dd>
|
|
<dt>message</dt>
|
|
<dd>Can be called by implementation code.
|
|
The message will be sent to every requester.</dd>
|
|
<dt>toString</dt>
|
|
<dd>Just calls the top level PVStructure toString method.</dd>
|
|
<dt>getTraceLevel</dt>
|
|
<dd>This can be used for debugging. There are currently three
|
|
levels that are used by existing code.
|
|
<dl>
|
|
<dt>0</dt>
|
|
<dd>Produce no trace messages.</dd>
|
|
<dt>1</dt>
|
|
<dd>Issue a message to std::cout whenever anything is created
|
|
or destroyed.</dd>
|
|
<dt>2</dt>
|
|
<dd>In addition to lifetime messages also issue a message
|
|
whenever the record is accessed by pvAccess client.</dd>
|
|
</dl>
|
|
</dd>
|
|
<dt>setTraceLevel</dt>
|
|
<dd>Set the trace level. Note that special, described below.
|
|
provides a record support that allows a pvAccess client
|
|
to set the trace level of a record.</dd>
|
|
</dl>
|
|
<p>The protected methods are:</p>
|
|
<dl>
|
|
<dt>PVRecord</dt>
|
|
<dd>The constructor. It requires a recordName and a top level PVStructure.</dd>
|
|
<dt>initPVRecord</dt>
|
|
<dd>This method must be called by derived class.</dd>
|
|
<dt>getPVStructure</dt>
|
|
<dd>Called by derived class.</dd>
|
|
</dl>
|
|
<h3>class PVRecordField</h3>
|
|
<pre>
|
|
class PVRecordField {
|
|
public virtual epics::pvData::PostHandler,
|
|
public std::tr1::enable_shared_from_this<PVRecordField>
|
|
public:
|
|
POINTER_DEFINITIONS(PVRecordField);
|
|
PVRecordField(
|
|
epics::pvData::PVFieldPtr const & pvField,
|
|
PVRecordStructurePtr const &parent,
|
|
PVRecordPtr const & pvRecord);
|
|
virtual ~PVRecordField();
|
|
virtual void destroy();
|
|
PVRecordStructurePtr getParent();
|
|
epics::pvData::PVFieldPtr getPVField();
|
|
epics::pvData::String getFullFieldName();
|
|
epics::pvData::String getFullName();
|
|
PVRecordPtr getPVRecord();
|
|
bool addListener(PVListenerPtr const & pvListener);
|
|
virtual void removeListener(PVListenerPtr const & pvListener);
|
|
virtual void postPut();
|
|
virtual void message(
|
|
epics::pvData::String const & message,
|
|
epics::pvData::MessageType messageType);
|
|
protected:
|
|
PVRecordFieldPtr getPtrSelf()
|
|
{
|
|
return shared_from_this();
|
|
}
|
|
virtual void init();
|
|
virtual void postParent(PVRecordFieldPtr const & subField);
|
|
virtual void postSubField();
|
|
private:
|
|
...
|
|
};
|
|
</pre>
|
|
<p>When PVRecord is created it creates a PVRecordField for every field in the PVStructure
|
|
that holds the data. It has the following methods:
|
|
</p>
|
|
|
|
<dl>
|
|
<dt>PVRecordField</dt>
|
|
<dd>The constructor.</dd>
|
|
<dt>~PVRecordField</dt>
|
|
<dd>The destructor.</dd>
|
|
<dt>destroy</dt>
|
|
<dd>Called by PVRecordStructure when it's destroy method is called.</dd>
|
|
<dt>getParent</dt>
|
|
<dd>Get the parent PVRecordStructure for this field.</dd>
|
|
<dt>getPVField</dt>
|
|
<dd>Get the PVField associated with this PVRecordField.</dd>
|
|
<dt>getFullFieldName</dt>
|
|
<dd>This gets the full name of the field, i.e. field,field,..</dd>
|
|
<dt>getFullName</dt>
|
|
<dd>This gets recordName plus the full name of the field, i.e. recordName.field,field,..</dd>
|
|
<dt>getPVRecord</dt>
|
|
<dd>Returns the PVRecord to which this field belongs.</dd>
|
|
<dt>addListener</dt>
|
|
<dd>Add A PVListener to this field.
|
|
Whenever this field or any subfield if this field is modified the listener will be notified.
|
|
PVListener is described below.
|
|
Before a listener can call addListener it must first call PVRecord.registerListener.</dd>
|
|
<dt>removeListener</dt>
|
|
<dd>Remove a PVListener.</dd>
|
|
<dt>postPut</dt>
|
|
<dd>This is called by the code that implements the data interface.
|
|
It is called whenever the put method is called.</dd>
|
|
<dt>message</dt>
|
|
<dd>Called by implementation code. It calls PVRecord::message after prepending the full
|
|
fieldname.</dd>
|
|
</dl>
|
|
<h3>class PVRecordStructure</h3>
|
|
<pre>
|
|
class PVRecordStructure : public PVRecordField {
|
|
public:
|
|
POINTER_DEFINITIONS(PVRecordStructure);
|
|
PVRecordStructure(
|
|
epics::pvData::PVStructurePtr const & pvStructure,
|
|
PVRecordFieldPtrArrayPtr const & pvRecordField);
|
|
virtual ~PVRecordStructure();
|
|
virtual void destroy();
|
|
PVRecordFieldPtrArrayPtr getPVRecordFields();
|
|
epics::pvData::PVStructurePtr getPVStructure();
|
|
virtual void removeListener(PVListenerPtr const & pvListener);
|
|
virtual void postPut();
|
|
protected:
|
|
virtual void init();
|
|
private:
|
|
...
|
|
};
|
|
</pre>
|
|
<p>When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure
|
|
that holds the data. It has the following methods:
|
|
</p>
|
|
<dl>
|
|
<dt>PVRecordStructure</dt>
|
|
<dd>The constructor.</dd>
|
|
<dt>~PVRecordStructure</dt>
|
|
<dd>The destructor.</dd>
|
|
<dt>getPVRecordFields</dt>
|
|
<dd>Get the PVRecordField array for the subfields</dd>
|
|
<dt>getPVStructure</dt>
|
|
<dd>Get the PVStructure for this field.</dd>
|
|
<dt>removeListener</dt>
|
|
<dd>Remove a PVListener.</dd>
|
|
<dt>postPut</dt>
|
|
<dd>This is called by the code that implements the data interface.
|
|
It is called whenever the put method is called.</dd>
|
|
<h3>class PVRecordClient</h3>
|
|
<pre>
|
|
class PVRecordClient {
|
|
POINTER_DEFINITIONS(PVRecordClient);
|
|
virtual ~PVRecordClient();
|
|
virtual void detach(PVRecordPtr const & pvRecord);
|
|
};
|
|
</pre>
|
|
<p>where</p>
|
|
<dl>
|
|
<dt>~PVRecordClient</dt>
|
|
<dd>The destructor.</dd>
|
|
<dt>detach</dt>
|
|
<dd>The record is being removed from the master database,</dd>
|
|
</dl>
|
|
</dl>
|
|
<h3>class PVListener</h3>
|
|
<pre>
|
|
class PVListener {
|
|
virtual public PVRecordClient
|
|
public:
|
|
POINTER_DEFINITIONS(PVListener);
|
|
virtual ~PVListener();
|
|
virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
|
|
virtual void dataPut(
|
|
PVRecordStructurePtr const &
|
|
requested,PVRecordFieldPtr const & pvRecordField) = 0;
|
|
virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
|
|
virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
|
|
virtual void unlisten(PVRecordPtr const & pvRecord);
|
|
};
|
|
</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 record is being destroyed. The listener must release all
|
|
access to the record.</dd>
|
|
</dl>
|
|
<h3>class PVDatabase</h3>
|
|
<pre>
|
|
class PVDatabase : virtual public epics::pvData::Requester {
|
|
public:
|
|
POINTER_DEFINITIONS(PVDatabase);
|
|
static PVDatabasePtr getMaster();
|
|
virtual ~PVDatabase();
|
|
virtual void destroy();
|
|
PVRecordPtr findRecord(epics::pvData::String const& recordName);
|
|
bool addRecord(PVRecordPtr const & record);
|
|
epics::pvData::PVStringArrayPtr getRecordNames();
|
|
bool removeRecord(PVRecordPtr const & record);
|
|
virtual epics::pvData::String getRequesterName();
|
|
virtual void message(
|
|
epics::pvData::String const &message,
|
|
epics::pvData::MessageType messageType);
|
|
private:
|
|
PVDatabase();
|
|
};
|
|
</pre>
|
|
<p>where</p>
|
|
<dl>
|
|
<dt>getMaster</dt>
|
|
<dd>Get the master database. This is the database that localChannelProvider access.</dd>
|
|
<dt>~PVDatabase</dt>
|
|
<dd>The destructor.</dd>
|
|
<dt>destroy</dt>
|
|
<dd>This is called by remote channelAccess when process exits.
|
|
This destroys and removes all records in the PVDatabase.</dd>
|
|
<dt>findRecord</dt>
|
|
<dd>Find a record. An empty pointer is returned if the record is not in the database.</dd>
|
|
<dt>addRecord</dt>
|
|
<dd>Add a record to the database.
|
|
If the record already exists it is not modified and false is returned.</dd>
|
|
<dt>getRecordNames</dt>
|
|
<dd>Returns an array of all the record names.</dd>
|
|
<dt>removeRecord</dt>
|
|
<dd>Remove a record from the database.
|
|
If the record was not in the database false is returned.</dd>
|
|
<dt>getRequesterName</dt>
|
|
<dd>Virtual method of Requester</dd>
|
|
<dt>message</dt>
|
|
<dd>Virtual message of Requester.</dd>
|
|
</dl>
|
|
<h2>pvAccess</h2>
|
|
<p>This is code that provides an implementation of channelProvider as
|
|
defined by pvAccess.
|
|
It provides access to PVRecords and is access by the server side of remote pvAccess.</p>
|
|
<h3>channelProviderLocal</h3>
|
|
<p>This is a complete implementation of channelProvider and ,
|
|
except for channelRPC, provides a complete implementation of Channel
|
|
as defined by pvAccess.
|
|
For monitors it calls the code described in the following sections.</p>
|
|
<h3>pvCopy</h3>
|
|
<p>This provides code that creates a top level PVStructure that is an arbitrary
|
|
subset of the fields in the PVStructure from a PVRecord.
|
|
In addition it provides code that monitors changes to the fields in a PVRecord.
|
|
A client configures the desired set of subfields and monitoring options
|
|
via a pvRequest structure.
|
|
pvAccess provides a class CreatePVRequest that creates a pvRequest.
|
|
The pvCopy code provides the same functionality as the pvCopy code in pvIOCJava.
|
|
</p>
|
|
<h3>monitorAlgorithm</h3>
|
|
<p>Currently all that is implemented is a header file.
|
|
The only algorithm currently implemented is <b>onPut</b>
|
|
</p>
|
|
<h3>monitorFactory</h3>
|
|
<h4>Overview</h4>
|
|
<p><b>epics::pvData::monitor</b> defines the monitor interfaces
|
|
as seen by a client.
|
|
See
|
|
<a href="http://epics-pvdata.sourceforge.net/docbuild/pvDatabaseCPP/tip/documentation/pvDatabaseCPP.html">pvDatabaseCPP.html</a>
|
|
For details.</p>
|
|
<p>
|
|
monitorFactory implements the
|
|
monitoring interfaces for a PVRecord.
|
|
It implements queueSize=0 and queueSize>=2.
|
|
</p>
|
|
|
|
<p>
|
|
The implementation uses PVCopy and PVCopyMonitor which are implemented in pvCopy.
|
|
When PVCopyMonitor tells monitor that changes
|
|
have occurred, monitor applies the appropriate algorithm to each changed field.</p>
|
|
|
|
<p>Currently only algorithm <b>onPut</b> is implemented but,
|
|
like pvIOCJava there are plans to support for the following monitor algorithms:</p>
|
|
<dl>
|
|
<dt>onPut</dt>
|
|
<dd>A monitor is issued whenever a put is issued to the field. This is the
|
|
default unless the record defines deadbands for a field. An exception is
|
|
the top level timeStamp which by default is made onChange and monitor
|
|
will not be raised.</dd>
|
|
<dt>onChange</dt>
|
|
<dd>This provides two options: 1) A monitor is raised whenever a field
|
|
changes value, and 2) A monitor will never be raised for the field.</dd>
|
|
<dt>deadband</dt>
|
|
<dd>The field must be a numeric scalar. Whenever the absolute or percentage
|
|
value of the field changes by more than a deadband a monitor is issued.
|
|
The record instance can also define deadbands.</dd>
|
|
<dt>periodic</dt>
|
|
<dd>A monitor is issued at a periodic rate if a put was issued to any field
|
|
being monitored.</dd>
|
|
</dl>
|
|
<h4>MonitorFactory</h4>
|
|
<p>MonitorFactory provides the following methods:</p>
|
|
<pre>class MonitorFactory
|
|
{
|
|
static MonitorPtr create(
|
|
PVRecordPtr const & pvRecord,
|
|
MonitorRequester::shared_pointer const & monitorRequester,
|
|
PVStructurePtr const & pvRequest);
|
|
static void registerMonitorAlgorithmCreater(
|
|
MonitorAlgorithmCreatePtr const & monitorAlgorithmCreate,
|
|
String const & algorithmName);
|
|
}</pre>
|
|
|
|
<p>where</p>
|
|
<dl>
|
|
<dt>create</dt>
|
|
<dd>Create a monitor. The arguments are:
|
|
<dl>
|
|
<dt>pvRecord</dt>
|
|
<dd>The record being monitored.</dd>
|
|
<dt>monitorRequester</dt>
|
|
<dd>The monitor requester. This is the code to which monitot events
|
|
will be delivered.</dd>
|
|
<dt>pvRequest</dt>
|
|
<dd>The request options</dd>
|
|
</dl>
|
|
</dd>
|
|
<dt>registerMonitorAlgorithmCreater</dt>
|
|
<dd>Called by code that implements a monitor algorithm.</dd>
|
|
</dl>
|
|
<h2>special</h2>
|
|
<p>This section provides two useful record support modules
|
|
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:
|
|
<pre>
|
|
traceRecord
|
|
structure arguments
|
|
string recordName
|
|
int level 0
|
|
structure result
|
|
string status
|
|
</pre>
|
|
where:
|
|
<dl>
|
|
<dt>recordName</dt>
|
|
<dd>The name of the record to set the trace level.</dd>
|
|
<dt>level</dt>
|
|
<dd>The level to set. The meaning is:
|
|
<dl>
|
|
<dt>0</dt>
|
|
<dd>No trace messages generated</dd>
|
|
<dt>1</dt>
|
|
<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
|
|
for each get and put request.</dd>
|
|
<dt>>2</dt>
|
|
<dd>Currently no definition</dd>
|
|
</dl>
|
|
</dd>
|
|
<dt>result</dt>
|
|
<dd>The result of a cannelPutGet request</dd>
|
|
</dl>
|
|
<p>testExampleServerMain.cpp has an example of how to create a traceRecord:
|
|
<pre>
|
|
PVDatabasePtr master = PVDatabase::getMaster();
|
|
PVRecordPtr pvRecord;
|
|
String recordName;
|
|
bool result(false);
|
|
recordName = "traceRecordPGRPC";
|
|
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 set
|
|
the trace level of a record. It follows the pattern of a channelPutGet
|
|
record:
|
|
<pre>
|
|
traceRecord
|
|
structure arguments
|
|
string database master
|
|
string regularExpression .*
|
|
structure result
|
|
string status
|
|
string[] names
|
|
</pre>
|
|
where:
|
|
<dl>
|
|
<dt>database</dt>
|
|
<dd>The name of the datbase. The default is "master"</dd>
|
|
<dt>regularExpression</dt>
|
|
<dd>For now this is ignored and the complete list of names is always
|
|
returned.</dd>
|
|
<dt>status</dt>
|
|
<dd>The status of a putGet request.</dd>
|
|
<dt>names</dt>
|
|
<dd>The list of record names.</dd>
|
|
</dl>
|
|
<p>Note that swtshell has a command <b>channelList</b> that
|
|
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:
|
|
<pre>
|
|
recordName = "laptoprecordListPGRPC";
|
|
pvRecord = RecordListRecord::create(recordName);
|
|
result = master->addRecord(pvRecord);
|
|
if(!result) cout<< "record " << recordName << " not added" << endl;
|
|
</pre>
|
|
<h3>powerSupplyRecordTest</h3>
|
|
<p>This simulates a simple power supply record.
|
|
It is used for testing.</p>
|
|
|
|
</div>
|
|
</body>
|
|
</html>
|