updated documentation; fixed bugs while updating documentation

This commit is contained in:
Marty Kraimer
2014-07-10 13:25:58 -04:00
parent fa53d72258
commit 2fe3e66047
13 changed files with 2242 additions and 356 deletions

View File

@ -0,0 +1,50 @@
Release release/0.9.3 IN DEVELOPMENT
===========
The main changes since release 3.0.2 are:
* array semantics now enforce Copy On Write.
* String no longer defined.
* toString replaced by stream I/O
* union is new type.
* copy and monitor use new code in pvDataCPP
New Semantics for Arrays
--------
pvDatabaseCPP has been changed to use the new array implementation from pvDataCPP.
String no longer defined
---------
String is replaced by std::string.
toString replaced by stream I/O
---------
All uses of toString have been changed to use the steam I/O that pvDataCPP implements.
union is a new basic type.
------------
exampleDatabase now has example records for union and union array.
There are records for regular union and for variant union.
copy
----
The implementation of copy and monitor for pvAccess has been changed
to use the new monitor and copy support from pvDataCPP.
monitorPlugin
-------------
exampleDatabase now has a example plugin that implements onChange.
Release 0.9.2
==========
This was the starting point for RELEASE_NOTES

25
documentation/TODO.md Normal file
View File

@ -0,0 +1,25 @@
TODO
===========
monitorPlugin
-------------
A debate is on-going about what semantics should be.
memory leak
--------
arrayPerformanceMain shows a slight memory leak at termination.
channel destroy and recreate
-------------------
longArrayGet and longArrayPut fail if the channel is destroyed and immediately recreated. If epicsThreadSleep(1.0) is called between destroy and recreate then they work. The current version of each does wait.
create more regresion tests
----------------
Currently only some simple tests exist. Most of the testing has been via the examples

View File

@ -38,7 +38,7 @@
<h1>pvDatabaseCPP</h1> <h1>pvDatabaseCPP</h1>
<!-- Maturity: Working Draft or Request for Comments, or Recommendation, and date. --> <!-- Maturity: Working Draft or Request for Comments, or Recommendation, and date. -->
<h2 class="nocount">EPICS v4 Working Group, Working Draft, 19-Feb-2014</h2> <h2 class="nocount">EPICS v4 Working Group, Working Draft, 10-July-2014</h2>
<dl> <dl>
<dt>Latest version:</dt> <dt>Latest version:</dt>
<dd><a <dd><a
@ -46,12 +46,12 @@
</dd> </dd>
<dt>This version:</dt> <dt>This version:</dt>
<dd><a <dd><a
href= "pvDatabaseCPP_20140219.html">pvDatabaseCPP20140219.html</a> href= "pvDatabaseCPP_20140710.html">pvDatabaseCPP20140710.html
</dd> </a> </dd>
<dt>Previous version:</dt> <dt>Previous version:</dt>
<dd><a <dd><a
href= "pvDatabaseCPP_20140207.html">pvDatabaseCPP20140207.html</a> href= "pvDatabaseCPP_20140219.html">pvDatabaseCPP20140219.html
</dd> </a> </dd>
<dt>Editors:</dt> <dt>Editors:</dt>
<dd>Marty Kraimer, BNL</dd> <dd>Marty Kraimer, BNL</dd>
</dl> </dl>
@ -59,7 +59,6 @@
<p class="copyright">This product is made available subject to acceptance of the <a <p class="copyright">This product is made available subject to acceptance of the <a
href="http://epics-pvdata.sourceforge.net/LICENSE.html">EPICS open source license.</a></p> href="http://epics-pvdata.sourceforge.net/LICENSE.html">EPICS open source license.</a></p>
<hr /> <hr />
</div>
<h2 class="nocount">Abstract</h2> <h2 class="nocount">Abstract</h2>
@ -79,54 +78,10 @@ V4 control system programming environment:<br />
<h2 class="nocount">Status of this Document</h2> <h2 class="nocount">Status of this Document</h2>
<p>This is the 19-Feb-2014 version of of pvDatabaseCPP.</p> <p>This is the 10-July-2014 version of of pvDatabaseCPP.</p>
</p> </p>
<p>This version is a complete implementation of what is described in this manual.
<p> </div>
Since the last version of the documentation:
<dl>
<dt>examples</dt>
<dd>The examples have been moved to separate top level build areas.</dd>
<dt>test</dt>
<dd>The regression tests have been moved to a separate top level build area.
It is built from the top but nothing from the tests appears in the top
level bin directory.
<dt>exampleServer</dt>
<dd>This example now also includes pvaSrv, i. e. the pvAccess server for
interfacing to iocCore V3 records.
</dd>
</dl>
<p>
This project is ready for alpha users.
</p>
<p>
I have not had time to look at
two unresolved problems reported in the previous version of this document:
<dl>
<dt>memory leak</dt>
<dd>arrayPerformanceMain shows a slight memory leak at termination.</dd>
<dt>channel destroy and recreate</dt>
<dd>longArrayGet and longArrayPut fail if the channel is destroyed and
immediately recreated.
If epicsThreadSleep(1.0) is called between destroy and recreate then they work.
The current version of each does wait.
</dd>
</dl>
</p>
<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 requires changes to pvAccessCPP.
</dd>
<dt>Monitor Algorithms</dt>
<dd>Monitor algorithms have not been implemented.
Thus all monitors are onPut.</dd>
<dt>Create more regression tests</dt>
<dd>Currently only some simple tests exist.
Most of the testing has been via the examples.</dd>
</dl>
<div id="toc"> <div id="toc">
<h2 class="nocount" style="page-break-before: always">Table of Contents</h2> <h2 class="nocount" style="page-break-before: always">Table of Contents</h2>
@ -343,13 +298,14 @@ completion of later phases.
The rest of this document discusses only the first phase.</p> The rest of this document discusses only the first phase.</p>
<h3>Features Required for localChannelProvider</h3> <h3>Features Required for localChannelProvider</h3>
<dl> <dl>
<dt>pvCopy</dt> <dt>copy and monitor</dt>
<dd>Creates a PVStructure that contains a copy of an arbitary <dd>pvDataCPP provides facilities copy and monitor.
subset of the fields of another top level PVStructure. This facilities allow a client to access an arbitrary subset
It can copy data between the two and maintains a bitSet that show of the fields in the top level structure associated with a channel,
which fields are changed.<dd> and to monitor changes in the top level structure.
<dt>monitor</dt> pvDatabaseCPP uses what pvDataCPP provides and has code that
<dd>This provides the ability to monitor changes to fields of a record.</dd> associates these facilities with a PVRecord.
</dd>
<dt>PVRecord and PVDatabase</dt> <dt>PVRecord and PVDatabase</dt>
<dd>Defined below.</dd> <dd>Defined below.</dd>
<dt>The localChannelProvider itself</dt> <dt>The localChannelProvider itself</dt>
@ -435,6 +391,22 @@ mrk&gt; make
<p>This builds the example.</p> <p>This builds the example.</p>
<h2>iocshell commands</h2> <h2>iocshell commands</h2>
<p>Shell commands are made available via the standard DBD include mechanism
provided by iocCore.
The following provide EPICS V4 shell commands:</p>
<dl>
<dt>pvAccessCPP</dt>
<dd>PVAClientRegister.dbd and PVAServerRegister.dbd</dd>
<dt>pvaSrv</dt>
<dd>dbPv.dbd</dd>
<dt>pvDatabaseCPP</dt>
<dd>registerChannelProviderLocal.dbd</dd>
</dl>
<p>
Look at exampleServer/ioc/src/exampleServerInclude.dbd for an example
of how an application can make the shell commands available.
</p>
<h3>Commands From pvAccessCPP</h3>
<p>The following iocsh commands are provided for a V3IOC:</p> <p>The following iocsh commands are provided for a V3IOC:</p>
<dl> <dl>
<dt>startPVAClient</dt> <dt>startPVAClient</dt>
@ -449,16 +421,25 @@ mrk&gt; make
<dd>Starts the local channel provider</p> <dd>Starts the local channel provider</p>
<dt>stopPVAServer</dt> <dt>stopPVAServer</dt>
<dd>Stop the local channel provider</dd> <dd>Stop the local channel provider</dd>
</dl>
</p>
<h3>Commands implemented by pvDatabaseCPP</h3>
<p>The following iocsh commands are provided for a V3IOC:</p>
<dl>
<dt>pvdbl</dt> <dt>pvdbl</dt>
<dd>Provides a list of all the pvRecords in database <b>master</b> <dd>Provides a list of all the pvRecords in database <b>master</b>
</dd> </dd>
</dl> </dl>
<p>The client commands are provided via PVAClientRegister.dbd and the other commands <p>In addition any code that implements a PVRecord must implement an ioc command.</p>
via PVAServerRegister.dbd.</p> <p>Look at any of the examples to see how to implement shell commands.</p>
<p>In addition any code that implements a PVRecord must implement an ioc command. <h3>Commands implemented by pvaSrv</h3>
The directory example has examples of how to implement the registration code. <p><b>pvaSrv</b> provides a pvAccess server that provides access to iocCore records.</p>
See example/V3IOC/exampleCounter/src/ for a simple example.</p> <p>pvaSrv does not provide any shell commands but it can be part of an IOC.
Just make sure your application configures pvaSrv and then include the following file:
<pre>
include "dbPv.dbd"
</pre>
</p>
<h2>database</h2> <h2>database</h2>
<h3>src/database</h3> <h3>src/database</h3>
@ -492,7 +473,12 @@ See example/V3IOC/exampleCounter/src/ for a simple example.</p>
<pre> <pre>
recordName = "laptoprecordListPGRPC"; recordName = "laptoprecordListPGRPC";
pvRecord = RecordListRecord::create(recordName); pvRecord = RecordListRecord::create(recordName);
result = master-&gt;addRecord(pvRecord); if(pvRecord==NULL) {
cout &lt;&lt; "RecordListRecord::create failed" &lt;&lt; endl;
} else {
result = master->addRecord(pvRecord);
if(!result) cout&lt;&lt; "record " &lt;&lt; recordName &lt;&lt; " not added" &lt;&lt; endl;
}
</pre> </pre>
</dd> </dd>
<dt>traceRecord.h</dt> <dt>traceRecord.h</dt>
@ -571,6 +557,7 @@ public:
POINTER_DEFINITIONS(PVRecord); POINTER_DEFINITIONS(PVRecord);
virtual bool init() {initPVRecord(); return true;} virtual bool init() {initPVRecord(); return true;}
virtual void start() {}
virtual void process() {} virtual void process() {}
virtual void destroy(); virtual void destroy();
@ -582,9 +569,6 @@ public:
PVRecordStructurePtr getPVRecordStructure(); PVRecordStructurePtr getPVRecordStructure();
PVRecordFieldPtr findPVRecordField( PVRecordFieldPtr findPVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField); epics::pvData::PVFieldPtr const &amp; pvField);
bool addRequester(epics::pvData::RequesterPtr const &amp; requester);
bool removeRequester(epics::pvData::RequesterPtr const &amp; requester);
inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
void lock(); void lock();
void unlock(); void unlock();
bool tryLock(); bool tryLock();
@ -596,16 +580,6 @@ public:
bool removeListener(PVListenerPtr const &amp; pvListener); bool removeListener(PVListenerPtr const &amp; pvListener);
void beginGroupPut(); void beginGroupPut();
void endGroupPut(); void endGroupPut();
std::string getRequesterName() {return getRecordName();}
virtual void message(
std::string const &amp; message,
epics::pvData::MessageType messageType);
void message(
PVRecordFieldPtr const &amp; pvRecordField,
std::string const &amp; message,
epics::pvData::MessageType messageType);
void toString(epics::pvData::StringBuilder buf);
void toString(epics::pvData::StringBuilder buf,int indentLevel);
int getTraceLevel(); int getTraceLevel();
void setTraceLevel(int level); void setTraceLevel(int level);
protected: protected:
@ -629,19 +603,28 @@ private:
Derived classes must implement this method. Derived classes must implement this method.
This method Must call initPVRecord. This method Must call initPVRecord.
</dd> </dd>
<dt>start</dt>
<dd>Virtual method.
Optional method for derived class.
It is called before record is added to database.
The base method does nothing.
</dd>
<dt>process</dt> <dt>process</dt>
<dd>Virtual method. <dd>Virtual method.
Derived classes must implement this method. Derived classes usually implement this method.
It implements the semantics for the record.
The base implementation does nothing. The base implementation does nothing.
</dd> </dd>
<dt>destroy</dt> <dt>destroy</dt>
<dd>This is a virtual method. <dd>Virtual method.
A derived class must call the base class destroy method after it Optional method for derived class.
If the derived class implements this it
must call the base class destroy method after it
has released any resources it uses.</dd> has released any resources it uses.</dd>
<dt>create</dt> <dt>create</dt>
<dd>Static method to create dumb records, <dd>Static method to create dumb records,
i.e. records with a process method that does nothing. i.e. records with a process method that does nothing.
A derived class should have it';s own static create method. A derived class should have it's own static create method.
</dd> </dd>
<dt>~PVRecord</dt> <dt>~PVRecord</dt>
<dd>The destructor which must be virtual. A derived class must also have <dd>The destructor which must be virtual. A derived class must also have
@ -652,13 +635,6 @@ private:
<dd>Get the top level PVStructure.</dd> <dd>Get the top level PVStructure.</dd>
<dt>findPVRecordField</dt> <dt>findPVRecordField</dt>
<dd>Given a PVFieldPtr return the PVRecordFieldPtr for the field.</dd> <dd>Given a PVFieldPtr return the PVRecordFieldPtr for the field.</dd>
<dt>addRequester</dt>
<dd>Add a requester to receive messages.</dd>
<dt>removeRequester</dt>
<dd>Remove a message requester.</dd>
<dt>lock_guard</dt>
<dd>This is an inline method that locks the record. The record will automatically
be unlocked when control leaves the block that has the call.
<dt>lock</dt> <dt>lock</dt>
<dt>unlock</dt> <dt>unlock</dt>
<dd>Lock and Unlock the record. <dd>Lock and Unlock the record.
@ -691,14 +667,6 @@ private:
<dt>endGroupPut</dt> <dt>endGroupPut</dt>
<dd>End a group of puts. <dd>End a group of puts.
This results in all registered PVListeners being called.</dd> This results in all registered PVListeners being called.</dd>
<dt>getRequesterName</dt>
<dd>virtual method of Requester
</dd>
<dt>message</dt>
<dd>Can be called by implementation code.
The message will be sent to every requester.</dd>
<dt>toString</dt>
<dd>Just calls the top level PVStructure toString method.</dd>
<dt>getTraceLevel</dt> <dt>getTraceLevel</dt>
<dd>This can be used for debugging. There are currently three <dd>This can be used for debugging. There are currently three
levels that are used by existing code. levels that are used by existing code.
@ -748,9 +716,6 @@ public:
bool addListener(PVListenerPtr const &amp; pvListener); bool addListener(PVListenerPtr const &amp; pvListener);
virtual void removeListener(PVListenerPtr const &amp; pvListener); virtual void removeListener(PVListenerPtr const &amp; pvListener);
virtual void postPut(); virtual void postPut();
virtual void message(
std::string const &amp; message,
epics::pvData::MessageType messageType);
protected: protected:
PVRecordFieldPtr getPtrSelf() PVRecordFieldPtr getPtrSelf()
{ {
@ -794,9 +759,6 @@ that holds the data. It has the following methods:
<dt>postPut</dt> <dt>postPut</dt>
<dd>This is called by the code that implements the data interface. <dd>This is called by the code that implements the data interface.
It is called whenever the put method is called.</dd> It is called whenever the put method is called.</dd>
<dt>message</dt>
<dd>Called by implementation code. It calls PVRecord::message after prepending the full
fieldname.</dd>
</dl> </dl>
<h3>class PVRecordStructure</h3> <h3>class PVRecordStructure</h3>
<pre> <pre>
@ -805,7 +767,8 @@ public:
POINTER_DEFINITIONS(PVRecordStructure); POINTER_DEFINITIONS(PVRecordStructure);
PVRecordStructure( PVRecordStructure(
epics::pvData::PVStructurePtr const &amp; pvStructure, epics::pvData::PVStructurePtr const &amp; pvStructure,
PVRecordFieldPtrArrayPtr const &amp; pvRecordField); PVRecordStructurePtr const &amp; parent,
PVRecordPtr const &amp; pvRecord);
virtual ~PVRecordStructure(); virtual ~PVRecordStructure();
virtual void destroy(); virtual void destroy();
PVRecordFieldPtrArrayPtr getPVRecordFields(); PVRecordFieldPtrArrayPtr getPVRecordFields();
@ -901,9 +864,6 @@ public:
epics::pvData::PVStringArrayPtr getRecordNames(); epics::pvData::PVStringArrayPtr getRecordNames();
bool removeRecord(PVRecordPtr const &amp; record); bool removeRecord(PVRecordPtr const &amp; record);
virtual std::string getRequesterName(); virtual std::string getRequesterName();
virtual void message(
std::string const &amp;message,
epics::pvData::MessageType messageType);
private: private:
PVDatabase(); PVDatabase();
}; };
@ -929,98 +889,14 @@ private:
If the record was not in the database false is returned.</dd> If the record was not in the database false is returned.</dd>
<dt>getRequesterName</dt> <dt>getRequesterName</dt>
<dd>Virtual method of Requester</dd> <dd>Virtual method of Requester</dd>
<dt>message</dt>
<dd>Virtual message of Requester.</dd>
</dl> </dl>
<h2>pvAccess</h2> <h2>pvAccess</h2>
<p>This is code that provides an implementation of channelProvider as <p>This is code that provides an implementation of channelProvider as
defined by pvAccess. defined by pvAccess.
It provides access to PVRecords and is access by the server side of remote pvAccess.</p> It provides access to PVRecords and is accessed by the server side of remote pvAccess.
<h3>channelProviderLocal</h3> It uses the copy and monitor facilities from pvDataCPP and connects
<p>This is a complete implementation of channelProvider and , them to a PVRecord.
except for channelRPC, provides a complete implementation of Channel
as defined by pvAccess.
For monitors it calls the code described in the following sections.</p>
<h3>pvCopy</h3>
<p>This provides code that creates a top level PVStructure that is an arbitrary
subset of the fields in the PVStructure from a PVRecord.
In addition it provides code that monitors changes to the fields in a PVRecord.
A client configures the desired set of subfields and monitoring options
via a pvRequest structure.
pvAccess provides a class CreatePVRequest that creates a pvRequest.
The pvCopy code provides the same functionality as the pvCopy code in pvIOCJava.
</p> </p>
<h3>monitorAlgorithm</h3>
<p>Currently all that is implemented is a header file.
The only algorithm currently implemented is <b>onPut</b>
</p>
<h3>monitorFactory</h3>
<h4>Overview</h4>
<p><b>epics::pvData::monitor</b> defines the monitor interfaces
as seen by a client.
See
<a href="http://epics-pvdata.sourceforge.net/docbuild/pvDatabaseCPP/tip/documentation/pvDatabaseCPP.html">pvDatabaseCPP.html</a>
For details.</p>
<p>
monitorFactory implements the
monitoring interfaces for a PVRecord.
It implements queueSize=0 and queueSize&gt;=2.
</p>
<p>
The implementation uses PVCopy and PVCopyMonitor which are implemented in pvCopy.
When PVCopyMonitor tells monitor that changes
have occurred, monitor applies the appropriate algorithm to each changed field.</p>
<p>Currently only algorithm <b>onPut</b> is implemented but,
like pvIOCJava there are plans to support for the following monitor algorithms:</p>
<dl>
<dt>onPut</dt>
<dd>A monitor is issued whenever a put is issued to the field. This is the
default unless the record defines deadbands for a field. An exception is
the top level timeStamp which by default is made onChange and monitor
will not be raised.</dd>
<dt>onChange</dt>
<dd>This provides two options: 1) A monitor is raised whenever a field
changes value, and 2) A monitor will never be raised for the field.</dd>
<dt>deadband</dt>
<dd>The field must be a numeric scalar. Whenever the absolute or percentage
value of the field changes by more than a deadband a monitor is issued.
The record instance can also define deadbands.</dd>
<dt>periodic</dt>
<dd>A monitor is issued at a periodic rate if a put was issued to any field
being monitored.</dd>
</dl>
<h4>MonitorFactory</h4>
<p>MonitorFactory provides the following methods:</p>
<pre>class MonitorFactory
{
static MonitorPtr create(
PVRecordPtr const &amp; pvRecord,
MonitorRequester::shared_pointer const &amp; monitorRequester,
PVStructurePtr const &amp; pvRequest);
static void registerMonitorAlgorithmCreater(
MonitorAlgorithmCreatePtr const &amp; monitorAlgorithmCreate,
String const &amp; algorithmName);
}</pre>
<p>where</p>
<dl>
<dt>create</dt>
<dd>Create a monitor. The arguments are:
<dl>
<dt>pvRecord</dt>
<dd>The record being monitored.</dd>
<dt>monitorRequester</dt>
<dd>The monitor requester. This is the code to which monitot events
will be delivered.</dd>
<dt>pvRequest</dt>
<dd>The request options</dd>
</dl>
</dd>
<dt>registerMonitorAlgorithmCreater</dt>
<dd>Called by code that implements a monitor algorithm.</dd>
</dl>
<h2>special</h2> <h2>special</h2>
<p>This section provides two useful record support modules <p>This section provides two useful record support modules
and one that is used for testing.</p> and one that is used for testing.</p>
@ -1030,7 +906,7 @@ the trace level of a record. It follows the pattern of a channelPutGet
record: record:
<pre> <pre>
traceRecord traceRecord
structure arguments structure argument
string recordName string recordName
int level 0 int level 0
structure result structure result
@ -1071,17 +947,18 @@ if(!result) cout&lt;&lt; "record " &lt;&lt; recordName &lt;&lt; " not added" &lt
</pre> </pre>
</p> </p>
<h3>recordList</h3> <h3>recordList</h3>
<p>This implements a PVRecord that allows a client to set <p>This implements a PVRecord that allows a client to
the trace level of a record. It follows the pattern of a channelPutGet get the names of all the PVRecords in the PVDatabase.
It follows the pattern of a channelPutGet
record: record:
<pre> <pre>
traceRecord traceRecord
structure arguments structure argument
string database master string database master
string regularExpression .* string regularExpression .*
structure result structure result
string status string status
string[] names string[] name
</pre> </pre>
where: where:
<dl> <dl>
@ -1092,8 +969,8 @@ where:
returned.</dd> returned.</dd>
<dt>status</dt> <dt>status</dt>
<dd>The status of a putGet request.</dd> <dd>The status of a putGet request.</dd>
<dt>names</dt> <dt>name</dt>
<dd>The list of record names.</dd> <dd>The array of record names.</dd>
</dl> </dl>
<p>Note that swtshell, which is a Java GUI tool, 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. requires that a record of this type is present and calls it.
@ -1101,7 +978,7 @@ Thus user code does not have to use a channelGetPut to get the list
of record names.</p> of record names.</p>
<p>testExampleServerMain.cpp has an example of how to create a traceRecord: <p>testExampleServerMain.cpp has an example of how to create a traceRecord:
<pre> <pre>
recordName = "laptoprecordListPGRPC"; recordName = "recordListPGRPC";
pvRecord = RecordListRecord::create(recordName); pvRecord = RecordListRecord::create(recordName);
result = master-&gt;addRecord(pvRecord); result = master-&gt;addRecord(pvRecord);
if(!result) cout&lt;&lt; "record " &lt;&lt; recordName &lt;&lt; " not added" &lt;&lt; endl; if(!result) cout&lt;&lt; "record " &lt;&lt; recordName &lt;&lt; " not added" &lt;&lt; endl;
@ -1151,6 +1028,7 @@ exampleServer
exampleServerInclude.dbd exampleServerInclude.dbd
exampleServerMain.cpp exampleServerMain.cpp
exampleServerRegister.cpp exampleServerRegister.cpp
exampleServerRegister.dbd
ioc ioc
Db Db
... ...
@ -1165,7 +1043,10 @@ exampleServer
where where
<dl> <dl>
<dt>ExampleRELEASE.local</dt> <dt>ExampleRELEASE.local</dt>
<dd>This is the file that must be copied to RELEASE.local <dd>
If you make a copy of exampleServer and use it
to create a new server,
This is the file that must be copied to RELEASE.local
and edited.</dd> and edited.</dd>
<dt>exampleServer.h</dt> <dt>exampleServer.h</dt>
<dd>The header file for the service.</dd> <dd>The header file for the service.</dd>
@ -1182,6 +1063,9 @@ where
<dt>exampleServerRegister.cpp</dt> <dt>exampleServerRegister.cpp</dt>
<dd>This has the code to start the service via the following iocsh <dd>This has the code to start the service via the following iocsh
command. command.
<dt>exampleServerRegister.dbd</dt>
<dd>This is the file that is used to create the shell command
exampleServerCreateRecord.</dd>
<pre> <pre>
exampleServerCreateRecord exampleServer exampleServerCreateRecord exampleServer
</pre> </pre>
@ -1273,19 +1157,20 @@ private:
ExampleServerPtr ExampleServer::create( ExampleServerPtr ExampleServer::create(
std::string const &amp; recordName) std::string const &amp; recordName)
{ {
StandardPVFieldPtr standardPVField = getStandardPVField(); StandardFieldPtr standardField = getStandardField();
FieldCreatePtr fieldCreate = getFieldCreate();
PVDataCreatePtr pvDataCreate = getPVDataCreate(); PVDataCreatePtr pvDataCreate = getPVDataCreate();
PVStructurePtr pvArgument = standardPVField-&gt;scalar(pvString,""); StructureConstPtr topStructure = fieldCreate-&gt;createFieldBuilder()-&gt;
PVStructurePtr pvResult = standardPVField-&gt;scalar(pvString,"timeStamp"); addNestedStructure("argument")-&gt;
StringArray names; add("value",pvString)-&gt;
names.reserve(2); endNested()-&gt;
PVFieldPtrArray fields; addNestedStructure("result") -&gt;
fields.reserve(2); add("value",pvString) -&gt;
names.push_back("argument"); add("timeStamp",standardField-&gt;timeStamp()) -&gt;
fields.push_back(pvArgument); endNested()-&gt;
names.push_back("result"); createStructure();
fields.push_back(pvResult); PVStructurePtr pvStructure = pvDataCreate-&gt;createPVStructure(topStructure);
PVStructurePtr pvStructure = pvDataCreate-&gt;createPVStructure(names,fields);
ExampleServerPtr pvRecord( ExampleServerPtr pvRecord(
new ExampleServer(recordName,pvStructure)); new ExampleServer(recordName,pvStructure));
if(!pvRecord-&gt;init()) pvRecord.reset(); if(!pvRecord-&gt;init()) pvRecord.reset();
@ -1364,15 +1249,23 @@ int main(int argc,char *argv[])
String recordName("exampleServer"); String recordName("exampleServer");
PVRecordPtr pvRecord = ExampleServer::create(recordName); PVRecordPtr pvRecord = ExampleServer::create(recordName);
bool result = master-&gt;addRecord(pvRecord); bool result = master-&gt;addRecord(pvRecord);
cout &lt;&lt; "result of addRecord " &lt;&lt; recordName &lt;&lt; " " &lt;&lt; result &lt;&lt; endl; if(!result) cout&lt;&lt; "record " &lt;&lt; recordName &lt;&lt; " not added" &lt;&lt; endl;
pvRecord.reset(); recordName = "traceRecordPGRPC";
startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true); pvRecord = TraceRecord::create(recordName);
cout &lt;&lt; "exampleServer\n"; result = master-&gtr;addRecord(pvRecord);
if(!result) cout&lt;&lt; "record " &lt;&lt; recordName &lt;&lt; " not added" &lt;&lt; endl;
recordName = "recordListPGRPC";
pvRecord = RecordListRecord::create(recordName);
result = master-&gtr;addRecord(pvRecord);
if(!result) cout&lt;&lt; "record " &lt;&lt; recordName &lt;&lt; " not added" &lt;&lt; endl;
ServerContext::shared_pointer pvaServer =
startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
PVStringArrayPtr pvNames = master-&gtr;getRecordNames();
shared_vector&lt;const string&gtr; names = pvNames-&gtr;view();
for(size_t i=0; i&lt;names.size(); ++i) cout &lt;&lt; names[i] &lt;&lt; endl;
string str; string str;
while(true) { while(true) {
cout &lt;&lt; "Type exit to stop: \n"; cout &lt;&lt; "Type exit to stop: \n";
getline(cin,str);
if(str.compare("exit")==0) break;
} }
return 0; return 0;
@ -1382,9 +1275,9 @@ This:
<ul> <ul>
<li>Gets a pointer to the master database.</li> <li>Gets a pointer to the master database.</li>
<li>Creates the local Channel Provider. This starts the pvAccess server.</li> <li>Creates the local Channel Provider. This starts the pvAccess server.</li>
<li>Creates a ExampleServer record with the name exampleServer <li>Creates record exampleServer </li>
</li> <li>creates records traceRecordPGRPC and recordListPGRPC</li>
<li>Prints exampleServer on standard out.</li> <li>lists all the records</li>
<li>Runs forever until the user types exit on standard in.</li> <li>Runs forever until the user types exit on standard in.</li>
</ul> </ul>
<h3>V3IOC exampleServer</h3> <h3>V3IOC exampleServer</h3>
@ -1397,16 +1290,20 @@ mrk&gt; ../../../bin/linux-x86_64/exampleServer st.cmd
<p>You can then issue the commands dbl and pvdbl: <p>You can then issue the commands dbl and pvdbl:
<pre> <pre>
epics&gt; dbl epics&gt; dbl
double01 pvdouble
pvcounter
pvenum
pvdoubleArray
pvstringArray
epics&gt; pvdbl epics&gt; pvdbl
exampleServer exampleServer
epics&gt; epics&gt;
</pre> </pre>
double01 is a v3Record. dbl shows the V3 records.
exampleServer is a pvRecord. pvdbl shows the pvRecords.
</p> </p>
<p> <p>
It starts pvASrv so that the V3 records can be accessed via Channel Access It starts pvaSrv so that the V3 records can be accessed via Channel Access
or via PVAccess.</p> or via PVAccess.</p>
<h2>exampleDatabase</h2> <h2>exampleDatabase</h2>
@ -1511,8 +1408,8 @@ Once the client and local provider code has started then the following creates a
</p> </p>
<pre> <pre>
PVDatabasePtr master = PVDatabase::getMaster(); PVDatabasePtr master = PVDatabase::getMaster();
ChannelAccess::shared_pointer channelAccess = getChannelAccess(); ChannelProvider::shared_pointer provider =
ChannelProvider::shared_pointer provider = channelAccess-&gt;getProvider(providerName); getChannelProviderRegistry()-&gt;getProvider(providerName);
Channel::shared_pointer channel = provider-&gt;createChannel(channelName,channelRequester); Channel::shared_pointer channel = provider-&gt;createChannel(channelName,channelRequester);
</pre> </pre>

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,8 @@ dbLoadRecords("db/dbDouble.db","name=double05")
dbLoadRecords("db/dbStringArray.db","name=stringArray01") dbLoadRecords("db/dbStringArray.db","name=stringArray01")
dbLoadRecords("db/dbEnum.db","name=enum01") dbLoadRecords("db/dbEnum.db","name=enum01")
dbLoadRecords("db/dbCounter.db","name=counter01"); dbLoadRecords("db/dbCounter.db","name=counter01");
dbLoadRecords("db/dbArray.db","name=doubleArray,type=DOUBLE");
cd ${TOP}/iocBoot/${IOC} cd ${TOP}/iocBoot/${IOC}
iocInit() iocInit()

View File

@ -147,17 +147,20 @@ void ExampleDatabase::create()
createVariantUnionArrayRecord(master,"exampleVariantUnionArray"); createVariantUnionArrayRecord(master,"exampleVariantUnionArray");
recordName = "examplePowerSupply"; recordName = "examplePowerSupply";
PVStructurePtr pvStructure = createPowerSupply(); PVStructurePtr pvStructure = createPowerSupply();
PowerSupplyPtr psr = PowerSupplyPtr psr = PowerSupply::create(recordName,pvStructure);
PowerSupply::create(recordName,pvStructure); if(psr==NULL) {
if(psr.get()==NULL) { cout << "PowerSupply::create failed" << endl;
cout << "PowerSupply::create failed" << endl; } else {
return; result = master->addRecord(psr);
if(!result) cout<< "record " << recordName << " not added" << endl;
} }
result = master->addRecord(psr);
if(!result) cout<< "record " << recordName << " not added" << endl;
recordName = "laptoprecordListPGRPC"; recordName = "laptoprecordListPGRPC";
pvRecord = RecordListRecord::create(recordName); pvRecord = RecordListRecord::create(recordName);
result = master->addRecord(pvRecord); if(pvRecord==NULL) {
if(!result) cout<< "record " << recordName << " not added" << endl; cout << "RecordListRecord::create failed" << endl;
} else {
result = master->addRecord(pvRecord);
if(!result) cout<< "record " << recordName << " not added" << endl;
}
} }

View File

@ -1,4 +1,5 @@
include "base.dbd" include "base.dbd"
include "PVAClientRegister.dbd"
include "PVAServerRegister.dbd" include "PVAServerRegister.dbd"
include "registerChannelProviderLocal.dbd" include "registerChannelProviderLocal.dbd"
include "dbPv.dbd" include "dbPv.dbd"

View File

@ -9,7 +9,7 @@
* @date 2013.04.02 * @date 2013.04.02
*/ */
#include <pv/standardPVField.h> #include <pv/standardField.h>
#include <pv/exampleServer.h> #include <pv/exampleServer.h>
using namespace epics::pvData; using namespace epics::pvData;
@ -23,19 +23,20 @@ namespace epics { namespace exampleServer {
ExampleServerPtr ExampleServer::create( ExampleServerPtr ExampleServer::create(
string const & recordName) string const & recordName)
{ {
StandardPVFieldPtr standardPVField = getStandardPVField(); StandardFieldPtr standardField = getStandardField();
FieldCreatePtr fieldCreate = getFieldCreate();
PVDataCreatePtr pvDataCreate = getPVDataCreate(); PVDataCreatePtr pvDataCreate = getPVDataCreate();
PVStructurePtr pvArgument = standardPVField->scalar(pvString,""); StructureConstPtr topStructure = fieldCreate->createFieldBuilder()->
PVStructurePtr pvResult = standardPVField->scalar(pvString,"timeStamp"); addNestedStructure("argument")->
StringArray names; add("value",pvString)->
names.reserve(2); endNested()->
PVFieldPtrArray fields; addNestedStructure("result") ->
fields.reserve(2); add("value",pvString) ->
names.push_back("argument"); add("timeStamp",standardField->timeStamp()) ->
fields.push_back(pvArgument); endNested()->
names.push_back("result"); createStructure();
fields.push_back(pvResult); PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure);
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(names,fields);
ExampleServerPtr pvRecord( ExampleServerPtr pvRecord(
new ExampleServer(recordName,pvStructure)); new ExampleServer(recordName,pvStructure));
if(!pvRecord->init()) pvRecord.reset(); if(!pvRecord->init()) pvRecord.reset();

View File

@ -22,6 +22,7 @@
#include <pv/standardPVField.h> #include <pv/standardPVField.h>
#include <pv/exampleServer.h> #include <pv/exampleServer.h>
#include <pv/traceRecord.h> #include <pv/traceRecord.h>
#include <pv/recordList.h>
#include <pv/channelProviderLocal.h> #include <pv/channelProviderLocal.h>
#include <pv/serverContext.h> #include <pv/serverContext.h>
@ -42,15 +43,20 @@ int main(int argc,char *argv[])
recordName = "exampleServer"; recordName = "exampleServer";
pvRecord = ExampleServer::create(recordName); pvRecord = ExampleServer::create(recordName);
result = master->addRecord(pvRecord); result = master->addRecord(pvRecord);
cout << "result of addRecord " << recordName << " " << result << endl; if(!result) cout<< "record " << recordName << " not added" << endl;
recordName = "traceRecordPGRPC"; recordName = "traceRecordPGRPC";
pvRecord = TraceRecord::create(recordName); pvRecord = TraceRecord::create(recordName);
result = master->addRecord(pvRecord); result = master->addRecord(pvRecord);
if(!result) cout<< "record " << recordName << " not added" << endl; if(!result) cout<< "record " << recordName << " not added" << endl;
pvRecord.reset(); recordName = "recordListPGRPC";
pvRecord = RecordListRecord::create(recordName);
result = master->addRecord(pvRecord);
if(!result) cout<< "record " << recordName << " not added" << endl;
ServerContext::shared_pointer pvaServer = ServerContext::shared_pointer pvaServer =
startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true); startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
cout << "exampleServer\n"; PVStringArrayPtr pvNames = master->getRecordNames();
shared_vector<const string> names = pvNames->view();
for(size_t i=0; i<names.size(); ++i) cout << names[i] << endl;
string str; string str;
while(true) { while(true) {
cout << "Type exit to stop: \n"; cout << "Type exit to stop: \n";

View File

@ -24,26 +24,16 @@ RecordListRecordPtr RecordListRecord::create(
{ {
FieldCreatePtr fieldCreate = getFieldCreate(); FieldCreatePtr fieldCreate = getFieldCreate();
PVDataCreatePtr pvDataCreate = getPVDataCreate(); PVDataCreatePtr pvDataCreate = getPVDataCreate();
StringArray argNames(2); StructureConstPtr topStructure = fieldCreate->createFieldBuilder()->
FieldConstPtrArray argFields(2); addNestedStructure("argument")->
argNames[0] = "database"; add("database",pvString)->
argFields[0] = fieldCreate->createScalar(pvString); add("regularExpression",pvString)->
argNames[1] = "regularExpression"; endNested()->
argFields[1] = fieldCreate->createScalar(pvString); addNestedStructure("result") ->
StringArray resNames(2); add("status",pvString) ->
FieldConstPtrArray resFields(2); addArray("name",pvString) ->
resNames[0] = "status"; endNested()->
resFields[0] = fieldCreate->createScalar(pvString); createStructure();
resNames[1] = "names";
resFields[1] = fieldCreate->createScalarArray(pvString);
StringArray topNames(2);
FieldConstPtrArray topFields(2);
topNames[0] = "argument";
topFields[0] = fieldCreate->createStructure(argNames,argFields);
topNames[1] = "result";
topFields[1] = fieldCreate->createStructure(resNames,resFields);
StructureConstPtr topStructure =
fieldCreate->createStructure(topNames,topFields);
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure); PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure);
RecordListRecordPtr pvRecord( RecordListRecordPtr pvRecord(
new RecordListRecord(recordName,pvStructure)); new RecordListRecord(recordName,pvStructure));
@ -78,21 +68,21 @@ bool RecordListRecord::init()
if(regularExpression.get()==NULL) return false; if(regularExpression.get()==NULL) return false;
status = pvStructure->getStringField("result.status"); status = pvStructure->getStringField("result.status");
if(status.get()==NULL) return false; if(status.get()==NULL) return false;
PVFieldPtr pvField = pvStructure->getSubField("result.names"); PVFieldPtr pvField = pvStructure->getSubField("result.name");
if(pvField.get()==NULL) { if(pvField.get()==NULL) {
std::cerr << "no result.names" << std::endl; std::cerr << "no result.name" << std::endl;
return false; return false;
} }
names = static_pointer_cast<PVStringArray>( name = static_pointer_cast<PVStringArray>(
pvStructure->getScalarArrayField("result.names",pvString)); pvStructure->getScalarArrayField("result.name",pvString));
if(names.get()==NULL) return false; if(name.get()==NULL) return false;
return true; return true;
} }
void RecordListRecord::process() void RecordListRecord::process()
{ {
PVStringArrayPtr pvNames = PVDatabase::getMaster()->getRecordNames(); PVStringArrayPtr pvNames = PVDatabase::getMaster()->getRecordNames();
names->replace(pvNames->view()); name->replace(pvNames->view());
string message(""); string message("");
if(database->get().compare("master")!=0) { if(database->get().compare("master")!=0) {
message += " can only access master "; message += " can only access master ";

View File

@ -37,7 +37,7 @@ private:
epics::pvData::PVStringPtr database; epics::pvData::PVStringPtr database;
epics::pvData::PVStringPtr regularExpression; epics::pvData::PVStringPtr regularExpression;
epics::pvData::PVStringPtr status; epics::pvData::PVStringPtr status;
epics::pvData::PVStringArrayPtr names; epics::pvData::PVStringArrayPtr name;
}; };
}} }}

View File

@ -24,24 +24,15 @@ TraceRecordPtr TraceRecord::create(
{ {
FieldCreatePtr fieldCreate = getFieldCreate(); FieldCreatePtr fieldCreate = getFieldCreate();
PVDataCreatePtr pvDataCreate = getPVDataCreate(); PVDataCreatePtr pvDataCreate = getPVDataCreate();
StringArray topNames(2); StructureConstPtr topStructure = fieldCreate->createFieldBuilder()->
FieldConstPtrArray topFields(2); addNestedStructure("argument")->
topNames[0] = "argument"; add("recordName",pvString)->
topNames[1] = "result"; add("level",pvInt)->
StringArray argNames(2); endNested()->
FieldConstPtrArray argFields(2); addNestedStructure("result") ->
argNames[0] = "recordName"; add("status",pvString) ->
argNames[1] = "level"; endNested()->
argFields[0] = fieldCreate->createScalar(pvString); createStructure();
argFields[1] = fieldCreate->createScalar(pvInt);
topFields[0] = fieldCreate->createStructure(argNames,argFields);
StringArray resNames(1);
FieldConstPtrArray resFields(1);
resNames[0] = "status";
resFields[0] = fieldCreate->createScalar(pvString);
topFields[1] = fieldCreate->createStructure(resNames,resFields);
StructureConstPtr topStructure =
fieldCreate->createStructure(topNames,topFields);
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure); PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure);
TraceRecordPtr pvRecord( TraceRecordPtr pvRecord(
new TraceRecord(recordName,pvStructure)); new TraceRecord(recordName,pvStructure));

View File

@ -15,6 +15,9 @@
using namespace epics::pvData; using namespace epics::pvData;
using std::string; using std::string;
using std::cout;
using std::cerr;
using std::endl;
namespace epics { namespace pvDatabase { namespace epics { namespace pvDatabase {
@ -24,24 +27,23 @@ PVStructurePtr createPowerSupply()
StandardFieldPtr standardField = getStandardField(); StandardFieldPtr standardField = getStandardField();
PVDataCreatePtr pvDataCreate = getPVDataCreate(); PVDataCreatePtr pvDataCreate = getPVDataCreate();
size_t nfields = 5;
StringArray names;
names.reserve(nfields);
FieldConstPtrArray powerSupply;
powerSupply.reserve(nfields);
names.push_back("alarm");
powerSupply.push_back(standardField->alarm());
names.push_back("timeStamp");
powerSupply.push_back(standardField->timeStamp());
string properties("alarm,display");
names.push_back("voltage");
powerSupply.push_back(standardField->scalar(pvDouble,properties));
names.push_back("power");
powerSupply.push_back(standardField->scalar(pvDouble,properties));
names.push_back("current");
powerSupply.push_back(standardField->scalar(pvDouble,properties));
return pvDataCreate->createPVStructure( return pvDataCreate->createPVStructure(
fieldCreate->createStructure(names,powerSupply)); fieldCreate->createFieldBuilder()->
add("alarm",standardField->alarm()) ->
add("timeStamp",standardField->timeStamp()) ->
addNestedStructure("power") ->
add("value",pvDouble) ->
add("alarm",standardField->alarm()) ->
endNested()->
addNestedStructure("voltage") ->
add("value",pvDouble) ->
add("alarm",standardField->alarm()) ->
endNested()->
addNestedStructure("current") ->
add("value",pvDouble) ->
add("alarm",standardField->alarm()) ->
endNested()->
createStructure());
} }
PowerSupplyPtr PowerSupply::create( PowerSupplyPtr PowerSupply::create(
@ -77,62 +79,40 @@ bool PowerSupply::init()
PVFieldPtr pvField; PVFieldPtr pvField;
bool result; bool result;
pvField = pvStructure->getSubField("timeStamp"); pvField = pvStructure->getSubField("timeStamp");
if(pvField.get()==NULL) { if(pvField==NULL) {
std::cerr << "no timeStamp" << std::endl; cerr << "no timeStamp" << endl;
return false; return false;
} }
result = pvTimeStamp.attach(pvField); result = pvTimeStamp.attach(pvField);
if(!result) { if(!result) {
std::cerr << "no timeStamp" << std::endl; cerr << "no timeStamp" << endl;
return false; return false;
} }
pvField = pvStructure->getSubField("alarm"); pvField = pvStructure->getSubField("alarm");
if(pvField.get()==NULL) { if(pvField==NULL) {
std::cerr << "no alarm" << std::endl; cerr << "no alarm" << endl;
return false; return false;
} }
result = pvAlarm.attach(pvField); result = pvAlarm.attach(pvField);
if(!result) { if(!result) {
std::cerr << "no alarm" << std::endl; cerr << "no alarm" << endl;
return false; return false;
} }
string name; pvCurrent = pvStructure->getSubField<PVDouble>("current.value");
name = "current.value"; if(pvCurrent==NULL) {
pvField = pvStructure->getSubField(name); cerr << "no current\n";
if(pvField.get()==NULL) {
name = "current";
pvField = pvStructure->getSubField(name);
}
if(pvField.get()==NULL) {
std::cerr << "no current" << std::endl;
return false; return false;
} }
pvCurrent = pvStructure->getDoubleField(name); pvVoltage = pvStructure->getSubField<PVDouble>("voltage.value");
if(pvCurrent.get()==NULL) return false; if(pvVoltage==NULL) {
name = "voltage.value"; cerr << "no current\n";
pvField = pvStructure->getSubField(name);
if(pvField.get()==NULL) {
name = "voltage";
pvField = pvStructure->getSubField(name);
}
if(pvField.get()==NULL) {
std::cerr << "no voltage" << std::endl;
return false; return false;
} }
pvVoltage = pvStructure->getDoubleField(name); pvPower = pvStructure->getSubField<PVDouble>("power.value");
if(pvVoltage.get()==NULL) return false; if(pvPower==NULL) {
name = "power.value"; cerr << "no powert\n";
pvField = pvStructure->getSubField(name);
if(pvField.get()==NULL) {
name = "power";
pvField = pvStructure->getSubField(name);
}
if(pvField.get()==NULL) {
std::cerr << "no power" << std::endl;
return false; return false;
} }
pvPower = pvStructure->getDoubleField(name);
if(pvPower.get()==NULL) return false;
return true; return true;
} }