added examplePVADoubleGet; updated to latest pvDataCPP-md

This commit is contained in:
Marty Kraimer
2013-08-06 10:26:56 -04:00
parent e40fe7b0ac
commit 614491c02e
26 changed files with 1072 additions and 55 deletions

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, 25-Jul-2013</h2>
<h2 class="nocount">EPICS v4 Working Group, Working Draft, 06-Aug-2013</h2>
<dl>
<dt>Latest version:</dt>
<dd><a
@ -47,13 +47,14 @@
<dt>This version:</dt>
<dd><a
href=
"http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvDatabaseCPP/raw-file/tip/documentation/pvDatabaseCPP_20130725.html">
pvDatabaseCPP20130725.html</a>
"http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvDatabaseCPP/raw-file/tip/documentation/pvDatabaseCPP_20130806.html">
pvDatabaseCPP20130806.html</a>
</dd>
<dt>Previous version:</dt>
<dd><a
href="http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvDatabaseCPP/raw-file/tip/documentation/pvDatabaseCPP_20130627.html">
pvDatabaseCPP20130627.html</a>
href=
"http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvDatabaseCPP/raw-file/tip/documentation/pvDatabaseCPP_20130725.html">
pvDatabaseCPP20130725.html</a>
</dd>
<dt>Editors:</dt>
<dd>Marty Kraimer, BNL</dd>
@ -77,7 +78,7 @@ The minimum that an extenson must provide is a top level PVStructure and a proce
<h2 class="nocount">Status of this Document</h2>
<p>This is the 25-Jul-2013 version of the definition of pvDatabaseCPP.
<p>This is the 06-Aug-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
@ -90,11 +91,8 @@ 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>Termination issues.</dt>
<dd>There are memory leak and other issues when either the main programs or v3IOC terminates.</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
@ -105,8 +103,6 @@ This project is ready for alpha users.
<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>
@ -157,7 +153,13 @@ A record is smart because code can be attached to a record, which is accessed vi
<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.
as part of a V3IOC.</dd>
<dt>examplePVADoubleArrayGet</dt>
<dd>This is an example PVRecord that uses channelGet to get an array of doubles from
another PVRecord via pvAccess. The example shows how to get the value either via
remote pvAccess or by directly accessing the local channelProvider.
Again the records can be deployed either as a standalone process or
as part of a V3IOC.</dd>
</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
@ -171,10 +173,10 @@ level PVStructure and the following three methods:</p>
<dd>This is what makes a record smart.
</dd>
<dt>destroy</dt>
<dd>This releases and resorurces used by the impplementation.</dd>
<dd>This releases and resources used by the implementation.</dd>
</dl>
<h3>Getting started</h3>
<p>Included with this project are two examples that are useful for
<p>Included with this project are three 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:
@ -188,6 +190,10 @@ The examples are:
<dd>This has a database with several records.</dd>
<dt>exampleServer</dt>
<dd>This is exampleServer as part of a V3IOC</dd>
<dt>examplePVADoubleArrayGetMain</dt>
<dd>This is the example of how to use pvAccess to get data.</dd>
<dt>examplePVADoubleArrayGet</dt>
<dd>This is examplePVADoubleArrayGet as part of a V3IOC</dd>
</dt>
</p>
<h3>exampleCounter</h3>
@ -218,24 +224,11 @@ exampleCounter is a pvRecord.
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.hg.sourceforge.net/hgweb/epics-pvdata/swtshellJava/raw-file/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:
<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 includes the following records:</p>
<dl>
<dt>exampleCounter</dt>
<dd>A record that is an instance of exampleCounter described below.
@ -257,6 +250,22 @@ They will work on the exampleServer with the following differences:
<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>swtshell</h3>
<p>The Java program
<a
href="http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/swtshellJava/raw-file/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>Relationship with pvIOCJava.</h3>
<p>This document descibes a C++ implementation of some of the components in pvIOCJava,
@ -580,6 +589,32 @@ The following are the minimium features required</p>
</dl>
<p>The following sections describes the classes required for the first phase.</p>
<h2>iocshell commands</h2>
<p>The following iocsh commands are provided:</p>
<dl>
<dt>startPVAClient</dt>
<dd>Starts the client side of pvAccess.s
It makes channel provider <b>pvAccess</b> available.
After startPVAServer is called the channel provider <b>local</b> will
also be available.
</dd>
<dt>stopPVAClient</dt>
<dd>Stops pvAccess.</dd>
<dt>startPVAServer</dt>
<dd>Starts the local channel provider</p>
<dt>stopPVAServer</dt>
<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>
</dl>
<p>The client commands are provided via PVAClientRegister.dbd and the othet commands
via PVAServerRegister.dbd.</p>
<p>In addition any code that implements a PVRecord must implement an ioc command.
The directory example has examples of how to implement the registration code.
See example/v3IOC/exampleCounter/src/ for a simple example.</p>
<h2>database</h2>
<p>This directory has the following files:</p>
<dl>
@ -1236,6 +1271,314 @@ if(!result) cout&lt;&lt; "record " &lt;&lt; recordName &lt;&lt; " not added" &lt
<h3>powerSupplyRecordTest</h3>
<p>This simulates a simple power supply record.
It is used for testing.</p>
<h2>Accessing Other PVRecords</h2>
<p>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>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>
<h3>examplePVADoubleArrayGet</h3>
examplePVADoubleArrayGet shows how to use pvAccess to get data.</p>
<p>The code resides in three directories:</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>
</dl>
<h4>examplePVADoubleArrayGet Implementation</h4>
<p>examplePVADoubleArrayGet.h contains the following:</p>
<pre>
...
class ExamplePVADoubleArrayGet :
public PVRecord,
public epics::pvAccess::ChannelRequester,
public epics::pvAccess::ChannelGetRequester
{
public:
POINTER_DEFINITIONS(ExamplePVADoubleArrayGet);
static ExamplePVADoubleArrayGetPtr create(
epics::pvData::String const &amp; recordName,
epics::pvData::String const &amp; providerName,
epics::pvData::String const &amp; channelName
);
virtual ~ExamplePVADoubleArrayGet() {}
virtual void destroy();
virtual bool init();
virtual void process();
virtual void channelCreated(
const epics::pvData::Status&amp; status,
epics::pvAccess::Channel::shared_pointer const &amp; channel);
virtual void channelStateChange(
epics::pvAccess::Channel::shared_pointer const &amp; channel,
epics::pvAccess::Channel::ConnectionState connectionState);
virtual void channelGetConnect(
const epics::pvData::Status&amp; status,
epics::pvAccess::ChannelGet::shared_pointer const &amp; channelGet,
epics::pvData::PVStructure::shared_pointer const &amp; pvStructure,
epics::pvData::BitSet::shared_pointer const &amp; bitSet);
virtual void getDone(const epics::pvData::Status&amp; status);
private:
...
</pre>
<p>All the non-static methods are either PVRecord, PVChannel, or PVChannelGet methods
and will not be discussed further.
The create method is called to create a new PVRecord instance with code that will issue
a ChannelGet::get request every time the process method of the instance is called.
Some other pvAccess client can issue a channelGet, to the record instance, with a request
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(
String const &amp; recordName,
String const &amp; providerName,
String const &amp; channelName)
{
PVStructurePtr pvStructure = getStandardPVField()-&gt;scalarArray(
pvDouble,"alarm.timeStamp");
ExamplePVADoubleArrayGetPtr pvRecord(
new ExamplePVADoubleArrayGet(
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.
Note that if init returns false it returns a pointer to NULL.</p>
<p>The init method is:</p>
<pre>
bool ExamplePVADoubleArrayGet::init()
{
initPVRecord();
PVStructurePtr pvStructure = getPVRecordStructure()-&gt;getPVStructure();
pvTimeStamp.attach(pvStructure-&gt;getSubField("timeStamp"));
pvAlarm.attach(pvStructure-&gt;getSubField("alarm"));
pvValue = static_pointer_cast&lt;PVDoubleArray&gt;(
pvStructure-&gt;getScalarArrayField("value",pvDouble));
if(pvValue==NULL) {
return false;
}
ChannelAccess::shared_pointer channelAccess = getChannelAccess();
ChannelProvider::shared_pointer provider =
channelAccess-&gt;getProvider(providerName);
if(provider==NULL) {
cout &lt;&lt; getRecordName() &lt;&lt; " provider "
&lt;&lt; providerName &lt;&lt; " does not exist" &lt;&lt; endl;
return false;
}
ChannelRequester::shared_pointer channelRequester =
dynamic_pointer_cast&lt;ChannelRequester&gt;(getPtrSelf());
channel = provider-&gt;createChannel(channelName,channelRequester);
event.wait();
if(!status.isOK()) {
cout &lt;&lt; getRecordName() &lt;&lt; " createChannel failed "
&lt;&lt; status.getMessage() &lt;&lt; endl;
return false;
}
ChannelGetRequester::shared_pointer channelGetRequester =
dynamic_pointer_cast&lt;ChannelGetRequester&gt;(getPtrSelf());
PVStructurePtr pvRequest = getCreateRequest()-&gt;createRequest(
"value,alarm,timeStamp",getPtrSelf());
channelGet = channel-&gt;createChannelGet(channelGetRequester,pvRequest);
event.wait();
if(!status.isOK()) {
cout &lt;&lt; getRecordName() &lt;&lt; " createChannelGet failed "
&lt;&lt; status.getMessage() &lt;&lt; endl;
return false;
}
getPVValue = static_pointer_cast&lt;PVDoubleArray&gt;(
getPVStructure-&gt;getScalarArrayField("value",pvDouble));
if(getPVValue==NULL) {
cout &lt;&lt; getRecordName() &lt;&lt; " get value not PVDoubleArray" &lt;&lt; endl;
return false;
}
return true;
}
</pre>
<p>This first makes sure the pvStructure has the fields it requires:</p>
<dl>
<dt>timeStamp</dt>
<dd>A timeStamp structure. This will be set to the current time when process is called.</dd>
<dt>alarm</dt>
<dd>An alarm structure. This will be used to pass status information to the client when
process is called.</dd>
<dt>value</dt>
<dd>This must be a scalarArray of type double.
It is where data is copied when the channelGet is issued.</dd>
</dl>
<p>Next it makes sure the channelProvider exists.</p>
<p>Next it creates the channel and waits until it connects.</p>
<p>Next it creates the channelGet and waits until it is created.</p>
<p>Next it makes sure it has connected to a double array field.</p>
<p>If anything goes wrong during initialization it returns false.
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>
</div>
</body>

View File

@ -5,6 +5,7 @@ DIRS += record
DIRS += pvCopy
DIRS += exampleCounter
DIRS += exampleServer
DIRS += examplePVADoubleArrayGet
DIRS += v3IOC
include $(TOP)/configure/RULES_DIRS

View File

@ -0,0 +1,14 @@
TOP=../..
include $(TOP)/configure/CONFIG
PROD_HOST += examplePVADoubleArrayGetMain
examplePVADoubleArrayGetMain_SRCS += examplePVADoubleArrayGetMain.cpp
examplePVADoubleArrayGetMain_LIBS += pvDatabase pvAccess pvData Com
examplePVADoubleArrayGetMain_LIBS += pvDatabaseExample
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

View File

@ -0,0 +1,80 @@
/*ExamplePVADoubleArrayGetMain.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
*/
/* Author: Marty Kraimer */
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <iostream>
#include <pv/standardField.h>
#include <pv/standardPVField.h>
#include <pv/examplePVADoubleArrayGet.h>
#include <pv/traceRecord.h>
#include <pv/channelProviderLocal.h>
#include <pv/serverContext.h>
#include <pv/clientFactory.h>
using namespace std;
using std::tr1::static_pointer_cast;
using namespace epics::pvData;
using namespace epics::pvAccess;
using namespace epics::pvDatabase;
static StandardPVFieldPtr standardPVField = getStandardPVField();
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;
}

View File

@ -192,17 +192,15 @@ static void testPVScalarArray(
BitSetPtr bitSet;
String builder;
size_t offset;
ConvertPtr convert = getConvert();
size_t n = 5;
//DoubleArray values(n);
shared_vector<double> values(n);
pvRecord->lock_guard();
cout << endl;
pvStructureRecord = pvRecord->getPVRecordStructure()->getPVStructure();
pvValueRecord = pvStructureRecord->getScalarArrayField(valueNameRecord,scalarType);
for(size_t i=0; i<n; i++) values[i] = i;
pvValueRecord->PVScalarArray::putFrom<pvDouble>(values);
const shared_vector<const double> xxx(freeze(values));
pvValueRecord->putFrom(xxx);
StructureConstPtr structure = pvCopy->getStructure();
builder.clear(); structure->toString(&builder);
cout << "structure from copy" << endl << builder << endl;
@ -213,8 +211,10 @@ ConvertPtr convert = getConvert();
builder.clear(); pvValueCopy->toString(&builder);
cout << "after initCopy pvValueCopy " << builder << endl;
cout << endl;
values.resize(n);
for(size_t i=0; i<n; i++) values[i] = i + .06;
pvValueRecord->PVScalarArray::putFrom<pvDouble>(values);
const shared_vector<const double> yyy(freeze(values));
pvValueRecord->putFrom(yyy);
pvCopy->updateCopySetBitSet(pvStructureCopy,bitSet,true);
builder.clear(); pvValueCopy->toString(&builder);
cout << "after put(i+ .06) pvValueCopy " << builder << endl;
@ -232,8 +232,10 @@ ConvertPtr convert = getConvert();
cout << " bitSet " << builder;
cout << endl;
bitSet->clear();
values.resize(n);
for(size_t i=0; i<n; i++) values[i] = i + 1.0;
pvValueRecord->PVScalarArray::putFrom<pvDouble>(values);
const shared_vector<const double> zzz(freeze(values));
pvValueRecord->putFrom(zzz);
builder.clear();
bitSet->toString(&builder);
cout << "before updateCopyFromBitSet";
@ -256,8 +258,10 @@ ConvertPtr convert = getConvert();
bitSet->toString(&builder);
cout << " bitSet " << builder;
cout << endl;
values.resize(n);
for(size_t i=0; i<n; i++) values[i] = i + 2.0;
pvValueRecord->PVScalarArray::putFrom<pvDouble>(values);
const shared_vector<const double> ttt(freeze(values));
pvValueRecord->putFrom(ttt);
bitSet->set(0);
cout << "before updateRecord";
builder.clear(); pvValueRecord->toString(&builder);

View File

@ -14,6 +14,10 @@ SRC_DIRS += $(EXAMPLESRC)/exampleServer
INC+= exampleServerCreateRecords.h
LIBSRCS += exampleServerCreateRecords.cpp
SRC_DIRS += $(EXAMPLESRC)/examplePVADoubleArrayGet
INC+= examplePVADoubleArrayGet.h
LIBSRCS += examplePVADoubleArrayGet.cpp
include $(TOP)/configure/RULES

View File

@ -0,0 +1,161 @@
/* examplePVADoubleArrayGet.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.08.02
*/
#include <pv/examplePVADoubleArrayGet.h>
#include <pv/standardPVField.h>
#include <pv/convert.h>
namespace epics { namespace pvDatabase {
using namespace epics::pvData;
using namespace epics::pvAccess;
using std::tr1::static_pointer_cast;
using std::tr1::dynamic_pointer_cast;
using std::cout;
using std::endl;
ExamplePVADoubleArrayGetPtr ExamplePVADoubleArrayGet::create(
String const & recordName,
String const & providerName,
String const & channelName)
{
PVStructurePtr pvStructure = getStandardPVField()->scalarArray(
pvDouble,"alarm.timeStamp");
ExamplePVADoubleArrayGetPtr pvRecord(
new ExamplePVADoubleArrayGet(
recordName,providerName,channelName,pvStructure));
if(!pvRecord->init()) pvRecord.reset();
return pvRecord;
}
ExamplePVADoubleArrayGet::ExamplePVADoubleArrayGet(
String const & recordName,
String providerName,
String channelName,
PVStructurePtr const & pvStructure)
: PVRecord(recordName,pvStructure),
providerName(providerName),
channelName(channelName),
convert(getConvert())
{
}
void ExamplePVADoubleArrayGet::destroy()
{
PVRecord::destroy();
}
bool ExamplePVADoubleArrayGet::init()
{
initPVRecord();
PVStructurePtr pvStructure = getPVRecordStructure()->getPVStructure();
pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
pvAlarm.attach(pvStructure->getSubField("alarm"));
pvValue = static_pointer_cast<PVDoubleArray>(
pvStructure->getScalarArrayField("value",pvDouble));
if(pvValue==NULL) {
return false;
}
ChannelAccess::shared_pointer channelAccess = getChannelAccess();
ChannelProvider::shared_pointer provider =
channelAccess->getProvider(providerName);
if(provider==NULL) {
cout << getRecordName() << " provider "
<< providerName << " does not exist" << endl;
return false;
}
ChannelRequester::shared_pointer channelRequester =
dynamic_pointer_cast<ChannelRequester>(getPtrSelf());
channel = provider->createChannel(channelName,channelRequester);
event.wait();
if(!status.isOK()) {
cout << getRecordName() << " createChannel failed "
<< status.getMessage() << endl;
return false;
}
ChannelGetRequester::shared_pointer channelGetRequester =
dynamic_pointer_cast<ChannelGetRequester>(getPtrSelf());
PVStructurePtr pvRequest = getCreateRequest()->createRequest(
"value,alarm,timeStamp",getPtrSelf());
channelGet = channel->createChannelGet(channelGetRequester,pvRequest);
event.wait();
if(!status.isOK()) {
cout << getRecordName() << " createChannelGet failed "
<< status.getMessage() << endl;
return false;
}
getPVValue = static_pointer_cast<PVDoubleArray>(
getPVStructure->getScalarArrayField("value",pvDouble));
if(getPVValue==NULL) {
cout << getRecordName() << " get value not PVDoubleArray" << endl;
return false;
}
return true;
}
void ExamplePVADoubleArrayGet::process()
{
status = Status::Ok;
channelGet->get(false);
event.wait();
timeStamp.getCurrent();
pvTimeStamp.set(timeStamp);
AlarmSeverity severity(noAlarm);
if(!status.isOK()) {
switch(status.getType()) {
case Status::STATUSTYPE_OK: severity = noAlarm; break;
case Status::STATUSTYPE_WARNING: severity = minorAlarm; break;
case Status::STATUSTYPE_ERROR: severity = majorAlarm; break;
case Status::STATUSTYPE_FATAL: severity = invalidAlarm; break;
}
alarm.setSeverity(severity);
} else {
convert->copy(getPVValue,pvValue);
}
alarm.setMessage(status.getMessage());
pvAlarm.set(alarm);
}
void ExamplePVADoubleArrayGet::channelCreated(
const Status& status,
Channel::shared_pointer const & channel)
{
this->status = status;
this->channel = channel;
event.signal();
}
void ExamplePVADoubleArrayGet::channelStateChange(
Channel::shared_pointer const & channel,
Channel::ConnectionState connectionState)
{
}
void ExamplePVADoubleArrayGet::channelGetConnect(
const Status& status,
ChannelGet::shared_pointer const & channelGet,
PVStructure::shared_pointer const & pvStructure,
BitSet::shared_pointer const & bitSet)
{
this->status = status;
this->channelGet = channelGet;
this->getPVStructure = pvStructure;
this->bitSet = bitSet;
event.signal();
}
void ExamplePVADoubleArrayGet::getDone(const Status& status)
{
this->status = status;
event.signal();
}
}}

View File

@ -0,0 +1,83 @@
/* examplePVADoubleArrayGet.h */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.08.02
*/
#ifndef EXAMPLEPVADOUBLEARRAYGET_H
#define EXAMPLEPVADOUBLEARRAYGET_H
#include <pv/timeStamp.h>
#include <pv/pvTimeStamp.h>
#include <pv/alarm.h>
#include <pv/pvAlarm.h>
#include <pv/pvDatabase.h>
#include <pv/pvCopy.h>
#include <pv/pvAccess.h>
#include <pv/serverContext.h>
namespace epics { namespace pvDatabase {
class ExamplePVADoubleArrayGet;
typedef std::tr1::shared_ptr<ExamplePVADoubleArrayGet> ExamplePVADoubleArrayGetPtr;
class ExamplePVADoubleArrayGet :
public PVRecord,
public epics::pvAccess::ChannelRequester,
public epics::pvAccess::ChannelGetRequester
{
public:
POINTER_DEFINITIONS(ExamplePVADoubleArrayGet);
static ExamplePVADoubleArrayGetPtr create(
epics::pvData::String const & recordName,
epics::pvData::String const & providerName,
epics::pvData::String const & channelName
);
virtual ~ExamplePVADoubleArrayGet() {}
virtual void destroy();
virtual bool init();
virtual void process();
virtual void channelCreated(
const epics::pvData::Status& status,
epics::pvAccess::Channel::shared_pointer const & channel);
virtual void channelStateChange(
epics::pvAccess::Channel::shared_pointer const & channel,
epics::pvAccess::Channel::ConnectionState connectionState);
virtual void channelGetConnect(
const epics::pvData::Status& status,
epics::pvAccess::ChannelGet::shared_pointer const & channelGet,
epics::pvData::PVStructure::shared_pointer const & pvStructure,
epics::pvData::BitSet::shared_pointer const & bitSet);
virtual void getDone(const epics::pvData::Status& status);
private:
ExamplePVADoubleArrayGet(epics::pvData::String const & recordName,
epics::pvData::String providerName,
epics::pvData::String channelName,
epics::pvData::PVStructurePtr const & pvStructure);
epics::pvData::String providerName;
epics::pvData::String channelName;
epics::pvData::ConvertPtr convert;
epics::pvData::PVDoubleArrayPtr pvValue;
epics::pvData::PVTimeStamp pvTimeStamp;
epics::pvData::TimeStamp timeStamp;
epics::pvData::PVAlarm pvAlarm;
epics::pvData::Alarm alarm;
epics::pvAccess::Channel::shared_pointer channel;
epics::pvAccess::ChannelGet::shared_pointer channelGet;
epics::pvData::Mutex mutex;
epics::pvData::Event event;
epics::pvData::Status status;
epics::pvData::PVStructurePtr getPVStructure;
epics::pvData::BitSetPtr bitSet;
epics::pvData::PVDoubleArrayPtr getPVValue;
};
}}
#endif /* EXAMPLEPVADOUBLEARRAYGET_H */

View File

@ -2,5 +2,6 @@ TOP = ../..
include $(TOP)/configure/CONFIG
DIRS += exampleCounter
DIRS += exampleServer
DIRS += examplePVADoubleArrayGet
include $(TOP)/configure/RULES_DIRS

View File

@ -0,0 +1,21 @@
TOP=../../../..
include $(TOP)/configure/CONFIG
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
#----------------------------------------------------
# Optimization of db files using dbst (DEFAULT: NO)
#DB_OPT = YES
#----------------------------------------------------
# Create and install (or just install)
# databases, templates, substitutions like this
DB += ai.db
#----------------------------------------------------
# If <anyname>.db template is not named <anyname>*.template add
# <anyname>_TEMPLATE = <templatename>
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

View File

@ -0,0 +1,13 @@
record(ai, "$(name)")
{
field(PREC, "1")
field(EGU, "amps")
field(HIHI, "8")
field(HIGH, "6")
field(LOW, "4")
field(LOLO, "2")
field(HHSV, "MAJOR")
field(HSV, "MINOR")
field(LSV, "MINOR")
field(LLSV, "MAJOR")
}

View File

@ -0,0 +1,8 @@
TOP = ../../..
include $(TOP)/configure/CONFIG
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *src*))
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Src*))
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *db*))
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Db*))
include $(TOP)/configure/RULES_DIRS

View File

@ -0,0 +1,48 @@
TOP=../../../..
include $(TOP)/configure/CONFIG
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
#=============================
#==================================================
# Build an IOC support library
#
DBD += examplePVADoubleArrayGet.dbd
LIBRARY_IOC += examplePVADoubleArrayGetSupport
examplePVADoubleArrayGetSupport_SRCS += examplePVADoubleArrayGet.cpp
examplePVADoubleArrayGetSupport_LIBS += pvData
examplePVADoubleArrayGetSupport_LIBS += pvAccess
examplePVADoubleArrayGetSupport_LIBS += pvDatabase
examplePVADoubleArrayGetSupport_LIBS += pvDatabaseExample
examplePVADoubleArrayGetSupport_LIBS += $(EPICS_BASE_IOC_LIBS)
#=============================
# build an ioc application
PROD_IOC += examplePVADoubleArrayGet
# <name>_registerRecordDeviceDriver.cpp will be created from <name>.dbd
examplePVADoubleArrayGet_SRCS += examplePVADoubleArrayGet_registerRecordDeviceDriver.cpp
examplePVADoubleArrayGet_SRCS_DEFAULT += examplePVADoubleArrayGetMain.cpp
examplePVADoubleArrayGet_SRCS_vxWorks += -nil-
# The following adds support from base/src/vxWorks
examplePVADoubleArrayGet_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary
examplePVADoubleArrayGet_LIBS += pvData pvAccess
examplePVADoubleArrayGet_LIBS += pvDatabase
examplePVADoubleArrayGet_LIBS += pvDatabaseExample
examplePVADoubleArrayGet_LIBS += examplePVADoubleArrayGetSupport
examplePVADoubleArrayGet_LIBS += $(EPICS_BASE_IOC_LIBS)
#===========================
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

View File

@ -0,0 +1,85 @@
/*examplePVADoubleArrayGet.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.07.24
*/
/* Author: Marty Kraimer */
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <iostream>
#include <cantProceed.h>
#include <epicsStdio.h>
#include <epicsMutex.h>
#include <epicsEvent.h>
#include <epicsThread.h>
#include <iocsh.h>
#include <epicsExport.h>
#include <pv/pvIntrospect.h>
#include <pv/pvData.h>
#include <pv/standardField.h>
#include <pv/standardPVField.h>
#include <pv/pvAccess.h>
#include <pv/pvDatabase.h>
#include <pv/examplePVADoubleArrayGet.h>
using namespace epics::pvData;
using namespace epics::pvAccess;
using namespace epics::pvDatabase;
using std::cout;
using std::endl;
static StandardPVFieldPtr standardPVField = getStandardPVField();
static const iocshArg testArg0 = { "recordName", iocshArgString };
static const iocshArg testArg1 = { "providerName", iocshArgString };
static const iocshArg testArg2 = { "channelName", iocshArgString };
static const iocshArg *testArgs[] = {
&testArg0,&testArg1,&testArg2};
static const iocshFuncDef examplePVADoubleArrayGetFuncDef = {
"examplePVADoubleArrayGetCreateRecord", 3, testArgs};
static void examplePVADoubleArrayGetCallFunc(const iocshArgBuf *args)
{
PVDatabasePtr master = PVDatabase::getMaster();
PVRecordPtr pvRecord;
bool result(false);
String recordName;
PVStructurePtr pvStructure = standardPVField->scalarArray(
pvDouble,"alarm,timeStamp");
pvRecord = PVRecord::create("doubleArray",pvStructure);
result = master->addRecord(pvRecord);
if(!result) cout<< "record " << recordName << " not added" << endl;
recordName = args[0].sval;
char *providerName = args[1].sval;
char *channelName = args[2].sval;
ExamplePVADoubleArrayGetPtr record = ExamplePVADoubleArrayGet::create(recordName,providerName,channelName);
if(record!=NULL)
result = master->addRecord(record);
if(!result) cout << "recordname" << " not added" << endl;
}
static void examplePVADoubleArrayGetRegister(void)
{
static int firstTime = 1;
if (firstTime) {
firstTime = 0;
iocshRegister(&examplePVADoubleArrayGetFuncDef, examplePVADoubleArrayGetCallFunc);
}
}
epicsExportRegistrar(examplePVADoubleArrayGetRegister);

View File

@ -0,0 +1,4 @@
include "base.dbd"
include "PVAServerRegister.dbd"
include "PVAClientRegister.dbd"
registrar("examplePVADoubleArrayGetRegister")

View File

@ -0,0 +1,31 @@
/* examplePVADoubleArrayGetMain.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.07.24
*/
#include <stddef.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include "epicsExit.h"
#include "epicsThread.h"
#include "iocsh.h"
int main(int argc,char *argv[])
{
if(argc>=2) {
iocsh(argv[1]);
epicsThreadSleep(.2);
}
iocsh(NULL);
epicsExit(0);
return(0);
}

View File

@ -0,0 +1,5 @@
TOP = ../..
include $(TOP)/configure/CONFIG
ARCH = $(EPICS_HOST_ARCH)
TARGETS = envPaths
include $(TOP)/configure/RULES.ioc

View File

@ -0,0 +1,16 @@
< envPaths
cd ${TOP}
## Register all support components
dbLoadDatabase("dbd/examplePVADoubleArrayGet.dbd")
examplePVADoubleArrayGet_registerRecordDeviceDriver(pdbbase)
## Load record instances
dbLoadRecords("db/dbArray.db","name=double01,type=DOUBLE")
cd ${TOP}/iocBoot/${IOC}
iocInit()
startPVAClient
startPVAServer
examplePVADoubleArrayGetCreateRecord examplePVADoubleArrayGet local doubleArray

View File

@ -0,0 +1,16 @@
< envPaths
cd ${TOP}
## Register all support components
dbLoadDatabase("dbd/examplePVADoubleArrayGet.dbd")
examplePVADoubleArrayGet_registerRecordDeviceDriver(pdbbase)
## Load record instances
dbLoadRecords("db/dbArray.db","name=double01,type=DOUBLE")
cd ${TOP}/iocBoot/${IOC}
iocInit()
startPVAClient
startPVAServer
examplePVADoubleArrayGetCreateRecord examplePVADoubleArrayGet pvAccess arrayDouble

View File

@ -27,7 +27,9 @@ LIBSRCS += monitorFactory.cpp
SRC_DIRS += $(DATABASE)/v3IOC
DBD += PVAServerRegister.dbd
DBD += PVAClientRegister.dbd
LIBSRCS += PVAServerRegister.cpp
LIBSRCS += PVAClientRegister.cpp
SRC_DIRS += $(DATABASE)/special
INC += recordList.h

View File

@ -91,7 +91,8 @@ PVStringArrayPtr PVDatabase::getRecordNames()
for(iter = recordMap.begin(); iter!=recordMap.end(); ++iter) {
names[i++] = (*iter).first;
}
pvStringArray->replace(names);
shared_vector<const String> temp(freeze(names));
pvStringArray->replace(temp);
return pvStringArray;
}

View File

@ -46,7 +46,7 @@ typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
* @author mrk
*/
class PVRecord :
public epics::pvData::Requester,
public virtual epics::pvData::Requester,
public std::tr1::enable_shared_from_this<PVRecord>
{
public:

View File

@ -280,7 +280,7 @@ ChannelGetLocalPtr ChannelGetLocal::create(
PVCopyPtr pvCopy = PVCopy::create(
pvRecord,
pvRequest,
"field");
"");
if(pvCopy.get()==NULL) {
Status status(
Status::Status::STATUSTYPE_ERROR,
@ -434,7 +434,7 @@ ChannelPutLocalPtr ChannelPutLocal::create(
PVCopyPtr pvCopy = PVCopy::create(
pvRecord,
pvRequest,
"field");
"");
if(pvCopy.get()==NULL) {
Status status(
Status::Status::STATUSTYPE_ERROR,

View File

@ -31,13 +31,15 @@ void copy(
throw std::length_error("pvSubArrayCopy from length error");
}
size_t capacity = pvTo.getCapacity();
if(toOffset+len>capacity) pvTo.setCapacity(toOffset+len);
if(toOffset+len>capacity) capacity = toOffset + len;
shared_vector<T> temp(capacity);
typename PVValueArray<T>::const_svector vecFrom = pvFrom.view();
typename PVValueArray<T>::const_svector vecTo = pvTo.view();
shared_vector<T> temp;
pvTo.swap(temp);
for(size_t i=0; i<toOffset; ++i) temp[i] = vecTo[i];
for(size_t i=0; i<len; ++i) temp[i + toOffset] = vecFrom[i + fromOffset];
pvTo.replace(temp);
for(size_t i=len + toOffset; i<capacity; ++i) temp[i] = vecTo[i];
shared_vector<const T> temp2(freeze(temp));
pvTo.replace(temp2);
}
void copy(
@ -162,15 +164,15 @@ void copy(
throw std::length_error("pvSubArrayCopy from length error");
}
size_t capacity = to.getCapacity();
if(toOffset+len>capacity) to.setCapacity(toOffset+len);
if(toOffset+len>capacity) capacity = toOffset+len;
shared_vector<PVStructurePtr> temp(capacity);
typename PVValueArray<PVStructurePtr>::const_svector vecFrom = from.view();
typename PVValueArray<PVStructurePtr>::const_svector vecTo = to.view();
shared_vector<PVStructurePtr> temp;
to.swap(temp);
for(size_t i=0; i<len; ++i) {
temp[i + toOffset] = vecFrom[i + fromOffset];
}
to.replace(temp);
for(size_t i=0; i<toOffset; ++i) temp[i] = vecTo[i];
for(size_t i=0; i<len; ++i) temp[i + toOffset] = vecFrom[i + fromOffset];
for(size_t i=len + toOffset; i<capacity; ++i) temp[i] = vecTo[i];
shared_vector<const PVStructurePtr> temp2(freeze(temp));
to.replace(temp2);
}
void copy(

View File

@ -0,0 +1,72 @@
/*PVAClientRegister.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.08.05
*/
/* Author: Marty Kraimer */
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <iostream>
#include <iocsh.h>
#include <epicsExport.h>
#include <pv/pvAccess.h>
#include <pv/clientFactory.h>
using std::cout;
using std::endl;
using namespace epics::pvData;
using namespace epics::pvAccess;
static const iocshFuncDef startPVAClientFuncDef = {
"startPVAClient", 0, 0
};
extern "C" void startPVAClient(const iocshArgBuf *args)
{
ClientFactory::start();
}
static const iocshFuncDef stopPVAClientFuncDef = {
"stopPVAClient", 0, 0
};
extern "C" void stopPVAClient(const iocshArgBuf *args)
{
ClientFactory::stop();
}
static void startPVAClientRegister(void)
{
static int firstTime = 1;
if (firstTime) {
firstTime = 0;
iocshRegister(&startPVAClientFuncDef, startPVAClient);
}
}
static void stopPVAClientRegister(void)
{
static int firstTime = 1;
if (firstTime) {
firstTime = 0;
iocshRegister(&stopPVAClientFuncDef, stopPVAClient);
}
}
epicsExportRegistrar(startPVAClientRegister);
epicsExportRegistrar(stopPVAClientRegister);

View File

@ -0,0 +1,2 @@
registrar("startPVAClientRegister")
registrar("stopPVAClientRegister")