more work in examples; documentation is now up to date
This commit is contained in:
+199
-249
@@ -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, 06-Feb-2014</h2>
|
||||
<h2 class="nocount">EPICS v4 Working Group, Working Draft, 07-Feb-2014</h2>
|
||||
<dl>
|
||||
<dt>Latest version:</dt>
|
||||
<dd><a
|
||||
@@ -46,11 +46,11 @@
|
||||
</dd>
|
||||
<dt>This version:</dt>
|
||||
<dd><a
|
||||
href= "pvDatabaseCPP_20131121.html">pvDatabaseCPP20131121.html</a>
|
||||
href= "pvDatabaseCPP_20140207.html">pvDatabaseCPP20140207.html</a>
|
||||
</dd>
|
||||
<dt>Previous version:</dt>
|
||||
<dd><a
|
||||
href= "pvDatabaseCPP_20131120.html">pvDatabaseCPP20131120.html</a>
|
||||
href= "pvDatabaseCPP_20131121.html">pvDatabaseCPP20131121.html</a>
|
||||
</dd>
|
||||
<dt>Editors:</dt>
|
||||
<dd>Marty Kraimer, BNL</dd>
|
||||
@@ -79,7 +79,7 @@ V4 control system programming environment:<br />
|
||||
|
||||
<h2 class="nocount">Status of this Document</h2>
|
||||
|
||||
<p>This is the 06-Feb-2014 version of of pvDatabaseCPP.</p>
|
||||
<p>This is the 07-Feb-2014 version of of pvDatabaseCPP.</p>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -117,8 +117,8 @@ two unresolved problems reported in the previous version of this document:
|
||||
<p>Future enhancements in priority order:</p>
|
||||
<dl>
|
||||
<dt>channelArray</dt>
|
||||
<dd>The arguments that have type <b>int</b> should be changed to <b>size_t</b>
|
||||
This will not be done until pvDataCPP-md is merged into pvDataCPP.
|
||||
<dd>The arguments that have type <b>int</b> should be changed to <b>size_t</b>.
|
||||
This requires changes to pvAccessCPP.
|
||||
</dd>
|
||||
<dt>Monitor Algorithms</dt>
|
||||
<dd>Monitor algorithms have not been implemented.
|
||||
@@ -137,7 +137,7 @@ two unresolved problems reported in the previous version of this document:
|
||||
<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.
|
||||
What this project supplies is a complete implementation of the server side of pvAccess.
|
||||
This project supplies is 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,
|
||||
@@ -170,7 +170,8 @@ A record is smart because code can be attached to a record, which is accessed vi
|
||||
<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>
|
||||
This component also provides a C++ implementation
|
||||
of 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 the later case the IOC has both a database of V3 Records
|
||||
@@ -221,7 +222,7 @@ Type exit to stop:
|
||||
<pre>
|
||||
mrk> pvput -r "field(argument.value)" exampleServer World
|
||||
...
|
||||
mrk> pvget -r "record{process=true}field(result.value)" exampleServer
|
||||
mrk> pvget -r "record[process=true]field(result.value)" exampleServer
|
||||
exampleServer
|
||||
structure
|
||||
string value Hello World
|
||||
@@ -232,6 +233,9 @@ mrk>
|
||||
mrk> pwd
|
||||
/home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
|
||||
mrk> ../../bin/linux-x86_64/exampleServer st.cmd
|
||||
</pre>
|
||||
<p>You will see the following:</p>
|
||||
<pre>
|
||||
> envPaths
|
||||
epicsEnvSet("ARCH","linux-x86_64")
|
||||
epicsEnvSet("IOC","exampleServer")
|
||||
@@ -281,10 +285,12 @@ epics>
|
||||
</pre>
|
||||
<p>Just like previously you can then execute a pvput and pvget and see Hello World.
|
||||
</p>
|
||||
<p>The examples, i. e. exampleServer, exampleLink, examplePowerSupple,
|
||||
<p>The examples, i. e. exampleServer, exampleLink, examplePowerSupply,
|
||||
and exampleDatabase, are described in separate sections below.
|
||||
In addition arrayPerformance can be used to measure that performance of big
|
||||
arrays. It is also described in a later section.</p>
|
||||
<p>Reading section exampleServer and looking at it's code is a good way
|
||||
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,
|
||||
@@ -374,22 +380,6 @@ The following are the minimium features required</p>
|
||||
</dd>
|
||||
</dl>
|
||||
<p>The following sections describes the classes required for the first phase.</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 "exampleServer".
|
||||
They will work on the exampleServer with the following differences:
|
||||
<dl>
|
||||
<dt>startExample.zip</dt>
|
||||
<dd>Do NOT use this. Instead run the exampleServer as described above.</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>The exampleService shown below provides an example of using swtshell.</p>
|
||||
|
||||
<h2>Building pvDatabaseCPP</h2>
|
||||
<p>To build pvDatabaseCPP You must provide a file RELEASE.local
|
||||
@@ -458,7 +448,7 @@ mrk> make
|
||||
<dd>Stop the local channel provider</dd>
|
||||
<dt>pvdbl</dt>
|
||||
<dd>Provides a list of all the pvRecords in database <b>master</b>
|
||||
It is similar to the iocsh command <b>dbl</b></dd>
|
||||
</dd>
|
||||
</dl>
|
||||
<p>The client commands are provided via PVAClientRegister.dbd and the other commands
|
||||
via PVAServerRegister.dbd.</p>
|
||||
@@ -1102,7 +1092,7 @@ where:
|
||||
<dt>names</dt>
|
||||
<dd>The list of record names.</dd>
|
||||
</dl>
|
||||
<p>Note that swtshell has a command <b>channelList</b> that
|
||||
<p>Note that swtshell, which is a Java GUI tool, 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>
|
||||
@@ -1116,6 +1106,7 @@ if(!result) cout<< "record " << recordName << " not added" <
|
||||
</p>
|
||||
|
||||
<h2>exampleServer</h2>
|
||||
<h3>Overview</h3>
|
||||
<p>The example implements a simple service that has a top level pvStructure:
|
||||
<pre>
|
||||
structure
|
||||
@@ -1142,9 +1133,11 @@ mrk> pwd
|
||||
/home/hg/pvDatabaseCPP/exampleService
|
||||
mrk> bin/linux-x86_64/exampleService
|
||||
</pre>
|
||||
<h3>Directory Layout</h3>
|
||||
<p>
|
||||
The directory structure is:
|
||||
</pre>
|
||||
The directory layout is:
|
||||
</p>
|
||||
<pre>
|
||||
exampleServer
|
||||
configure
|
||||
ExampleRELEASE.local
|
||||
@@ -1155,9 +1148,8 @@ exampleServer
|
||||
exampleServerInclude.dbd
|
||||
exampleServerMain.cpp
|
||||
exampleServerRegister.cpp
|
||||
example
|
||||
ioc
|
||||
Db
|
||||
dbArray.db
|
||||
...
|
||||
src
|
||||
exampleServerInclude.dbd
|
||||
@@ -1192,24 +1184,35 @@ exampleServerCreateRecord exampleServer
|
||||
</pre>
|
||||
Multiple commands can be issued to create multiple service records.
|
||||
</dd>
|
||||
<dt>example</dt>
|
||||
<dt>ioc</dt>
|
||||
<dd>This is for building a V3 IOC application.</dd>
|
||||
<dt>example/Db</dt>
|
||||
<dt>ioc/Db</dt>
|
||||
<dd>This has template files for creating V3 records.</dd>
|
||||
<dt>example/src/exampleServerInclude.dbd</dt>
|
||||
<dd>The commands to build a V3 database</dd>
|
||||
<dt>example/src/exampleServerMain.cpp</dt>
|
||||
<dd>The source file for running a V3 IOC.</dd>
|
||||
<dt>ioc/src</dt>
|
||||
<dd>The files for running a V3 IOC.</dd>
|
||||
<dt>iocBoot/exampleServer</dt>
|
||||
<dd>A place to start exampleServer as part of a V3IOC.
|
||||
It has a st.cmd file that starts the ioc and also starts pvAccess
|
||||
and the example.</dd>
|
||||
</dl>
|
||||
<h4>exampleServer.h</h4>
|
||||
<p>If only a main program is desired then the directory layout is:</p>
|
||||
<pre>
|
||||
exampleServer
|
||||
configure
|
||||
ExampleRELEASE.local
|
||||
...
|
||||
src
|
||||
exampleServer.h
|
||||
exampleServer.cpp
|
||||
exampleServerMain.cpp
|
||||
</pre>
|
||||
<p>Thus if only a main program is required the directory layout is simple.</p>
|
||||
<p>Also many sites will want to build the src directory in an area
|
||||
separate from where the iocs are build.</p>
|
||||
<h3>exampleServer.h</h3>
|
||||
<p>The example resides in src
|
||||
The implementation is in exampleServer.cpp.
|
||||
A serious implementation might break the code into a header and an
|
||||
implementation file.<p>
|
||||
</p>
|
||||
</p>The description consists of</p>
|
||||
<pre>
|
||||
class ExampleServer;
|
||||
@@ -1343,7 +1346,7 @@ void ExampleServer::process()
|
||||
</pre>
|
||||
It gives a value to result.value and
|
||||
then sets the timeStamp to the current time.
|
||||
<h4>exampleServerMain.cpp</h4>
|
||||
<h3>src/exampleServerMain.cpp</h3>
|
||||
<p><b>NOTE:</b>
|
||||
This is a shorter version of the actual code.
|
||||
It shows the essential code.
|
||||
@@ -1381,7 +1384,7 @@ This:
|
||||
<li>Prints exampleServer on standard out.</li>
|
||||
<li>Runs forever until the user types exit on standard in.</li>
|
||||
</ul>
|
||||
<h4>V3IOC exampleServer</h4>
|
||||
<h3>V3IOC exampleServer</h3>
|
||||
<p>To start exampleServer as part of a V3IOC:
|
||||
<pre>
|
||||
mrk> pwd
|
||||
@@ -1399,18 +1402,13 @@ epics>
|
||||
double01 is a v3Record.
|
||||
exampleServer is a pvRecord.
|
||||
</p>
|
||||
<p>
|
||||
It starts pvASrv so that the V3 records can be accessed via Channel Access
|
||||
or via PVAccess.</p>
|
||||
|
||||
<h2>exampleDatabase</h2>
|
||||
<h3>exampleDatabase</h3>
|
||||
<p>This example provides the exampleCounter record in addition to a number of other PVRecords.
|
||||
Like exampleCounter it can be started either as a standalone main program or as
|
||||
a part of a V3IOC.
|
||||
The exampleServer pvDatabase has many records including the following:</p>
|
||||
<p>The exampleServer pvDatabase has many records including the following:</p>
|
||||
<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>
|
||||
@@ -1425,40 +1423,161 @@ The exampleServer pvDatabase has many records including the following:</p>
|
||||
<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>
|
||||
<p>exampleDatabase can be started as a main program or as a V3 IOIC.
|
||||
If started as a V3 IOC it also has a number of V3 records,
|
||||
and starts pvaSrv so that the V3 records can be accessed via Channel Access
|
||||
or via PVAccess.</p>
|
||||
<h2>exampleLink</h2>
|
||||
<p>This example show how a service can access other PVRecords.
|
||||
This section 1) starts with a discussion of accessing data via pvAccess
|
||||
and 2) gives a brief description of an example that gets data for an array of doubles.</p>
|
||||
<h3>examplePVADoubleArrayGet</h3>
|
||||
examplePVADoubleArrayGet shows how to use pvAccess to get data.</p>
|
||||
<p>The code resides in three directories:</p>
|
||||
<h3>Discussion</h3>
|
||||
<h4>Access Alternatives</h4>
|
||||
<p>The process routine of a PVRecord can access other PVRecords in two ways:</p>
|
||||
<dl>
|
||||
<dt>example/src/examplePVADoubleArrayGet</dt>
|
||||
<dd>This is the implementation code for the example.</dd>
|
||||
<dt>example/examplePVADoubleArrayGet</dt>
|
||||
<dd>This is the code that starts the example as a main program.</dd>
|
||||
<dt>example/V3IOC/examplePVADoubleArrayGet/src</dt>
|
||||
<dd>This is the code for starting the example as part of a V3IOC.</dd>
|
||||
<dt>iocBoot/examplePVADoubleArrayGet</dt>
|
||||
<dd>This has the st.cmd files to start the example.</dd>
|
||||
<dt>Directly accessing local pvDatabase</dt>
|
||||
<dd>
|
||||
If the other PVRecord is accessed via the master PVDatabase then
|
||||
threading issues are up to the implementation.
|
||||
For now this method will not be discussed.</dd>
|
||||
<dt>Access via pvAccess</dt>
|
||||
<dd>
|
||||
If access is via pvAccess then locking is handled by pvAccess.</dd>
|
||||
</dl>
|
||||
<h3>examplePVADoubleArrayGet Implementation</h3>
|
||||
<p>examplePVADoubleArrayGet.h contains the following:</p>
|
||||
<p>Access via pvAccess can be done either by local or remote channel provider.</p>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt>Access via channelProviderLocal</dt>
|
||||
<dd>
|
||||
If the local pvAccess server is used the implementation must be careful that it does not
|
||||
cause deadlocks.
|
||||
When the process method is called the pvRecord for the process method is locked.
|
||||
When it makes a pvAccess get, put, etc request the other record is locked.
|
||||
Thus if a set of pvAccess links are implemented the possibility of deadlocks
|
||||
exists. A simple example is two records that have links to each other.
|
||||
More complex sets are easily created.
|
||||
Unless the developer has complete control of the set of records then remote pvAccess should
|
||||
be used.
|
||||
But this results in more context switches.
|
||||
</dd>
|
||||
<dt>Access via remote pvAccess</dt>
|
||||
<dd>If remote pvAccess is used then all locking issues are handled by pvAccess.
|
||||
The linked channel can be a pvRecord in the local pvDatabase or can be implemented
|
||||
by a remote pvAccess server.</dd>
|
||||
</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 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>
|
||||
If the linked channel is a local pvRecord then,
|
||||
for scalar and structure arrays,
|
||||
raw data is NOT copied for gets.
|
||||
This is because pvData uses shared_vector to hold the raw data.
|
||||
Instead of copying the raw data the reference count is incremented.</p>
|
||||
<p>For puts the linked array will force a new allocation of the raw data in the linked record,
|
||||
i. e. copy on write semantics are enforced. This is done automatically
|
||||
by pvData and not by pvDatabase.</p>
|
||||
<h4>Some details</h4>
|
||||
<p>As mentioned before a pvDatabase server can be either a separate process,
|
||||
i. e. a main program, or can be part of a V3IOC.</p>
|
||||
<p>A main pvDatabase server issues the following calls:</p>
|
||||
<pre>
|
||||
ClientFactory::start();
|
||||
ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
|
||||
...
|
||||
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.
|
||||
These must be called before any code that uses links is initialized.
|
||||
After these two calls there will be two channel providers: <b>local</b>, and <b>pvAccess</b>.
|
||||
|
||||
</p>
|
||||
<p>A pvDatabase that is part of a V3IOC has the following in the st.cmd file.</p>
|
||||
<pre>
|
||||
...
|
||||
class ExamplePVADoubleArrayGet :
|
||||
iocInit()
|
||||
startPVAClient
|
||||
startPVAServer
|
||||
## commands to create pvRecords
|
||||
</pre>
|
||||
<p>
|
||||
Once the client and local provider code has started then the following creates a channel access link.
|
||||
</p>
|
||||
<pre>
|
||||
PVDatabasePtr master = PVDatabase::getMaster();
|
||||
ChannelAccess::shared_pointer channelAccess = getChannelAccess();
|
||||
ChannelProvider::shared_pointer provider = channelAccess->getProvider(providerName);
|
||||
Channel::shared_pointer channel = provider->createChannel(channelName,channelRequester);
|
||||
</pre>
|
||||
|
||||
<h3>Directory Layout</h3>
|
||||
<pre>
|
||||
exampleLink
|
||||
configure
|
||||
ExampleRELEASE.local
|
||||
...
|
||||
src
|
||||
exampleLink.h
|
||||
exampleLink.cpp
|
||||
exampleLinkInclude.dbd
|
||||
exampleLinkRegister.cpp
|
||||
ioc
|
||||
Db
|
||||
src
|
||||
exampleLinkInclude.dbd
|
||||
exampleLinkMain.cpp
|
||||
iocBoot
|
||||
exampleLink
|
||||
st.local
|
||||
st.remote
|
||||
...
|
||||
</pre>
|
||||
<p>This example is only built to be run as part of a V3 IOC.
|
||||
Note that two startup files are available: st.local and st.remote.
|
||||
st.local has two records: doubleArray and exampleLink.
|
||||
doubleArray is a record that can be changed via a call to pvput.
|
||||
exampleLink is a record that, when processed, gets the value from doubleArray and sets its value equal
|
||||
to the value read.
|
||||
st.local has both records.
|
||||
st.remote has only one record named exampleLinkRemote.
|
||||
<p>
|
||||
<p>To start the example:</p>
|
||||
<pre>
|
||||
mrk> pwd
|
||||
/home/hg/pvDatabaseCPP/exampleLink/iocBoot/exampleLink
|
||||
mrk> ../../bin/linux-x86_64/exampleLink st.local
|
||||
</pre>
|
||||
<p>then in another window:</p>
|
||||
<pre>
|
||||
mrk> pvput doubleArray 4 100 200 300 400
|
||||
Old : doubleArray 0
|
||||
New : doubleArray 4 100 200 300 400
|
||||
mrk> pvget -r "record[process=true]field(value)" exampleLink
|
||||
exampleLink
|
||||
structure
|
||||
double[] value [100,200,300,400]
|
||||
mrk>
|
||||
</pre>
|
||||
<h3>exampleLink Implementation</h3>
|
||||
<p>exampleLink.h contains the following:</p>
|
||||
<pre>
|
||||
...
|
||||
class ExampleLink :
|
||||
public PVRecord,
|
||||
public epics::pvAccess::ChannelRequester,
|
||||
public epics::pvAccess::ChannelGetRequester
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ExamplePVADoubleArrayGet);
|
||||
static ExamplePVADoubleArrayGetPtr create(
|
||||
POINTER_DEFINITIONS(ExampleLink);
|
||||
static ExampleLinkPtr create(
|
||||
epics::pvData::String const & recordName,
|
||||
epics::pvData::String const & providerName,
|
||||
epics::pvData::String const & channelName
|
||||
);
|
||||
virtual ~ExamplePVADoubleArrayGet() {}
|
||||
virtual ~ExampleLink() {}
|
||||
virtual void destroy();
|
||||
virtual bool init();
|
||||
virtual void process();
|
||||
@@ -1486,26 +1605,26 @@ to process in order to test the example.</p>
|
||||
<p>All of the initialization is done by a combination of the create and init methods so
|
||||
lets look at them:</p>
|
||||
<pre>
|
||||
ExamplePVADoubleArrayGetPtr ExamplePVADoubleArrayGet::create(
|
||||
ExampleLinkPtr ExampleLink::create(
|
||||
String const & recordName,
|
||||
String const & providerName,
|
||||
String const & channelName)
|
||||
{
|
||||
PVStructurePtr pvStructure = getStandardPVField()->scalarArray(
|
||||
pvDouble,"alarm.timeStamp");
|
||||
ExamplePVADoubleArrayGetPtr pvRecord(
|
||||
new ExamplePVADoubleArrayGet(
|
||||
ExampleLinkPtr pvRecord(
|
||||
new ExampleLink(
|
||||
recordName,providerName,channelName,pvStructure));
|
||||
if(!pvRecord->init()) pvRecord.reset();
|
||||
return pvRecord;
|
||||
}
|
||||
</pre>
|
||||
<p>This first creates a new ExamplePVADoubleArrayGet instance,
|
||||
and then calls the init method and the returns a ExamplePVADoubleArrayGetPtr.
|
||||
<p>This first creates a new ExampleLink instance,
|
||||
and then calls the init method and the returns a ExampleLinkPtr.
|
||||
Note that if init returns false it returns a pointer to NULL.</p>
|
||||
<p>The init method is:</p>
|
||||
<pre>
|
||||
bool ExamplePVADoubleArrayGet::init()
|
||||
bool ExampleLink::init()
|
||||
{
|
||||
initPVRecord();
|
||||
|
||||
@@ -1573,181 +1692,12 @@ bool ExamplePVADoubleArrayGet::init()
|
||||
This a return of true means that it has successfully created a channelGet and is ready
|
||||
to issue gets when process is called.</p>
|
||||
<p>Look at the code for more details.</p>
|
||||
<h4>Starting the example as a main program</h4>
|
||||
<pre>
|
||||
...
|
||||
int main(int argc,char *argv[])
|
||||
{
|
||||
PVDatabasePtr master = PVDatabase::getMaster();
|
||||
ClientFactory::start();
|
||||
ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
|
||||
PVRecordPtr pvRecord;
|
||||
bool result(false);
|
||||
String recordName;
|
||||
PVStructurePtr pvStructure = standardPVField->scalarArray(
|
||||
pvDouble,"alarm,timeStamp");
|
||||
recordName = "doubleArray";
|
||||
pvRecord = PVRecord::create(recordName,pvStructure);
|
||||
result = master->addRecord(pvRecord);
|
||||
if(!result) cout<< "record " << recordName << " not added" << endl;
|
||||
ServerContext::shared_pointer serverContext = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
|
||||
recordName = "examplePVADoubleArrayGet";
|
||||
if(argc>1) recordName = argv[1];
|
||||
String providerName("local");
|
||||
if(argc>2) providerName = argv[2];
|
||||
String channelName("doubleArray");
|
||||
if(argc>3) channelName = argv[3];
|
||||
pvRecord = ExamplePVADoubleArrayGet::create(
|
||||
recordName,providerName,channelName);
|
||||
if(pvRecord!=NULL) {
|
||||
result = master->addRecord(pvRecord);
|
||||
cout << "result of addRecord " << recordName << " " << result << endl;
|
||||
} else {
|
||||
cout << "ExamplePVADoubleArrayGet::create failed" << endl;
|
||||
}
|
||||
string str;
|
||||
while(true) {
|
||||
cout << "Type exit to stop: \n";
|
||||
getline(cin,str);
|
||||
if(str.compare("exit")==0) break;
|
||||
|
||||
}
|
||||
serverContext->shutdown();
|
||||
epicsThreadSleep(1.0);
|
||||
serverContext->destroy();
|
||||
ClientFactory::stop();
|
||||
channelProvider->destroy();
|
||||
return 0;
|
||||
}
|
||||
</pre>
|
||||
<p>The first statements initializes the client factory and localChannelProvider.</p>
|
||||
<p>
|
||||
It then creates a PVRecord that has a double array as the value field.
|
||||
The name of the record is <b>doubleArray</b>.
|
||||
This is the record that can be accessed via pvAccess if the channelName is
|
||||
<b>doubleArray</b>.
|
||||
</p>
|
||||
<p>It then creates a examplePVADoubleArrayGet instance.</p>
|
||||
<p>It the runs forever until the exit is typed.</p>
|
||||
|
||||
<h4>Start the example as part of a V3IOC</h4>
|
||||
The directory example/V3IOC/examplePVADoubleArrayGet has two subdirectories: Db and src.
|
||||
These are like for any other V3IOC application.
|
||||
The Db directory is for a regular ai record.
|
||||
The src directory has code similar to any V3IOC application.
|
||||
In particular it has definitions and code for creating the following iocsh commands:</p>
|
||||
<pre>
|
||||
examplePVADoubleArrayGetRegister recordName providerName channelName
|
||||
</pre>
|
||||
where
|
||||
<dl>
|
||||
<dt>recordName</dt>
|
||||
<dt>providerName</dt>
|
||||
<dt>channelName</dt>
|
||||
</dl>
|
||||
<p>The directory iocBoot/examplePVADoubleArrayGet has two st.cmd files:</p>
|
||||
<dl>
|
||||
<dt>st.local</dt>
|
||||
<dd>This creates the example using localChannelProvider.</dd>
|
||||
<dt>st.remote</dt>
|
||||
<dd>This creates the example using the remote channel provider.</dd>
|
||||
</dl>
|
||||
<h3>Discussion</h3>
|
||||
<h4>Access Alternatives</h4>
|
||||
<p>The process routine of a PVRecord can access other PVRecords in two ways:</p>
|
||||
<dl>
|
||||
<dt>Directly accessing local pvDatabase</dt>
|
||||
<dd>
|
||||
If the other PVRecord is accessed via the master PVDatabase then
|
||||
threading issues are up to the implementation.
|
||||
For now this method will not be discussed.</dd>
|
||||
<dt>Access via pvAccess</dt>
|
||||
<dd>
|
||||
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>
|
||||
If the local pvAccess server is used the implementation must be careful that it does not
|
||||
caused deadlocks.
|
||||
When the process method is called the pvRecord for the process method is locked.
|
||||
When it makes a pvAccess get, put, etc request the other record is locked.
|
||||
Thus if a set of pvAccess links are implemented the possibility of deadlocks
|
||||
exists. A simple example is two records that have links to each other.
|
||||
More complex sets are easily created.
|
||||
Unless the developer has complete control of the set of records then remote pvAccess should
|
||||
be used.
|
||||
But this results in more context switches.
|
||||
</dd>
|
||||
<dt>Access via remote pvAccess</dt>
|
||||
<dd>If remote pvAccess is used then all lockimg issues are handled by pvAccess.
|
||||
The linked channel can be a pvRecord in the local pvDatabase or can be implemented
|
||||
by a remote pvAccess server.</dd>
|
||||
</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 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>
|
||||
If the linked channel is a local pvRecord then,
|
||||
for scalar and structure arrays,
|
||||
raw data is NOT copied for gets.
|
||||
This is because pvData uses shared_vector to hold the raw data.
|
||||
Instead of copying the raw data the reference count is incremented.</p>
|
||||
<p>For puts the linked array will force a new allocation of the raw data in the linked record,
|
||||
i. e. copy on write semantics are enforced. This is done automatically
|
||||
by pvData and not by pvDatabase.</p>
|
||||
<h4>Some details</h4>
|
||||
<p>As mentioned before a pvDatabase server can be either a separate process,
|
||||
i. e. a main program, or can be part of a V3IOC.</p>
|
||||
<p>A main pvDatabase server issues the following calls:</p>
|
||||
<pre>
|
||||
ClientFactory::start();
|
||||
ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
|
||||
...
|
||||
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.
|
||||
These must be called before any code that uses links is initialized.
|
||||
After these two calls there will be two channel providers: <b>local</b>, and <b>pvAccess</b>.
|
||||
|
||||
</p>
|
||||
<p>A pvDatabase that is part of a V3IOC has the following in the st.cmd file.</p>
|
||||
<pre>
|
||||
...
|
||||
iocInit()
|
||||
startPVAClient
|
||||
startPVAServer
|
||||
## commands to create pvRecords
|
||||
</pre>
|
||||
<p>
|
||||
Once the client and local provider code has started then the following creates a channel access link.
|
||||
</p>
|
||||
<pre>
|
||||
PVDatabasePtr master = PVDatabase::getMaster();
|
||||
ChannelAccess::shared_pointer channelAccess = getChannelAccess();
|
||||
ChannelProvider::shared_pointer provider = channelAccess->getProvider(providerName);
|
||||
Channel::shared_pointer channel = provider->createChannel(channelName,channelRequester);
|
||||
</pre>
|
||||
<h2>examplePowerSupply</h2>
|
||||
<dl>
|
||||
<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>
|
||||
</dl>
|
||||
<h3>powerSupplyRecordTest</h3>
|
||||
<p>This simulates a simple power supply record.
|
||||
It is used for testing.</p>
|
||||
<p>This is an example of creating a service that requires a somewhat complicated
|
||||
top level PVStructure.
|
||||
It is similar to the powerSupply example that is provided with pvIOCJava.
|
||||
Look at the code for details.
|
||||
</p>
|
||||
<h2>Array Performance and Memory Example</h2>
|
||||
<p>This section describes main programs that demonstrate performance
|
||||
of large arrays and can also be used to check for memory leaks.
|
||||
|
||||
Reference in New Issue
Block a user