more work in examples; documentation is now up to date

This commit is contained in:
Marty Kraimer
2014-02-07 13:57:32 -05:00
parent 9a798bc05a
commit 61d884334a
72 changed files with 2404 additions and 685 deletions
+199 -249
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, 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&gt; pvput -r "field(argument.value)" exampleServer World
...
mrk&gt; pvget -r "record{process=true}field(result.value)" exampleServer
mrk&gt; pvget -r "record[process=true]field(result.value)" exampleServer
exampleServer
structure
string value Hello World
@@ -232,6 +233,9 @@ mrk&gt;
mrk&gt; pwd
/home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
mrk&gt; ../../bin/linux-x86_64/exampleServer st.cmd
</pre>
<p>You will see the following:</p>
<pre>
&gt; envPaths
epicsEnvSet("ARCH","linux-x86_64")
epicsEnvSet("IOC","exampleServer")
@@ -281,10 +285,12 @@ epics&gt;
</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&gt; 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&lt;&lt; "record " &lt;&lt; recordName &lt;&lt; " not added" &lt
</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&gt; pwd
/home/hg/pvDatabaseCPP/exampleService
mrk&gt; 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&gt; pwd
@@ -1399,18 +1402,13 @@ epics&gt;
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-&gt;getProvider(providerName);
Channel::shared_pointer channel = provider-&gt;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&gt; pwd
/home/hg/pvDatabaseCPP/exampleLink/iocBoot/exampleLink
mrk&gt; ../../bin/linux-x86_64/exampleLink st.local
</pre>
<p>then in another window:</p>
<pre>
mrk&gt; pvput doubleArray 4 100 200 300 400
Old : doubleArray 0
New : doubleArray 4 100 200 300 400
mrk&gt; pvget -r "record[process=true]field(value)" exampleLink
exampleLink
structure
double[] value [100,200,300,400]
mrk&gt;
</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 &amp; recordName,
epics::pvData::String const &amp; providerName,
epics::pvData::String const &amp; 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 &amp; recordName,
String const &amp; providerName,
String const &amp; channelName)
{
PVStructurePtr pvStructure = getStandardPVField()-&gt;scalarArray(
pvDouble,"alarm.timeStamp");
ExamplePVADoubleArrayGetPtr pvRecord(
new ExamplePVADoubleArrayGet(
ExampleLinkPtr pvRecord(
new ExampleLink(
recordName,providerName,channelName,pvStructure));
if(!pvRecord-&gt;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-&gt;scalarArray(
pvDouble,"alarm,timeStamp");
recordName = "doubleArray";
pvRecord = PVRecord::create(recordName,pvStructure);
result = master-&gt;addRecord(pvRecord);
if(!result) cout&lt;&lt; "record " &lt;&lt; recordName &lt;&lt; " not added" &lt;&lt; endl;
ServerContext::shared_pointer serverContext = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
recordName = "examplePVADoubleArrayGet";
if(argc&gt;1) recordName = argv[1];
String providerName("local");
if(argc&gt;2) providerName = argv[2];
String channelName("doubleArray");
if(argc&gt;3) channelName = argv[3];
pvRecord = ExamplePVADoubleArrayGet::create(
recordName,providerName,channelName);
if(pvRecord!=NULL) {
result = master-&gt;addRecord(pvRecord);
cout &lt;&lt; "result of addRecord " &lt;&lt; recordName &lt;&lt; " " &lt;&lt; result &lt;&lt; endl;
} else {
cout &lt;&lt; "ExamplePVADoubleArrayGet::create failed" &lt;&lt; endl;
}
string str;
while(true) {
cout &lt;&lt; "Type exit to stop: \n";
getline(cin,str);
if(str.compare("exit")==0) break;
}
serverContext-&gt;shutdown();
epicsThreadSleep(1.0);
serverContext-&gt;destroy();
ClientFactory::stop();
channelProvider-&gt;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-&gt;getProvider(providerName);
Channel::shared_pointer channel = provider-&gt;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.