interim commit

This commit is contained in:
Marty Kraimer
2013-04-12 08:06:48 -04:00
parent 500ecfd4ad
commit d67517b405
13 changed files with 1910 additions and 393 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, 11-Dec-2012</h2>
<h2 class="nocount">EPICS v4 Working Group, Working Draft, 09-Apr-2013</h2>
<dl>
<dt>Latest version:</dt>
<dd><a
@ -46,11 +46,11 @@
</dd>
<dt>This version:</dt>
<dd><a
href="http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvDatabaseCPP/raw-file/tip/documentation/pvDatabaseCPP_20121211.html">pvDatabaseCPP20121211.html</a>
href="http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvDatabaseCPP/raw-file/tip/documentation/pvDatabaseCPP_20130409.html">pvDatabaseCPP20131211.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_20121127.html">pvDatabaseCPP_20121127.html</a>
href="http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvDatabaseCPP/raw-file/tip/documentation/pvDatabaseCPP_20131211.html">pvDatabaseCPP20131211.html</a>
</dd>
<dt>Editors:</dt>
<dd>Marty Kraimer, BNL</dd>
@ -67,7 +67,7 @@ href="http://epics-pvdata.sourceforge.net/LICENSE.html">EPICS open source licens
which is a framework for implementing a network accessable database of smart memory resident
records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by
pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess.
The framework must be extended in order to create record instances.
The framework can be extended in order to create record instances that implements services.
The minimum that an extenson must provide is a top level PVStructure and a process method
but the framework provides for complex extensions.</p>
@ -97,7 +97,7 @@ V4 control system programming environment:</p>
<h2 class="nocount">Status of this Document</h2>
<p>This is the 11-Dec-2012 version of the definition of pvDatabaseCPP.
<p>This is the 09-Apr-2013 version of the definition of pvDatabaseCPP.
</p>
<p>This is the beginning of the implementation of pvDataBaseCPP.
It describes the features that will be provided.
@ -113,22 +113,16 @@ The class definition for PVDatabase are defined but not implemented.</p>
<h2>Introduction</h2>
<h3>Overview</h3>
<p>This document descibes a C++ implementation of some of the components in pvIOCJava.
It extracts the core components required to create a network accessible database of smart
<p>A brief description of a pvDatabase is that it is a set of network accessible, smart,
memory resident records.
pvDatabaseCPP does not and will not implement any of the specialized support that pvIOCJava
provides. Instead other projects will implement the specialized support.
It is expected that many services will be created that do not require the full features provided
by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of
them named pvDatabaseJava.
</p>
<p>A brief description of a pvDatase is that it is a network accessible set of smart memory resident
records. Each record has data composed of a top level PVStructure. Each record has a name which is
the channelName for pvAccess. A local Channel Provider implements the complete ChannelProvider and
Each record has data composed of a top level PVStructure.
Each record has a name which is the channelName for pvAccess.
A local Channel Provider implements the complete ChannelProvider and
Channel interfaces as defined by pvAccess.
The local provider provides access to the records in the pvDatabase.
This local provider is accessed by the remote pvAccess server.
A record is smart because code can be attached to a record.</p>
A record is smart because code can be attached to a record, which is accessed via a method named <b>process</b>.</p>
<p>This document describes components that provides the following features:
<dl>
<dt>database<dt>
@ -142,135 +136,182 @@ A record is smart because code can be attached to a record.</p>
<dd>This is a database of pvRecords.
Records can be added and removed from a database.</dd>
</dl>
<dt>localChannelProvider</dt>
<dd>This is a complete implementation of ChannelProvider and Channel as defined by pvAccess.
<dt>pvAccess</dt>
<dd>This is a complete implementation of a local 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>
</dl>
<p><b>database</b> does not itself implement pvRecord instances.
Instead it provides a base classes that make it easy to create record instances.
What does have to be implemented is a top
<p><b>database</b>
provides base classes that make it easy to create record instances.
The code attached to each record must create the top
level PVStructure and the following two methods:</p>
<dl>
<dt>init</dt>
<dd>This is a method for initializing the support.
It returns <b>true</b> if successful and <b>false</b> otherwise.
</dd>
<dt>process</dt>
<dd>This is what makes a record <b>smart</b>.
What process does is up to the implementation except that it must decide if
it's execution model is synchronous or asynchronous.
Synchronous means that when process returns the processing is complete.
Asynchronous means that when process returns the processing is <b>not</b> complete.
Instead process invokes other threads that will complete the processing at a later time.</dd>
<dt>isSynchronous</dt>
<dd>Which execution model is being implemented.</dd>
</dd>
</dl>
<h3>Relationship with pvIOCJava.</h3>
<p>This document descibes a C++ implementation of some of the components in pvIOCJava,
which also implements a pvDatabase.
It extracts the core components required to create a network accessible database of smart
memory resident records.
pvDatabaseCPP does not implement any of the specialized support that pvIOCJava
provides.
It is expected that many services will be created that do not require the full features provided
by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of
them named pvDatabaseJava.</p>
<p>Similar to epics base, pvIOCJava implements the concept of synchronous and asynchronous record processing.
For pvDatabaseCPP the process method is allowed to block.
Until a need is demonstrated this will remain true.
The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess.
The server side of remote pvAccess creates two threads for each client and always accesses
a record via these threads.
It is expected that these threads will be sufficient to efficently handle all channel methods except
channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests.
If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility,
then the scanning facility will have to provide some way of handling process requests that block.</p>
</p>
<h3>Example PVRecord Extension</h3>
<p>Directory <b>example/record</b> has an example PVRecord implementation.
It implements a counter.
The top level structure is:</p>
<p>The example implements a simple counter.
The example can be run on linux as follows:</p>
<pre>
structure
long value
</pre>
<p><b>NOTE:</b> The example compiles but does not build because nothing
is implemented.</p>
mrk&gt; pwd
/home/hg/pvDatabaseCPP
mrk&gt; bin/linux-x86_64/exampleCounter
<h4>exampleRecord.h</h4>
<p>This is the class description.
The example extends PVRecord.</p>
</pre>
<p>The example consists of two components:</p>
<dl>
<dt>ExampleCounter.h</dt>
<dd>The source code for the counter.</dd>
<dt>exampleCounterMain.cpp</dt>
<dd>A main program that runs the example so that it can be accessed
by a <b>pvAccess</b> client.</dd>
</dl>
<h4>ExampleCounter.h</h4>
<p>The example resides in <b>src/database</b>.
The complete implementation is in the header file.
A serious implementation would probably break the code into two files:
1) a header, and 2) the implementation. The description consists of</p>
<pre>
class ExampleRecord :
public virtual PVRecord
class ExampleCounter;
typedef std::tr1::shared_ptr&lt;ExampleCounter&gt; ExampleCounterPtr;
class ExampleCounter :
public PVRecord
{
public:
POINTER_DEFINITIONS(ExampleRecord);
static PVRecordPtr create(epics::pvData::String const &amp; recordName);
virtual ~ExampleRecord();
virtual bool isSynchronous();
virtual void process(
epics::pvDatabase::RecordProcessRequesterPtr const &amp;processRequester);
POINTER_DEFINITIONS(ExampleCounter);
static ExampleCounterPtr create(
epics::pvData::String const &amp; recordName);
virtual ~ExampleCounter() {}
virtual bool init();
virtual void process();
private:
ExampleRecord(epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure,
epics::pvData::PVLongPtr const &amp;pvValue);
ExampleCounter(epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
epics::pvData::PVLongPtr pvValue;
};
</pre>
<p>where</p>
<dl>
<dt>create<dt>
<dd>This is example specific. See the implemention for details.</dd>
<dt>~ExampleRecord<dt>
<dd>This is example specific but each support should provide
a similar static method.
</dd>
<dt>~ExampleCounter<dt>
<dd>The destructor must be declared virtual.</dd>
<dt>isSynchronous<dt>
<dd>The implementation must say if process is synchronous or asynchronous.</dd>
<dt>init<dt>
<dd>A method to initialize the support. It returns <b>true</b>
if initialization is successful and <b>false</b> if not.
NOTE that this is a virtual method of PVRecord itself.</dd>
<dt>process<dt>
<dd><b>The</b> implementation.</dd>
<dt>ExampleRecord<dt>
<dd>
This again is a virtual method of PVRecord.
</dd>
<dt>ExampleCounter<dt>
<dd>For the example this is private.</dd>
<dt>pvValue</dt>
<dd>This is the field of the top level structure that <b>process</b>
accesses.
</dd>
<dl>
<h4>exampleRecord.cpp</h4>
<p>This is the class implementation.</p>
<p>The implementation of <b>create</b> is:</p>
<pre>
ExampleRecord::~ExampleRecord(){}
PVRecordPtr ExampleRecord::create(String const &amp; recordName)
ExampleCounterPtr ExampleCounter::create(
epics::pvData::String const &amp; recordName)
{
String properties;
PVStructurePtr pvStructure = getStandardPVField()-&gt;scalar(pvLong,properties);
PVLongPtr pvValue = pvStructure-&gt;getLongField("value");
PVRecordPtr pvRecord(new ExampleRecord(recordName,pvStructure,pvValue));
epics::pvData::PVStructurePtr pvStructure =
epics::pvData::getStandardPVField()-&gt;scalar(epics::pvData::pvDouble,"");
ExampleCounterPtr pvRecord(
new ExampleCounter(recordName,pvStructure));
if(!pvRecord-&gt;init()) pvRecord.reset();
return pvRecord;
}
ExampleRecord::ExampleRecord(
String const &amp; recordName,
PVStructurePtr const &amp; pvStructure,
PVLongPtr const &amp;pvValue)
: PVRecord(recordName,pvStructure),
pvValue(pvValue)
{}
bool ExampleRecord::isSynchronous() {return true;}
void ExampleRecord::process(
RecordProcessRequesterPtr const &amp;processRequester,bool alreadyLocked)
</pre>
This:
<ul>
<li>Creates the top level structure.</li>
<li>Creates a <b>ExampleCounterPtr</b> via the constructor.</li>
<li>Calls init and if it fails resets the shared pointer.</li>
<li>Returns the shared pointer to the newly created record.</li>
</ul>
<p>The private constructor is just:</p>
<pre>
ExampleCounter::ExampleCounter(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure)
: PVRecord(recordName,pvStructure)
{ }
</pre>
The example is very simple. It just calls the base class constructor.
<p>The implementation of <b>init</b> is:</p>
<pre>
bool ExampleCounter::init()
{
if(!alreadyLocked) lock();
pvValue-&gt;put(pvValue-&gt;get() + 1);
processRequester-&gt;recordProcessResult(Status::Ok);
unlock();
processRequester-&gt;recordProcessComplete();
dequeueProcessRequest(processRequester);
initPVRecord();
epics::pvData::PVFieldPtr pvField;
pvValue = getPVStructure()-&gt;getLongField("value");
if(pvValue.get()==NULL) return false;
return true;
}
</pre>
<p>where</p>
<dl>
<dt>create<dt>
<dd>Creates a PVStructure with a single subfield named value.
It gets the interface to the value field.
It then creates an ExampleRecord and returns it.
</dd>
<dt>~ExampleRecord<dt>
<dd>Does not have to do anything because of shared pointers.</dd>
<dt>ExampleRecord<dt>
<dd>Calls the base class constructor and sets pvValue.</dd>
<dt>isSynchronous<dt>
<dd>The example is synchronous.</dd>
<dt>process<dt>
<dd>Gets the curent value, increments it, and puts the new value.
It than calls two processRequester callbacks.</dd>
<dl>
<h4>exampleRecordMain.cpp</h4>
<p>This is a main for creating and running the example.</p>
This:
<ul>
<li>Calls <b>initRecord</b> which is implemented by the base class.
It MUST be called.</li>
<li>Calls <b>getLongField</b> to get the interface to the value field,
which must be a scalar with type long.</li>
<li>If a long value field was not found it returns false.</li>
<li>Returns true</li>
</ul>
<p>The implementation of <b>process</b> is:</p>
<pre>
void ExampleCounter::process()
{
pvValue-&gt;put(pvValue-&gt;get() + 1.0);
}
</pre>
It just adds 1.0 to the current value.
<h4>exampleCounterMain.cpp</h4>
<p>This is in <b>test/server</b>.
The main program is:</p>
<pre>
int main(int argc,char *argv[])
{
String recordName("exampleRecord");
PVRecordPtr pvRecord = ExampleRecord::create(recordName);
PVDatabasePtr pvDatabase = PVDatabase::getMaster();
pvDatabase-&gt;addRecord(pvRecord);
cout &lt;&lt; recordName &lt;&lt; "\n";
PVDatabasePtr master = PVDatabase::getMaster();
ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
String recordName("exampleCounter");
PVRecordPtr pvRecord = ExampleCounter::create(recordName);
bool result = master-&gt;addRecord(pvRecord);
cout &lt;&lt; "result of addRecord " &lt;&lt; recordName &lt;&lt; " " &lt;&lt; result &lt;&lt; endl;
pvRecord.reset();
cout &lt;&lt; "exampleServer\n";
string str;
while(true) {
cout &lt;&lt; "Type exit to stop: \n";
@ -281,16 +322,15 @@ int main(int argc,char *argv[])
return 0;
}
</pre>
<p>The main program creates an example record and adds it to the database.
It then runs until the process is stopped by typing <b>exit</b>.
<p>Until the process is stopped,
pvAccess clients can put and get the value field.
For example</p>
<pre>
pvget exampleRecord
pvput exampleRecord 5
</pre>
<p>Will both work.</p>
This:
<ul>
<li>Gets a pointer to the master database.</li>
<li>Creates the local Channel Provider. This starts the pvAccess server.</li>
<li>Creates a <b>ExampleCounter</b> record with the name <b>exampleCounter</b>
</li>
<li>Prints <b>exampleCounter</b> on standard out.</li>
<li>Runs forever until the user types <b>exit</b> on standard in.</li>
</ul>
<h3>Phased Development</h3>
<p>This documentation describes the first phase of a phased implementation of pvDatabaseCPP:</pp>
<dl>
@ -299,16 +339,20 @@ pvput exampleRecord 5
<dt>pvDatabase</d>
<dd>Database of PVRecords. Has methods find, add, and remove.</dd>
<dt>Local Channel Provider</dt>
<dd>These two features will be the first phase.
But only synchronous record processing will be supported.</dd>
<dd>Complete implementation of ChannelProvider and Channel.
This means that pvCopy and monitor are also implemented.</dd>
</dl>
<p>Future phases of pvDatabaseCPP should include:</p>
<p>Future phases of pvDatabaseCPP might include:</p>
<dl>
<dt>Install</dt>
<dd>This provides on-line add and delete.</dd>
<dd>This provides complete support for on-line add and delete
of sets of records.
With the first phase each "service" is responsible for it's own implementation.
All that is provided is addRecord and removeRecord.
</dd>
<dt>Field support</dt>
<dd>Add ability to optionally add support to fields.
In addition some of the basic support defined in pvIOCJava will also be implemented.</dd>
<dd>Add the ability to optionally add support to fields.
In addition some of the basic support defined in pvIOCJava could also be implemented.</dd>
<dt>XML parser</dt>
<dd>This provides the ability to create record instances without writing any code.</dd>
</dl>
@ -326,10 +370,10 @@ The rest of this document discusses only the first phase.</p>
<dd>This provides the ability to monitor changes to fields of a record.</dd>
<dt>PVRecord and PVDatabase</dt>
<dd>Defined below.</dd>
<dt>local ChannelProvider</dt>
<dt>The localChannelProvider itself</dt>
<dd>This is the pvAccess package in pvIOCJava.
The localChannelProvider will access data from PVRecords.
It will implement all channel methods except channelRPC.</dd>
It will implement all channel methods.</dd>
</dl>
<h3>Minumum Features Required for pvRecord</h3>
<p>The first phase will only implement record processing, i. e.
@ -340,9 +384,9 @@ The following are the minimium features required</p>
<dt>PVDatabase</dt>
<dd>This holds a set of PVRecords. It has methods to find, add, and remove records.</dd>
<dt>PVRecord</dt>
<dd>This, and a set of related interfaces, provide the following:
<dd>This, and a set of related interfaces, provides the following:
<dl>
<dt>PVStructure</dt>
<dt>Access to top level PVStructure</dt>
<dd>PVRecord is a wrapper on a top level pvStructure.</dd>
<dt>Record locking</dt>
<dd>A record can be locked and unlocked.
@ -353,12 +397,45 @@ The following are the minimium features required</p>
</dl>
</dd>
</dl>
<p>The following sections provide a first attempt to describe the classes required for the first
phase.</p>
<p>The last section gives a brief overview of the features provided by pvIOCJava.</p>
<p>The following sections describes the classes required for the first phase.</p>
<h2>database</h2>
<p>The classes in <b>pvDatabase.h</b> implement a database of memory resident
<p>This directory has the following files:</p>
<dl>
<dt>pvDatabase.h</dt>
<dd>
This is what is described in this section.
</dd>
<dt>pvDatabase.cpp</dt>
<dd>
The implementation of PVDatabase.
</dd>
<dt>pvRecord.cpp</dt>
<dd>
The implementation of the base class for <b>PVREcord</b>.
It can also implement record instances with a <b>process</b>
method does nothing.
This can be used to create a "dumb" record where all changes are
done by clients.
The complete implementation is provided in the header file.
Thus code will be generated only if other code includes the
header file and creates a record instance.
</dd>
<dt>exampleCounter.h</dt>
<dd>
This was described in the introduction.
</dd>
<dt>powerSupplyRecordTest.h</dt>
<dd>
This provides code that simulates a power supply.
It computes the current from the voltage and power.
It is used for testing.
The complete implementation is provided in the header file.
Thus code will be generated only if other code includes the
header file and creates a record instance.
</dd>
</dl>
<p>The classes in <b>pvDatabase.h</b> describe a database of memory resident
smart records.
It describes the following classes:</p>
<dl>
@ -371,11 +448,7 @@ It describes the following classes:</p>
<dt>PVRecordClient</dt>
<dd>This is called by anything that acceses PVRecord.</dd>
<dt>PVListener</dt>
<dd>This is implemented by anything that wants to trap calls to the PVRecord::message.</dd>
<dt>RecordProcessRequester</dt>
<dd>This is implemented by anything that calls PVRecord::queueProcessRequest.</dd>
<dt>RecordPutRequester</dt>
<dd>This is implemented by anything that calls PVRecord::queuePutRequest.</dd>
<dd>This is implemented by anything that wants to trap calls to PVRecord::message.</dd>
<dt>PVDatabase</dt>
<dd>This is a database of PVRecords.</dd>
</dl>
@ -419,6 +492,10 @@ typedef std::tr1::shared_ptr&lt;PVDatabase&gt; PVDatabasePtr;
i. e. via pvAccess.
Thus this section is mainly of interest to
the local channel provider and record implementers.</li>
<li>Most readers will not care about most of the PVRecord methods.
Most of the methods are used by the pvAccess code.
Service implementers will mostly be interested in methods <b>init</b> and <b>process</b>.
These are described first.
</ul>
<hr>PVRecord Methods</h4>
<pre>
@ -428,16 +505,17 @@ class PVRecord
{
public:
POINTER_DEFINITIONS(PVRecord);
virtual bool init() {initPVRecord(); return true;}
virtual void process() {}
static PVRecordPtr create(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
PVRecord(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
virtual ~PVRecord();
virtual void process(
RecordProcessRequesterPtr const &amp;recordProcessRequester,
bool alreadyLocked) = 0;
virtual bool isSynchronous() = 0;
virtual bool requestImmediatePut(epics::pvData::PVFieldPtr const &amp;pvField);
virtual void immediatePutDone();
virtual void destroy();
epics::pvData::String getRecordName();
PVRecordStructurePtr getPVRecordStructure();
@ -445,25 +523,20 @@ public:
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 unlock();
bool tryLock();
void lockOtherRecord(PVRecordPtr const &amp; otherRecord);
void addPVRecordClient(PVRecordClientPtr const &amp; pvRecordClient);
void removePVRecordClient(PVRecordClientPtr const &amp; pvRecordClient);
bool addPVRecordClient(PVRecordClientPtr const &amp; pvRecordClient);
bool removePVRecordClient(PVRecordClientPtr const &amp; pvRecordClient);
void detachClients();
bool addListener(PVListenerPtr const &amp; pvListener);
bool removeListener(PVListenerPtr const &amp; pvListener);
void beginGroupPut();
void endGroupPut();
void queueProcessRequest(
RecordProcessRequesterPtr const &amp;recordProcessRequester);
void dequeueProcessRequest(
RecordProcessRequesterPtr const &amp;recordProcessRequester);
void queuePutRequest(
RecordPutRequesterPtr const &amp;recordPutRequester);
void putDone(
RecordPutRequesterPtr const &amp;recordPutRequester);
virtual epics::pvData::String getRequesterName();
void message(
epics::pvData::String getRequesterName() {return getRecordName();}
virtual void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
void message(
@ -472,63 +545,39 @@ public:
epics::pvData::MessageType messageType);
void toString(epics::pvData::StringBuilder buf);
void toString(epics::pvData::StringBuilder buf,int indentLevel);
//init MUST be called after derived class is constructed
void init();
};
protected:
void initPVRecord();
epics::pvData::PVStructurePtr getPVStructure();
PVRecordPtr getPtrSelf()
{
return shared_from_this();
}
private:
...
}
</pre>
<p>The methods are:</h3>
<dl>
<dt>init</dt>
<dd>Virtual method.
<p>Derived classes must implement this method.
This method <b>Must</b> call initPVRecord.</p>
</dd>
<dt>process</dt>
<dd>Virtual method.
<p>Derived classes must implement this method.
The base implementation does nothing.</p>
</dd>
<dt>create</dt>
<dd>Static method to create dumb records, i.e. records with a process method
that does nothing.</dd>
<dt>PVRecord</dt>
<dd>The constructor. It requires a recordName and a top level PVStructure.</dd>
<dt>~PVRecord</dt>
<dd>The destructor which must be virtual. A derived class must also have
a virtual destructor.</dd>
<dt>process</dt>
<dd>Pure virtual method.
<p>Derived classes must implement this method.</p>
<p>A client <b>must</b> only call this method when
<b>RecordProcessRequester::becomeProcessor</b> is called as a result
of a <b>queueProcessRequest</b>.
A client can either call lock before calling processs
or let process lock the record.
If a client wants to put data into the record it should lock, put, and then call
process.</p>
<p>If the record is synchronous, process will return only when all processing
is complete. If the record is asynchronous then process arranges for some
other thread to do the processing and returns.</p>
<p>When processing is done the record calls two client callbacks:</p>
<dl>
<dt>RecordProcessRequester::recordProcessResult</dt>
<dd>This is called with the record still locked.
The clients can get data from the record.</dd>
<dt>RecordProcessRequester::recordProcessComplete</dt>
<dd>This is called with the record unlocked.
The client can no longer access the record.</dd>
</dl>
</dd>
<dt>isSynchronous</dt>
<dd>Pure virtual method. Derived classes must implement this method.</dd>
<dt>requestImmediatePut</dt>
<dd>This is a virtual method.
<p> The purpose is to allow the implementation to provide fields
that allow a client to abort process.
For example a motor record might provide a field <b>stop</b></p>
<p>The default always returns <b>false</b>.</p>
<p>A record implementation can override the default and return <b>true</b>.
In it does requestImmediatePut it returns with the record locked.</p>
<p>The client can change the value of the associated field and then call
<b>immediatePutDone</b></p>
</dd>
<dt>immediatePutDone</dt>
<dd>This is a virtual method.
<p>The default does nothing.</p>
<p>Must be called by client as a result of a call to <b>requestImmediatePut</b>
that returns <b>true</b>.</p>
</dd>
<dt>destroy</dt>
<dd>This is a virtual method.
<p>The default does nothing.</p>
</dd>
<dt>getRecordName</dt>
<dd>Return the recordName.</dd>
@ -540,6 +589,9 @@ public:
<dd>Add a requester to receive messages.</dd>
<dt>removeRequester</dt>
<dd>Remove a message requester.</dd>
<dt>lock_guard</dt>
<dd>This is an inline method that locks the record. The record will automatically
be unlocked when control leaves the block that has the call.
<dt>lock</dt>
<dt>unlock</dt>
<dd>Lock and Unlock the record.
@ -572,31 +624,18 @@ public:
<dt>endGroupPut</dt>
<dd>End a group of puts.
This results in all registered PVListeners being called.</dd>
<dt>queueProcessRequest</dt>
<dd>Queue a process request.</dd>
<dt>dequeueProcessRequest</dt>
<dd>This <b>must</b> be called by record implementation after it has
completed a process request.
</dd>
<dt>queuePutRequest</dt>
<dd>Queue a put request.
<p>This is for code that wants to change data in a record without processing.
If <b>RecordPutRequester::requestResult</b> is called with result <b>true</b>
then the record is locked and the client can make changes.
When done the client <b>must</b> call <b>putDone</b></p>
</dd>
<dt>putDone</dt>
<dd>Called by <b>RecordPutRequester</b> after changing values in record.
This method unlocks the record</dd>
<dt>getRequesterName</dt>
<dd>virtual method of <b>Requester</b>
</dd>
<dt>message</dt>
<dd>Can be called by implementation code.
The message will be sent to every requester.</dd>
<dt>init</dt>
<dd>This method <b>must</b> be called by derived class
<b>after</b> class is completely constructed.</dd>
<dt>toString</dt>
<dd>Just calls the top level PVStructure toString method.</dd>
<dt>initRecord</dt>
<dd>This method <b>must</b> be called by derived class.</dd>
<dt>getPVStructure</dt>
<dd>Called by derived class.</dd>
</dl>
<h3>class PVRecordField</h3>
<pre>
@ -621,6 +660,14 @@ public:
virtual void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
protected:
PVRecordFieldPtr getPtrSelf()
{
return shared_from_this();
}
virtual void init();
private:
...
};
</pre>
<p>When PVRecord is created it creates a PVRecordField for every field in the PVStructure
@ -669,6 +716,10 @@ public:
epics::pvData::PVStructurePtr getPVStructure();
virtual void removeListener(PVListenerPtr const &amp; pvListener);
virtual void postPut();
protected:
virtual void init();
private:
...
};
</pre>
<p>When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure
@ -737,63 +788,6 @@ public:
<dt>endGroupPut</dt>
<dd>A related set of changes is done.</dd>
</dl>
<h3>class RecordProcessRequester</h3>
<pre>
class RecordProcessRequester :
virtual public PVRecordClient,
virtual public epics::pvData::Requester
{
public:
POINTER_DEFINITIONS(RecordProcessRequester);
virtual ~RecordProcessRequester();
virtual void recordDestroyed() = 0;
virtual void becomeProcessor() = 0;
virtual void recordProcessResult(epics::pvData::Status status) = 0;
virtual void recordProcessComplete() = 0;
};
</pre>
<p>where</p>
<dl>
<dt>~RecordProcessRequester</dt>
<dd>The destructor.</dd>
<dt>recordDestroyed</dt>
<dd>Record is being destroyed.</dd>
<dt>becomeProcessor</dt>
<dd>Called as a result of queueRequeProcessst. The requester can the call process.</dd>
<dt>recordProcessResult</dt>
<dd>The results of record processing.
This is called with the record locked so that the process requester
can access data from the record.</dd>
<dt>recordProcessComplete</dt>
<dd>Processing is complete.
This is called with the record unlocked.
If the process requester called process with leaveActive true then the requester
must call setInactive.</dd>
</dl>
<h3>class RecordPutRequester</h3>
<pre>
class RecordPutRequester :
virtual public PVRecordClient
{
public:
POINTER_DEFINITIONS(RecordPutRequester);
virtual ~RecordPutRequester();
virtual void requestResult(bool result) = 0;
};
</pre>
<p>where</p>
<dl>
<dt>~RecordPutRequester</dt>
<dd>The destructor.</dd>
<dt>requestResult</dt>
<dd>Result of a call to queuePutRequest. If <b>requestResult</b> is <b>false</b>
then the caller can not access the record.
If <b>requestResult</b> is <b>true</b>
then the record is locked and the caller can get and put data in the record.
When done the caller must call <b>PVRecord::putDone</b>, which will unlock the
record.
</dd>
</dl>
<h3>class PVDatabase</h3>
<pre>
class PVDatabase : virtual public epics::pvData::Requester {
@ -804,6 +798,10 @@ public:
PVRecordPtr findRecord(epics::pvData::String const&amp; recordName);
bool addRecord(PVRecordPtr const &amp; record);
bool removeRecord(PVRecordPtr const &amp; record);
virtual epics::pvData::String getRequesterName();
virtual void message(
epics::pvData::String const &amp;message,
epics::pvData::MessageType messageType);
private:
PVDatabase();
};
@ -822,70 +820,21 @@ private:
<dt>removeRecord</dt>
<dd>Remove a record from the database.
If the record was not in the database false is returned.</dd>
<dt>getRequesterName</dt>
<dd>Virtual method of Requester</dd>
<dt>message</dt>
<dd>Virtual message of Requester.</dd>
</dl>
<h2>Local Channel Provider</h2>
<p>Not yet described.</p>
<h2>pvAccess</h2>
<p>Not yet described.
It is only of interest to someone who wants to understand how it works.
</p>
<p>A brief description is that it must implement the following components of pvIOCJava:</p>
<dl>
<dt>pvCopy</dt>
<dt>monitor</dt>
<dt>pvAccess</dt>
<dd>See the next section for a description</dd>
<dt>local ChannelProvider and Channel</dt>
</dl>
<h2>Summary of Packages in pvIOCJAVA</h2>
<p>The following are the direct sub packages of <b>pvIOCJava/src/org/epics/pvioc</b>:</p>
<dl>
<dt>pvCopy</dt>
<dd>This provides a copy of an arbitrary subset of the fields in a PVRecord.
It also provides the ability to detect and report changes to fields.
It is required for pvAccess.</dd>
<dt>monitor</dt>
<dd>This provides the ability to monitor changes to a PVRecord. It is required for pvAccess monitors.</dd>
<dt>pvAccess</dt>
<dd>The local implementation of Channel Provider and Channel.
It is accessed by the remote pvAccess server and can also be accessed by code in the same IOC.</dd>
<dt>database</dt>
<dd>This defines and implements PVRecord, PVDatabase , and PVListener.
It supports the basic feature required the implement a local Channel Provider.</dd>
<dt>support</dt>
<dd>This provides the ability to optionally attach code to any field of a pvRecord.
It and several sub packages provide a set of standard support modules.</dd>
<dt>install</dt>
<dd>This provides the ability to dynamically initialize and add new PVRecords. It also provides
the ability to dynamicall delete PVRecords.</d>
<dt>xml</dt>
<dd>This provides the ability to configure record instances without writing code.</dd>
<dt>util</dt>
<dd>This is misnamed since it is code related to scanning.</dd>
<dt>pdrv</dt>
<dd>This is portDriver, which is a proposed sucessor to the asynManager component of asynDriver.</dd>
<dt>swtshell</dt>
<dd>This is shell that is can either run under the same process as a JavaIOC or as a remote shell.
It is like a version of probe but for pvData/pvAccess.
Almost all of it's features work in either local or remote mode.
With a little more work all or it's features could work remotely.
This should be done and then only remote mode should be supported.
It can then be rewritten in a completely different language and using a complely different GUI
framework.
</dd>
<dt>caV3</dt>
<dd>This has two components:
<dl>
<dt>ClientFactory</dt>
<dd>This is a small wrapper on top of the caV3 client support implemented by pvAccess.
It allows code in the pvIOC to access V3Records via pvAccess.</dd>
<dt>ServerFactory</dt>
<dd>This is a caV3 server that allows a caV3 client to access a PVRecord.
The Java implementation uses CAJ, which does most of the work.
For now it will not be discussed in this document.</dd>
</dl>
</dd>
<dt>v3a</dt>
<dd>I do not know what this is.</dd>
</dl>
<p>In addition there is one class file <b>JavaIOC.java</b>.
This is starting a IOC instance.
This is not required for pvIOCCPP which is either a main or runs as part of a V3 IOC.</p>
</div>
</body>

View File

@ -10,10 +10,15 @@ pvDatabase_LIBS += Com pvData pvAccess
SRC_DIRS += $(DATABASE)/database
INC += pvDatabase.h
INC += powerSupplyRecordTest.h
INC += exampleCounter.h
LIBSRCS += pvRecord.cpp
LIBSRCS += pvDatabase.cpp
SRC_DIRS += $(DATABASE)/pvAccess
INC += channelProviderLocal.h
INC += pvCopy.h
LIBSRCS += channelProviderLocal.cpp
LIBSRCS += channelLocal.cpp
LIBSRCS += pvCopy.cpp
include $(TOP)/configure/RULES

View File

@ -0,0 +1,74 @@
/* exampleCounter.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.04.02
*/
#ifndef EXAMPLECOUNTER_H
#define EXAMPLECOUNTER_H
#include <pv/pvDatabase.h>
#include <pv/standardPVField.h>
namespace epics { namespace pvDatabase {
class ExampleCounter;
typedef std::tr1::shared_ptr<ExampleCounter> ExampleCounterPtr;
class ExampleCounter :
public PVRecord
{
public:
POINTER_DEFINITIONS(ExampleCounter);
static ExampleCounterPtr create(
epics::pvData::String const & recordName);
virtual ~ExampleCounter(){}
virtual bool init();
virtual void process();
private:
ExampleCounter(epics::pvData::String const & recordName,
epics::pvData::PVStructurePtr const & pvStructure);
epics::pvData::PVLongPtr pvValue;
};
ExampleCounterPtr ExampleCounter::create(
epics::pvData::String const & recordName)
{
epics::pvData::PVStructurePtr pvStructure =
epics::pvData::getStandardPVField()->scalar(epics::pvData::pvDouble,"");
ExampleCounterPtr pvRecord(
new ExampleCounter(recordName,pvStructure));
if(!pvRecord->init()) pvRecord.reset();
return pvRecord;
}
ExampleCounter::ExampleCounter(
epics::pvData::String const & recordName,
epics::pvData::PVStructurePtr const & pvStructure)
: PVRecord(recordName,pvStructure)
{
}
bool ExampleCounter::init()
{
initPVRecord();
epics::pvData::PVFieldPtr pvField;
pvValue = getPVStructure()->getLongField("value");
if(pvValue.get()==NULL) return false;
return true;
}
void ExampleCounter::process()
{
pvValue->put(pvValue->get() + 1.0);
}
}}
#endif /* EXAMPLECOUNTER_H */

View File

@ -11,10 +11,7 @@
#ifndef POWERSUPPLYRECORDTEST_H
#define POWERSUPPLYRECORDTEST_H
#include <pv/executor.h>
#include <pv/pvDatabase.h>
#include <pv/event.h>
#include <pv/timer.h>
#include <pv/timeStamp.h>
#include <pv/alarm.h>
#include <pv/pvTimeStamp.h>
@ -44,7 +41,6 @@ public:
private:
PowerSupplyRecordTest(epics::pvData::String const & recordName,
epics::pvData::PVStructurePtr const & pvStructure);
epics::pvData::PVStructurePtr pvStructure;
epics::pvData::PVDoublePtr pvCurrent;
epics::pvData::PVDoublePtr pvPower;
epics::pvData::PVDoublePtr pvVoltage;
@ -67,8 +63,7 @@ PowerSupplyRecordTestPtr PowerSupplyRecordTest::create(
PowerSupplyRecordTest::PowerSupplyRecordTest(
epics::pvData::String const & recordName,
epics::pvData::PVStructurePtr const & pvStructure)
: PVRecord(recordName,pvStructure),
pvStructure(pvStructure)
: PVRecord(recordName,pvStructure)
{
}
@ -80,6 +75,7 @@ PowerSupplyRecordTest::~PowerSupplyRecordTest()
bool PowerSupplyRecordTest::init()
{
initPVRecord();
epics::pvData::PVStructurePtr pvStructure = getPVStructure();
epics::pvData::PVFieldPtr pvField;
bool result;
pvField = pvStructure->getSubField("timeStamp");

View File

@ -14,6 +14,7 @@
using std::tr1::static_pointer_cast;
using namespace epics::pvData;
using namespace epics::pvAccess;
using namespace std;
namespace epics { namespace pvDatabase {
@ -21,30 +22,51 @@ PVDatabase::~PVDatabase() {}
PVDatabasePtr PVDatabase::getMaster()
{
PVDatabasePtr xxx;
return xxx;
static PVDatabasePtr master;
static Mutex mutex;
Lock xx(mutex);
if(master.get()==NULL) {
master = PVDatabasePtr(new PVDatabase());
}
return master;
}
PVDatabase::PVDatabase() {}
PVRecordPtr PVDatabase::findRecord(String const& recordName)
{
PVRecordMap::iterator iter = recordMap.find(recordName);
if(iter!=recordMap.end()) {
return (*iter).second;
}
PVRecordPtr xxx;
return xxx;
}
bool PVDatabase::addRecord(PVRecordPtr const & record)
{
return false;
String recordName = record->getRecordName();
PVRecordMap::iterator iter = recordMap.find(recordName);
if(iter!=recordMap.end()) return false;
recordMap.insert(PVRecordMap::value_type(recordName,record));
return true;
}
bool PVDatabase::removeRecord(PVRecordPtr const & record)
{
String recordName = record->getRecordName();
PVRecordMap::iterator iter = recordMap.find(recordName);
if(iter!=recordMap.end()) {
recordMap.erase(iter);
return true;
}
return false;
}
String PVDatabase::getRequesterName()
{
String xxx;
return xxx;
static String name("masterDatabase");
return name;
}
void PVDatabase::message(String const & message,MessageType messageType)

View File

@ -12,6 +12,7 @@
#define PVDATABASE_H
#include <list>
#include <map>
#include <deque>
#include <pv/pvAccess.h>
@ -21,6 +22,7 @@ namespace epics { namespace pvDatabase {
class PVRecord;
typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
class PVRecordField;
typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
@ -39,53 +41,201 @@ typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
class PVDatabase;
typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
/**
* Base interface for a record.
* @author mrk
*/
class PVRecord :
public epics::pvData::Requester,
public std::tr1::enable_shared_from_this<PVRecord>
{
public:
POINTER_DEFINITIONS(PVRecord);
/**
* Virtual initialization method.
* Must be implemented by derived classes.
* This method <b>Must</b> call initPVRecord.
* @return <b>true</b> for success and <b>false</b> for failure.
*/
virtual bool init() {initPVRecord(); return true;}
/**
* Must be implemented by derived classes.
* It is the method that makes a record smart.
* If it encounters errors it should raise alarms and/or
* call the <b>message</b> method provided by the base class.
*/
virtual void process() {}
/**
* Creates a <b>dump</b> record, i.e. a record where process does nothing.
* @param recordName The name of the record, which is also the channelName.
* @param pvStructure The top level structure.
* @return A shared pointer to the newly created record.
*/
static PVRecordPtr create(
epics::pvData::String const & recordName,
epics::pvData::PVStructurePtr const & pvStructure);
/**
* The constructor.
* @param recordName The name of the record, which is also the channelName.
* @param pvStructure The top level structure.
*/
PVRecord(
epics::pvData::String const & recordName,
epics::pvData::PVStructurePtr const & pvStructure);
/**
* The Destructor. Must be virtual.
*/
virtual ~PVRecord();
/**
* Destroy the PVRecord. Release any resources used and
* get rid of listeners and requesters.
*/
virtual void destroy();
/**
* Get the name of the record.
* @return The name.
*/
epics::pvData::String getRecordName();
/**
* Get the top level PVStructure.
* @return The shared pointer.
*/
PVRecordStructurePtr getPVRecordStructure();
/**
* Find the PVRecordField for the PVField.
* @param pvField The PVField.
* @return The shared pointer to the PVRecordField.
*/
PVRecordFieldPtr findPVRecordField(
epics::pvData::PVFieldPtr const & pvField);
/**
* Add a requester to receive messages.
* @param requester The requester.
* @return <b>true</b> if requester was added.
*/
bool addRequester(epics::pvData::RequesterPtr const & requester);
/**
* Remove a requester.
* @param requester The requester.`
* @return <b>true</b> if requester was removed.
*/
bool removeRequester(epics::pvData::RequesterPtr const & requester);
/**
* This is an inline method that locks the record.
* The record will automatically
* be unlocked when control leaves the block that has the call.
*/
inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
/**
* Lock the record.
* Any code must lock while accessing a record.
*/
void lock();
/**
* Unlock the record.
*/
void unlock();
/**
* If <b>true</b> then just like <b>lock</b>.
* If <b>false</b>client can not access record.
* Code can try to simultaneously hold the lock for more than two records
* by calling this method but must be willing to accept failure.
* @return <b>true</b> if the record is locked.
*/
bool tryLock();
/**
* A client that holds the lock for one record can lock one other record.
* A client <b>must</b> not call this if the client already has the lock for
* more then one record.
*
* @param otherRecord The other record to lock.
*/
void lockOtherRecord(PVRecordPtr const & otherRecord);
/**
* Every client that accesses the record must call this so that the
* client can be notified when the record is deleted.
* @param pvRecordClient The client.
* @return <b>true</b> if the client is added.
*/
bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
/**
* Remove a client.
* @param pvRecordClient The client.
* @return <b>true</b> if the client is removed.
*/
bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
/**
* remove all attached clients.
*/
void detachClients();
/**
* Add a PVListener.
* This must be called before calling pvRecordField.addListener.
* @param pvListener The listener.
* @return <b>true</b> if the listener was added.
*/
bool addListener(PVListenerPtr const & pvListener);
/**
* Remove a listener.
* @param pvListener The listener.
* @return <b>true</b> if the listener was removed.
*/
bool removeListener(PVListenerPtr const & pvListener);
/**
* Begins a group of puts.
*/
void beginGroupPut();
/**
* Ends a group of puts.
*/
void endGroupPut();
/**
* Virtual method of <b>Requester</b>
* @return the name of the requester.
*/
epics::pvData::String getRequesterName() {return getRecordName();}
/**
* Can be called by implementation code.
* The message will be sent to every requester.
* @param message The message.
* @param messageType The severity of the message.
*/
virtual void message(
epics::pvData::String const & message,
epics::pvData::MessageType messageType);
/**
* Called for a field. It will call the previous method
* after adding field name.
* @param pvRecordField The field for which the message is associated.
* @param message The message.
* @param messageType The severity of the message.
*/
void message(
PVRecordFieldPtr const & pvRecordField,
epics::pvData::String const & message,
epics::pvData::MessageType messageType);
/**
* Calls the next method with indentLevel = 0.
* @param buf String Builder.
*/
void toString(epics::pvData::StringBuilder buf);
/**
* Dumps the data from the top level PVStructure.
* @param buf String Builder.
* @param indentLevel The indentation level.
*/
void toString(epics::pvData::StringBuilder buf,int indentLevel);
//init MUST be called by derived class after derived class is constructed
virtual bool init() {initPVRecord(); return true;}
virtual void process() {}
protected:
/**
* Initializes the base class. Must be called by derived classes.
*/
void initPVRecord();
/**
* Convience method for derived classes.
* @return The shared pointer to the top level PVStructure.
*/
epics::pvData::PVStructurePtr getPVStructure();
PVRecordPtr getPtrSelf()
{
return shared_from_this();
@ -107,25 +257,82 @@ private:
bool isDestroyed;
};
/**
* Interface for a field of a record.
* One exists for each field of the top level PVStructure.
* @author mrk
*/
class PVRecordField :
public virtual epics::pvData::PostHandler,
public std::tr1::enable_shared_from_this<PVRecordField>
{
public:
POINTER_DEFINITIONS(PVRecordField);
/**
* Constructor.
* @param pvField The field from the top level structure.
* @param The parent.
*/
PVRecordField(
epics::pvData::PVFieldPtr const & pvField,
PVRecordStructurePtr const &parent,
PVRecordPtr const & pvRecord);
/**
* Destructor.
*/
virtual ~PVRecordField();
/**
* Get the parent.
* @return The parent.
*/
PVRecordStructurePtr getParent();
/**
* Get the PVField.
* @return The shared pointer.
*/
epics::pvData::PVFieldPtr getPVField();
/**
* Get the full name of the field, i.e. field,field,..
* @return The full name.
*/
epics::pvData::String getFullFieldName();
/**
* Get the recordName plus the full name of the field, i.e. recordName.field,field,..
* @return The name.
*/
epics::pvData::String getFullName();
/**
* Returns the PVRecord to which this field belongs.
* @return The shared pointer,
*/
PVRecordPtr getPVRecord();
/**
* Add A PVListener to this field.
* Whenever this field or any subfield if this field is modified the listener will be notified.
* PVListener is described below.
* Before a listener can call addListener it must first call PVRecord.registerListener.
* @param pvListener The listener.
* @return <b<true</b> if listener is added.
*/
bool addListener(PVListenerPtr const & pvListener);
/**
* Remove a listener.
* @param pvListener The listener.
* @return <b<true</b> if listener is removed.
*/
virtual void removeListener(PVListenerPtr const & pvListener);
/**
* This is called by the code that implements the data interface.
* It is called whenever the put method is called.
*/
virtual void postPut();
/**
* Called by implementation code.
* It calls PVRecord::message after prepending the full fieldname.
* @param message The message,
* @param messageType The message severity.
* @return
*/
virtual void message(
epics::pvData::String const & message,
epics::pvData::MessageType messageType);
@ -148,19 +355,51 @@ private:
friend class PVRecord;
};
/**
* Interface for a field that is a structure.
* One exists for each structure field of the top level PVStructure.
* @author mrk
*/
class PVRecordStructure : public PVRecordField {
public:
POINTER_DEFINITIONS(PVRecordStructure);
/**
* Constructor.
* @param pvStructure The data.
* @param parent The parent
* @param pvRecord The record that has this field.
*/
PVRecordStructure(
epics::pvData::PVStructurePtr const &pvStructure,
PVRecordStructurePtr const &parent,
PVRecordPtr const & pvRecord);
/**
* Destructor.
*/
virtual ~PVRecordStructure();
/**
* Get the sub fields.
* @return the array of PVRecordFieldPtr.
*/
PVRecordFieldPtrArrayPtr getPVRecordFields();
/**
* Get the data structure/
* @return The shared pointer.
*/
epics::pvData::PVStructurePtr getPVStructure();
/**
* Called by PVRecord::removeListener.
* @param pvListener The listener.
*/
virtual void removeListener(PVListenerPtr const & pvListener);
/**
* Called by implementation code of PVRecord.
*/
virtual void postPut();
protected:
/**
* Called by implementation code of PVRecord.
*/
virtual void init();
private:
epics::pvData::PVStructurePtr pvStructure;
@ -168,41 +407,116 @@ private:
friend class PVRecord;
};
/**
* An interface that must be implemented by any code that accesses the record.
* @author mrk
*/
class PVRecordClient {
public:
POINTER_DEFINITIONS(PVRecordClient);
/**
* Destructor.
*/
virtual ~PVRecordClient() {}
/**
* Detach from the record because it is being removed.
* @param pvRecord The record.
*/
virtual void detach(PVRecordPtr const & pvRecord) = 0;
};
/**
* An interface that is implemented by code that traps calls to PVRecord::message.
* @author mrk
*/
class PVListener :
virtual public PVRecordClient
{
public:
POINTER_DEFINITIONS(PVListener);
/**
* Destructor.
*/
virtual ~PVListener() {}
/**
* pvField has been modified.
* This is called if the listener has called PVRecordField::addListener for pvRecordField.
* @param pvRecordField The modified field.
*/
virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
/**
* A subfield has been modified.
* @param requested The structure that was requested.
* @param pvRecordField The field that was modified.
*/
virtual void dataPut(
PVRecordStructurePtr const & requested,
PVRecordFieldPtr const & pvRecordField) = 0;
/**
* Begin a set of puts.
* @param pvRecord The record.
*/
virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
/**
* End a set of puts.
* @param pvRecord The record.
*/
virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
};
/**
* The interface to a database of PVRecords.
* @author mrk
*/
class PVDatabase : virtual public epics::pvData::Requester {
public:
POINTER_DEFINITIONS(PVDatabase);
/**
* Get the master database.
* @return The shared pointer.
*/
static PVDatabasePtr getMaster();
/**
* Destructor
*/
virtual ~PVDatabase();
/**
* Find a record.
* An empty pointer is returned if the record is not in the database.
* @param recordName The record to find.
* @return The shared pointer.
*/
PVRecordPtr findRecord(epics::pvData::String const& recordName);
/**
* Add a record.
* @param The record to add.
* @return <b>true</b> if record was added.
*/
bool addRecord(PVRecordPtr const & record);
/**
* Remove a record.
* @param The record to remove.
* @return <b>true</b> if record was removed.
*/
bool removeRecord(PVRecordPtr const & record);
/**
* Virtual method of Requester.
* @return The name.
*/
virtual epics::pvData::String getRequesterName();
/**
* Virtual method of Requester.
* @param message The message.
* @param messageType The message severity.
* @return
*/
virtual void message(
epics::pvData::String const & message,
epics::pvData::MessageType messageType);
private:
PVDatabase();
PVRecordMap recordMap;
};
}}

View File

@ -92,6 +92,8 @@ String PVRecord::getRecordName() {return recordName;}
PVRecordStructurePtr PVRecord::getPVRecordStructure() {return pvRecordStructure;}
PVStructurePtr PVRecord::getPVStructure() {return pvStructure;}
PVRecordFieldPtr PVRecord::findPVRecordField(PVFieldPtr const & pvField)
{
return findPVRecordField(pvRecordStructure,pvField);
@ -488,42 +490,4 @@ void PVRecordStructure::postPut()
}
}
PVDatabase::PVDatabase()
{
}
PVDatabase::~PVDatabase() {}
PVDatabasePtr PVDatabase::getMaster()
{
PVDatabasePtr xxx;
return xxx;
}
PVRecordPtr PVDatabase::findRecord(String const& recordName)
{
PVRecordPtr xxx;
return xxx;
}
bool PVDatabase::addRecord(PVRecordPtr const & record)
{
return false;
}
bool PVDatabase::removeRecord(PVRecordPtr const & record)
{
return false;
}
String PVDatabase::getRequesterName()
{
String xxx;
return xxx;
}
void PVDatabase::message(String const & message,MessageType messageType)
{
}
}}

View File

@ -0,0 +1,687 @@
/* channelLocal.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
*/
#include <pv/channelProviderLocal.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;
class ChannelProcessLocal;
typedef std::tr1::shared_ptr<ChannelProcessLocal> ChannelProcessLocalPtr;
class ChannelGetLocal;
typedef std::tr1::shared_ptr<ChannelGetLocal> ChannelGetLocalPtr;
class ChannelPutLocal;
typedef std::tr1::shared_ptr<ChannelPutLocal> ChannelPutLocalPtr;
class ChannelPutGetLocal;
typedef std::tr1::shared_ptr<ChannelPutGetLocal> ChannelPutGetLocalPtr;
class ChannelMonitorLocal;
typedef std::tr1::shared_ptr<ChannelMonitorLocal> ChannelMonitorLocalPtr;
class ChannelRPCLocal;
typedef std::tr1::shared_ptr<ChannelRPCLocal> ChannelRPCLocalPtr;
class ChannelArrayLocal;
typedef std::tr1::shared_ptr<ChannelArrayLocal> ChannelArrayLocalPtr;
class ChannelLocalDebug {
public:
static void setLevel(int level);
static int getLevel();
};
static bool getProcess(PVStructurePtr pvRequest,bool processDefault)
{
PVFieldPtr pvField = pvRequest->getSubField("record._options.process");
if(pvField.get()==NULL || pvField->getField()->getType()!=scalar) {
return processDefault;
}
ScalarConstPtr scalar = static_pointer_cast<const Scalar>(
pvField->getField());
if(scalar->getScalarType()==pvString) {
PVStringPtr pvString = static_pointer_cast<PVString>(pvField);
return pvString->get().compare("true")==0 ? true : false;
} else if(scalar->getScalarType()==pvBoolean) {
PVBooleanPtr pvBoolean = static_pointer_cast<PVBoolean>(pvField);
return pvBoolean.get();
}
return processDefault;
}
/*
class ChannelRequestLocal :
public ChannelRequest
{
public:
POINTER_DEFINITIONS(ChannelRequestLocal)
ChannelRequestLocal() :thelock(mutex) {}
virtual ~ChannelRequestLocal() {}
virtual void destroy(){}
virtual void lock() {thelock.lock();}
virtual void unlock() {thelock.unlock();}
private:
Mutex mutex;
Lock thelock;
};
*/
class ChannelProcessLocal :
public ChannelProcess
{
public:
POINTER_DEFINITIONS(ChannelProcessLocal);
virtual ~ChannelProcessLocal();
static ChannelProcess::shared_pointer create(
ChannelProviderLocalPtr const &channelProvider,
ChannelProcess::shared_pointer const & channelProcessRequester,
PVStructurePtr const & pvRequest,
PVRecordPtr const &pvRecord);
virtual void process(bool lastRequest);
};
class ChannelGetLocal :
public ChannelGet,
public std::tr1::enable_shared_from_this<ChannelGetLocal>
{
public:
POINTER_DEFINITIONS(ChannelGetLocal);
virtual ~ChannelGetLocal(){}
static ChannelGetLocalPtr create(
ChannelLocalPtr const &channelLocal,
ChannelGetRequester::shared_pointer const & channelGetRequester,
PVStructurePtr const & pvRequest,
PVRecordPtr const &pvRecord);
virtual void get(bool lastRequest);
virtual void destroy();
virtual void lock() {thelock.lock();}
virtual void unlock() {thelock.unlock();}
private:
shared_pointer getPtrSelf()
{
return shared_from_this();
}
ChannelGetLocal(
bool callProcess,
ChannelLocalPtr const &channelLocal,
ChannelGetRequester::shared_pointer const & channelGetRequester,
PVCopyPtr const &pvCopy,
PVStructurePtr const&pvStructure,
BitSetPtr const & bitSet,
PVRecordPtr const &pvRecord)
:
firstTime(true),
isDestroyed(false),
callProcess(callProcess),
channelLocal(channelLocal),
channelGetRequester(channelGetRequester),
pvCopy(pvCopy),
pvStructure(pvStructure),
bitSet(bitSet),
pvRecord(pvRecord),
thelock(mutex)
{
thelock.unlock();
}
bool firstTime;
bool isDestroyed;
bool callProcess;
ChannelLocalPtr channelLocal;
ChannelGetRequester::shared_pointer channelGetRequester,;
PVCopyPtr pvCopy;
PVStructurePtr pvStructure;
BitSetPtr bitSet;
PVRecordPtr pvRecord;
Mutex mutex;
Lock thelock;
};
ChannelGetLocalPtr ChannelGetLocal::create(
ChannelLocalPtr const &channelLocal,
ChannelGetRequester::shared_pointer const & channelGetRequester,
PVStructurePtr const & pvRequest,
PVRecordPtr const &pvRecord)
{
PVCopyPtr pvCopy = PVCopy::create(
pvRecord,
pvRequest,
"field");
if(pvCopy.get()==NULL) {
Status status(
Status::Status::STATUSTYPE_ERROR,
"invalid pvRequest");
ChannelGet::shared_pointer channelGet;
PVStructurePtr pvStructure;
BitSetPtr bitSet;
channelGetRequester->channelGetConnect(
status,
channelGet,
pvStructure,
bitSet);
ChannelGetLocalPtr localGet;
return localGet;
}
PVStructurePtr pvStructure = pvCopy->createPVStructure();
BitSetPtr bitSet(new BitSet(pvStructure->getNumberFields()));
ChannelGetLocalPtr get(new ChannelGetLocal(
getProcess(pvRequest,false),
channelLocal,
channelGetRequester,
pvCopy,
pvStructure,
bitSet,
pvRecord));
channelLocal->addChannelGet(get);
channelGetRequester->channelGetConnect(Status::Ok, get, pvStructure,bitSet);
return get;
}
void ChannelGetLocal::destroy()
{
if(isDestroyed) return;
isDestroyed = true;
channelLocal->removeChannelGet(getPtrSelf());
channelLocal.reset();
channelGetRequester.reset();
pvCopy.reset();
pvStructure.reset();
bitSet.reset();
pvRecord.reset();
}
void ChannelGetLocal::get(bool lastRequest)
{
if(callProcess) pvRecord->process();
if(isDestroyed) {
Status status(
Status::Status::STATUSTYPE_ERROR,
"was destroyed");
channelGetRequester->getDone(status);
}
pvCopy->updateCopySetBitSet(pvStructure, bitSet, false);
if(firstTime) {
bitSet->clear();
bitSet->set(0);
firstTime = false;
}
channelGetRequester->getDone(Status::Ok);
if(lastRequest) destroy();
}
class ChannelLocalPut :
public ChannelPut
{
public:
POINTER_DEFINITIONS(ChannelLocalPut);
virtual ~ChannelLocalPut();
static ChannelPut::shared_pointer create(
ChannelProviderLocalPtr const &channelProvider,
ChannelPut::shared_pointer const & channelPutRequester,
PVStructurePtr const & pvRequest,
PVRecordPtr const &pvRecord);
virtual void put(bool lastRequest);
virtual void get(bool lastRequest);
};
class ChannelPutGetLocal :
public ChannelPutGet
{
public:
POINTER_DEFINITIONS(ChannelPutGetLocal);
virtual ~ChannelPutGetLocal();
static ChannelPutGet::shared_pointer create(
ChannelProviderLocalPtr const &channelProvider,
ChannelPutGet::shared_pointer const & channelPutGetRequester,
PVStructurePtr const & pvRequest,
PVRecordPtr const &pvRecord);
virtual void putGet(bool lastRequest);
virtual void getPut();
virtual void getGet();
};
class ChannelMonitorLocal :
public Monitor
{
public:
POINTER_DEFINITIONS(ChannelMonitorLocal);
virtual ~ChannelMonitorLocal();
static Monitor::shared_pointer create(
ChannelProviderLocalPtr const &channelProvider,
MonitorRequester::shared_pointer const & channelMonitorRequester,
PVStructurePtr const & pvRequest,
PVRecordPtr const &pvRecord);
virtual Status start();
virtual Status stop();
virtual MonitorElementPtr poll();
virtual void release(MonitorElementPtr const & monitorElement);
};
class ChannelRPCLocal :
public ChannelRPC
{
public:
POINTER_DEFINITIONS(ChannelRPCLocal);
virtual ~ChannelRPCLocal();
static ChannelRPC::shared_pointer create(
ChannelProviderLocalPtr const &channelProvider,
ChannelRPC::shared_pointer const & channelRPCRequester,
PVStructurePtr const & pvRequest,
PVRecordPtr const &pvRecord);
virtual void request(
PVStructurePtr const & pvArgument,
bool lastRequest);
};
class ChannelArrayLocal :
public ChannelArray
{
public:
POINTER_DEFINITIONS(ChannelArrayLocal);
virtual ~ChannelArrayLocal();
static ChannelArray::shared_pointer create(
ChannelProviderLocalPtr const &channelProvider,
ChannelArray::shared_pointer const & channelArrayRequester,
PVStructurePtr const & pvRequest,
PVRecordPtr const &pvRecord);
virtual void putArray(bool lastRequest, int offset, int count);
virtual void getArray(bool lastRequest, int offset, int count);
virtual void setLength(bool lastRequest, int length, int capacity);
};
int ChannelLocalDebugLevel = 0;
void ChannelLocalDebug::setLevel(int level) {ChannelLocalDebugLevel = level;}
int ChannelLocalDebug::getLevel() {return ChannelLocalDebugLevel;}
ChannelLocal::ChannelLocal(
ChannelProviderLocalPtr const & provider,
ChannelRequester::shared_pointer const & requester,
PVRecordPtr const & pvRecord)
: provider(provider),
requester(requester),
pvRecord(pvRecord),
beingDestroyed(false)
{
if(ChannelLocalDebug::getLevel()>0) {
printf("ChannelLocal::ChannelLocal\n");
}
}
ChannelLocal::~ChannelLocal()
{
if(ChannelLocalDebug::getLevel()>0) {
printf("ChannelLocal::~ChannelLocal\n");
}
}
void ChannelLocal::destroy()
{
if(ChannelLocalDebug::getLevel()>0) {
printf("ChannelLocal::destroy beingDestroyed %s\n",
(beingDestroyed ? "true" : "false"));
}
{
Lock xx(mutex);
if(beingDestroyed) return;
beingDestroyed = true;
}
while(true) {
std::set<ChannelProcess::shared_pointer>::iterator it;
it = channelProcessList.begin();
if(it==channelProcessList.end()) break;
it->get()->destroy();
channelProcessList.erase(it);
}
while(true) {
std::set<ChannelGet::shared_pointer>::iterator it;
it = channelGetList.begin();
if(it==channelGetList.end()) break;
it->get()->destroy();
channelGetList.erase(it);
}
while(true) {
std::set<ChannelPut::shared_pointer>::iterator it;
it = channelPutList.begin();
if(it==channelPutList.end()) break;
it->get()->destroy();
channelPutList.erase(it);
}
while(true) {
std::set<ChannelPutGet::shared_pointer>::iterator it;
it = channelPutGetList.begin();
if(it==channelPutGetList.end()) break;
it->get()->destroy();
channelPutGetList.erase(it);
}
while(true) {
std::set<Monitor::shared_pointer>::iterator it;
it = channelMonitorList.begin();
if(it==channelMonitorList.end()) break;
it->get()->destroy();
channelMonitorList.erase(it);
}
while(true) {
std::set<ChannelRPC::shared_pointer>::iterator it;
it = channelRPCList.begin();
if(it==channelRPCList.end()) break;
it->get()->destroy();
channelRPCList.erase(it);
}
while(true) {
std::set<ChannelArray::shared_pointer>::iterator it;
it = channelArrayList.begin();
if(it==channelArrayList.end()) break;
it->get()->destroy();
channelArrayList.erase(it);
}
provider->removeChannel(getPtrSelf());
}
void ChannelLocal::addChannelProcess(ChannelProcess::shared_pointer const & channelProcess)
{
if(ChannelLocalDebug::getLevel()>0) {
printf("ChannelLocal::addChannelProcess\n");
}
Lock xx(mutex);
if(beingDestroyed) return;
channelProcessList.insert(channelProcess);
}
void ChannelLocal::addChannelGet(ChannelGet::shared_pointer const &channelGet)
{
if(ChannelLocalDebug::getLevel()>0) {
printf("ChannelLocal::addChannelGet\n");
}
Lock xx(mutex);
if(beingDestroyed) return;
channelGetList.insert(channelGet);
}
void ChannelLocal::addChannelPut(ChannelPut::shared_pointer const &channelPut)
{
if(ChannelLocalDebug::getLevel()>0) {
printf("ChannelLocal::addChannelPut\n");
}
Lock xx(mutex);
if(beingDestroyed) return;
channelPutList.insert(channelPut);
}
void ChannelLocal::addChannelPutGet(ChannelPutGet::shared_pointer const &channelPutGet)
{
if(ChannelLocalDebug::getLevel()>0) {
printf("ChannelLocal::addChannelPutGet\n");
}
Lock xx(mutex);
if(beingDestroyed) return;
channelPutGetList.insert(channelPutGet);
}
void ChannelLocal::addChannelMonitor(Monitor::shared_pointer const &monitor)
{
if(ChannelLocalDebug::getLevel()>0) {
printf("ChannelLocal::addChannelMonitor\n");
}
Lock xx(mutex);
if(beingDestroyed) return;
channelMonitorList.insert(monitor);
}
void ChannelLocal::addChannelRPC(ChannelRPC::shared_pointer const &channelRPC)
{
if(ChannelLocalDebug::getLevel()>0) {
printf("ChannelLocal::addChannelRPC\n");
}
Lock xx(mutex);
if(beingDestroyed) return;
channelRPCList.insert(channelRPC);
}
void ChannelLocal::addChannelArray(ChannelArray::shared_pointer const &channelArray)
{
if(ChannelLocalDebug::getLevel()>0) {
printf("ChannelLocal::addChannelArray\n");
}
Lock xx(mutex);
if(beingDestroyed) return;
channelArrayList.insert(channelArray);
}
void ChannelLocal::removeChannelProcess(ChannelProcess::shared_pointer const &ref)
{
if(ChannelLocalDebug::getLevel()>0) {
printf("ChannelLocal::removeChannelProcess\n");
}
Lock xx(mutex);
if(beingDestroyed) return;
channelProcessList.erase(ref);
}
void ChannelLocal::removeChannelGet(ChannelGet::shared_pointer const &ref)
{
if(ChannelLocalDebug::getLevel()>0) {
printf("ChannelLocal::removeChannelGet\n");
}
Lock xx(mutex);
if(beingDestroyed) return;
channelGetList.erase(ref);
}
void ChannelLocal::removeChannelPut(ChannelPut::shared_pointer const &ref)
{
if(ChannelLocalDebug::getLevel()>0) {
printf("ChannelLocal::removeChannelPut\n");
}
Lock xx(mutex);
if(beingDestroyed) return;
channelPutList.erase(ref);
}
void ChannelLocal::removeChannelPutGet(ChannelPutGet::shared_pointer const &ref)
{
if(ChannelLocalDebug::getLevel()>0) {
printf("ChannelLocal::removeChannelPutGet\n");
}
Lock xx(mutex);
if(beingDestroyed) return;
channelPutGetList.erase(ref);
}
void ChannelLocal::removeChannelMonitor(Monitor::shared_pointer const &ref)
{
if(ChannelLocalDebug::getLevel()>0) {
printf("ChannelLocal::removeChannelMonitor\n");
}
Lock xx(mutex);
if(beingDestroyed) return;
channelMonitorList.erase(ref);
}
void ChannelLocal::removeChannelRPC(ChannelRPC::shared_pointer const &ref)
{
if(ChannelLocalDebug::getLevel()>0) {
printf("ChannelLocal::removeChannelRPC\n");
}
Lock xx(mutex);
if(beingDestroyed) return;
channelRPCList.erase(ref);
}
void ChannelLocal::removeChannelArray(ChannelArray::shared_pointer const &ref)
{
if(ChannelLocalDebug::getLevel()>0) {
printf("ChannelLocal::removeChannelArray\n");
}
Lock xx(mutex);
if(beingDestroyed) return;
channelArrayList.erase(ref);
}
String ChannelLocal::getRequesterName()
{
return requester->getRequesterName();
}
void ChannelLocal::message(
String const &message,
MessageType messageType)
{
requester->message(message,messageType);
}
ChannelProvider::shared_pointer ChannelLocal::getProvider()
{
return provider;
}
String ChannelLocal::getRemoteAddress()
{
return String("local");
}
Channel::ConnectionState ChannelLocal::getConnectionState()
{
return Channel::CONNECTED;
}
String ChannelLocal::getChannelName()
{
return pvRecord->getRecordName();
}
ChannelRequester::shared_pointer ChannelLocal::getChannelRequester()
{
return requester;
}
bool ChannelLocal::isConnected()
{
return true;
}
void ChannelLocal::getField(GetFieldRequester::shared_pointer const &requester,
String const &subField)
{
Status status(Status::STATUSTYPE_ERROR,
String("client asked for illegal field"));
requester->getDone(status,FieldConstPtr());
}
AccessRights ChannelLocal::getAccessRights(
PVField::shared_pointer const &pvField)
{
throw std::logic_error(String("Not Implemented"));
}
ChannelProcess::shared_pointer ChannelLocal::createChannelProcess(
ChannelProcessRequester::shared_pointer const & channelProcessRequester,
PVStructure::shared_pointer const & pvRequest)
{
Status status(Status::STATUSTYPE_ERROR,
String("ChannelProcess not supported"));
channelProcessRequester->channelProcessConnect(
status,
ChannelProcess::shared_pointer());
return ChannelProcess::shared_pointer();
}
ChannelGet::shared_pointer ChannelLocal::createChannelGet(
ChannelGetRequester::shared_pointer const &channelGetRequester,
PVStructure::shared_pointer const &pvRequest)
{
ChannelGetLocalPtr channelGet =
ChannelGetLocal::create(
getPtrSelf(),
channelGetRequester,
pvRequest,
pvRecord);
return channelGet;
}
ChannelPut::shared_pointer ChannelLocal::createChannelPut(
ChannelPutRequester::shared_pointer const &channelPutRequester,
PVStructure::shared_pointer const &pvRequest)
{
Status status(Status::STATUSTYPE_ERROR,
String("ChannelPut not supported"));
channelPutRequester->channelPutConnect(
status,
ChannelPut::shared_pointer(),
PVStructure::shared_pointer(),
BitSet::shared_pointer());
return ChannelPut::shared_pointer();
}
ChannelPutGet::shared_pointer ChannelLocal::createChannelPutGet(
ChannelPutGetRequester::shared_pointer const &channelPutGetRequester,
PVStructure::shared_pointer const &pvRequest)
{
Status status(Status::STATUSTYPE_ERROR,
String("ChannelPutGet not supported"));
channelPutGetRequester->channelPutGetConnect(
status,
ChannelPutGet::shared_pointer(),
PVStructure::shared_pointer(),
PVStructure::shared_pointer());
return ChannelPutGet::shared_pointer();
}
ChannelRPC::shared_pointer ChannelLocal::createChannelRPC(
ChannelRPCRequester::shared_pointer const & channelRPCRequester,
PVStructure::shared_pointer const & pvRequest)
{
Status status(Status::STATUSTYPE_ERROR,
String("ChannelRPC not supported"));
channelRPCRequester->channelRPCConnect(status,ChannelRPC::shared_pointer());
return ChannelRPC::shared_pointer();
}
Monitor::shared_pointer ChannelLocal::createMonitor(
MonitorRequester::shared_pointer const &monitorRequester,
PVStructure::shared_pointer const &pvRequest)
{
Status status(Status::STATUSTYPE_ERROR,
String("ChannelMonitor not supported"));
Monitor::shared_pointer thisPointer = dynamic_pointer_cast<Monitor>(getPtrSelf());
monitorRequester->monitorConnect(
status,
thisPointer,
StructureConstPtr());
return Monitor::shared_pointer();
}
ChannelArray::shared_pointer ChannelLocal::createChannelArray(
ChannelArrayRequester::shared_pointer const &channelArrayRequester,
PVStructure::shared_pointer const &pvRequest)
{
Status status(Status::STATUSTYPE_ERROR,
String("ChannelArray not supported"));
channelArrayRequester->channelArrayConnect(
status,
ChannelArray::shared_pointer(),
PVArray::shared_pointer());
return ChannelArray::shared_pointer();
}
void ChannelLocal::printInfo()
{
printf("ChannelLocal provides access to service\n");
}
void ChannelLocal::printInfo(StringBuilder out)
{
*out += "ChannelLocal provides access to service";
}
}}

View File

@ -0,0 +1,222 @@
/* channelChannelProviderLocal.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
*/
#include <pv/serverContext.h>
#include <pv/channelProviderLocal.h>
namespace epics { namespace pvDatabase {
using namespace epics::pvData;
using namespace epics::pvAccess;
using namespace std;
static String providerName("local");
class LocalChannelCTX;
typedef std::tr1::shared_ptr<LocalChannelCTX> LocalChannelCTXPtr;
class LocalChannelCTX :
public epics::pvData::Runnable,
public std::tr1::enable_shared_from_this<LocalChannelCTX>
{
public:
POINTER_DEFINITIONS(LocalChannelCTX);
static LocalChannelCTXPtr create(
ChannelProviderLocalPtr const &channelProvider);
ChannelProviderLocalPtr getChannelProviderLocal() {return channelProvider;}
virtual ~LocalChannelCTX();
virtual void run();
private:
LocalChannelCTX(
ChannelProviderLocalPtr const &channelProvider);
shared_pointer getPtrSelf()
{
return shared_from_this();
}
epics::pvData::Event event;
ChannelProviderLocalPtr channelProvider;
epics::pvAccess::ServerContextImpl::shared_pointer ctx;
epics::pvData::Thread *thread;
};
LocalChannelCTXPtr LocalChannelCTX::create(
ChannelProviderLocalPtr const & channelProvider)
{
static LocalChannelCTXPtr pvServiceChannelCTX;
static Mutex mutex;
Lock xx(mutex);
if(pvServiceChannelCTX.get()==0) {
pvServiceChannelCTX = LocalChannelCTXPtr(
new LocalChannelCTX(channelProvider));
}
return pvServiceChannelCTX;
}
LocalChannelCTX::LocalChannelCTX(
ChannelProviderLocalPtr const &channelProvider)
:
channelProvider(channelProvider),
ctx(ServerContextImpl::create()),
thread(new Thread(
String("pvServiceChannel"),
lowerPriority,
this,
epicsThreadStackBig))
{}
LocalChannelCTX::~LocalChannelCTX()
{
ctx->shutdown();
// we need thead.waitForCompletion()
event.wait();
epicsThreadSleep(1.0);
delete thread;
}
void LocalChannelCTX::run()
{
registerChannelProvider(channelProvider);
String providerName = channelProvider->getProviderName();
ctx->setChannelProviderName(providerName);
ctx->initialize(getChannelAccess());
ctx->printInfo();
ctx->run(0);
ctx->destroy();
event.signal();
}
ChannelProviderLocalPtr ChannelProviderLocal::create()
{
ChannelProviderLocalPtr channelProvider(new ChannelProviderLocal());
LocalChannelCTX::create(channelProvider);
return channelProvider;
}
ChannelProviderLocal::ChannelProviderLocal()
: pvDatabase(PVDatabase::getMaster()),
beingDestroyed(false)
{
}
ChannelProviderLocal::~ChannelProviderLocal()
{
pvDatabase.reset();
}
void ChannelProviderLocal::destroy()
{
Lock xx(mutex);
if(beingDestroyed) return;
unregisterChannelProvider(getPtrSelf());
beingDestroyed = true;
ChannelLocalList::iterator iter;
for(iter = channelList.begin(); iter!=channelList.end(); ++iter) {
ChannelLocalPtr channel = *iter;
channel->destroy();
}
channelList.clear();
}
String ChannelProviderLocal::getProviderName()
{
return providerName;
}
ChannelFind::shared_pointer ChannelProviderLocal::channelFind(
String const & channelName,
ChannelFindRequester::shared_pointer const &channelFindRequester)
{
Lock xx(mutex);
bool found = false;
ChannelLocalList::iterator iter;
for(iter = channelList.begin(); iter!=channelList.end(); ++iter)
{
if((*iter)->getChannelName().compare(channelName)==0) {
found = true;
break;
}
}
if(found) {
channelFindRequester->channelFindResult(
Status::Ok,
ChannelFind::shared_pointer(),
true);
}
PVRecordPtr pvRecord = pvDatabase->findRecord(channelName);
if(pvRecord.get()!=NULL) {
channelFindRequester->channelFindResult(
Status::Ok,
ChannelFind::shared_pointer(),
true);
} else {
Status notFoundStatus(Status::STATUSTYPE_ERROR,String("pv not found"));
channelFindRequester->channelFindResult(
notFoundStatus,
ChannelFind::shared_pointer(),
false);
}
return ChannelFind::shared_pointer();
}
Channel::shared_pointer ChannelProviderLocal::createChannel(
String const & channelName,
ChannelRequester::shared_pointer const &channelRequester,
short priority)
{
return createChannel(channelName,channelRequester,priority,"");
}
Channel::shared_pointer ChannelProviderLocal::createChannel(
String const & channelName,
ChannelRequester::shared_pointer const &channelRequester,
short priority,
String const &address)
{
Lock xx(mutex);
ChannelLocalList::iterator iter;
for(iter = channelList.begin(); iter!=channelList.end(); ++iter)
{
if((*iter)->getChannelName().compare(channelName)==0) {
return *iter;
}
}
PVRecordPtr pvRecord = pvDatabase->findRecord(channelName);
if(pvRecord.get()!=NULL) {
Channel::shared_pointer channel(new ChannelLocal(
getPtrSelf(),channelRequester,pvRecord));
channelRequester->channelCreated(
Status::Ok,
channel);
return channel;
}
Status notFoundStatus(Status::STATUSTYPE_ERROR,String("pv not found"));
channelRequester->channelCreated(
notFoundStatus,
Channel::shared_pointer());
return Channel::shared_pointer();
}
void ChannelProviderLocal::removeChannel(
Channel::shared_pointer const & channel)
{
Lock xx(mutex);
if(beingDestroyed) return;
ChannelLocalList::iterator iter;
for(iter = channelList.begin(); iter!=channelList.end(); ++iter)
{
if((*iter).get()==channel.get()) {
channelList.erase(iter);
return;
}
}
}
}}

View File

@ -0,0 +1,164 @@
/* channelProviderLocal.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
*/
#ifndef CHANNELPROVIDERLOCAL_H
#define CHANNELPROVIDERLOCAL_H
#include <string>
#include <cstring>
#include <stdexcept>
#include <memory>
#include <set>
#include <pv/lock.h>
#include <pv/pvType.h>
#include <pv/pvData.h>
#include <pv/pvAccess.h>
#include <pv/pvDatabase.h>
#include <pv/status.h>
#include <pv/pvCopy.h>
namespace epics { namespace pvDatabase {
class ChannelProviderLocal;
typedef std::tr1::shared_ptr<ChannelProviderLocal> ChannelProviderLocalPtr;
class ChannelLocal;
typedef std::tr1::shared_ptr<ChannelLocal> ChannelLocalPtr;
typedef std::set<ChannelLocalPtr> ChannelLocalList;
class ChannelProviderLocal :
public epics::pvAccess::ChannelProvider,
public std::tr1::enable_shared_from_this<ChannelProviderLocal>
{
public:
POINTER_DEFINITIONS(ChannelProviderLocal);
static ChannelProviderLocalPtr create();
virtual ~ChannelProviderLocal();
virtual void destroy();
virtual epics::pvData::String getProviderName();
virtual epics::pvAccess::ChannelFind::shared_pointer channelFind(
epics::pvData::String const &channelName,
epics::pvAccess::ChannelFindRequester::shared_pointer const & channelFindRequester);
virtual epics::pvAccess::Channel::shared_pointer createChannel(
epics::pvData::String const &channelName,
epics::pvAccess::ChannelRequester::shared_pointer const &channelRequester,
short priority);
virtual epics::pvAccess::Channel::shared_pointer createChannel(
epics::pvData::String const &channelName,
epics::pvAccess::ChannelRequester::shared_pointer const &channelRequester,
short priority,
epics::pvData::String const &address);
void removeChannel(
epics::pvAccess::Channel::shared_pointer const &channel);
private:
shared_pointer getPtrSelf()
{
return shared_from_this();
}
ChannelProviderLocal();
PVDatabasePtr pvDatabase;
ChannelLocalList channelList;
epics::pvData::Mutex mutex;
bool beingDestroyed;
friend class ChannelProviderLocalRun;
};
class ChannelLocal :
public epics::pvAccess::Channel,
public std::tr1::enable_shared_from_this<ChannelLocal>
{
public:
POINTER_DEFINITIONS(ChannelLocal);
ChannelLocal(
ChannelProviderLocalPtr const &channelProvider,
epics::pvAccess::ChannelRequester::shared_pointer const & requester,
PVRecordPtr const & pvRecord
);
virtual ~ChannelLocal();
virtual void destroy();
virtual epics::pvData::String getRequesterName();
virtual void message(
epics::pvData::String const & message,
epics::pvData::MessageType messageType);
virtual epics::pvAccess::ChannelProvider::shared_pointer getProvider();
virtual epics::pvData::String getRemoteAddress();
virtual epics::pvAccess::Channel::ConnectionState getConnectionState();
virtual epics::pvData::String getChannelName();
virtual epics::pvAccess::ChannelRequester::shared_pointer getChannelRequester();
virtual bool isConnected();
virtual void getField(
epics::pvAccess::GetFieldRequester::shared_pointer const &requester,
epics::pvData::String const & subField);
virtual epics::pvAccess::AccessRights getAccessRights(
epics::pvData::PVField::shared_pointer const &pvField);
virtual epics::pvAccess::ChannelProcess::shared_pointer createChannelProcess(
epics::pvAccess::ChannelProcessRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
virtual epics::pvAccess::ChannelGet::shared_pointer createChannelGet(
epics::pvAccess::ChannelGetRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
virtual epics::pvAccess::ChannelPut::shared_pointer createChannelPut(
epics::pvAccess::ChannelPutRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
virtual epics::pvAccess::ChannelPutGet::shared_pointer createChannelPutGet(
epics::pvAccess::ChannelPutGetRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
virtual epics::pvAccess::ChannelRPC::shared_pointer createChannelRPC(
epics::pvAccess::ChannelRPCRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
virtual epics::pvData::Monitor::shared_pointer createMonitor(
epics::pvData::MonitorRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
virtual epics::pvAccess::ChannelArray::shared_pointer createChannelArray(
epics::pvAccess::ChannelArrayRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
virtual void printInfo();
virtual void printInfo(epics::pvData::StringBuilder out);
// following called by derived classes
void addChannelProcess(epics::pvAccess::ChannelProcess::shared_pointer const &);
void addChannelGet(epics::pvAccess::ChannelGet::shared_pointer const &);
void addChannelPut(epics::pvAccess::ChannelPut::shared_pointer const &);
void addChannelPutGet(epics::pvAccess::ChannelPutGet::shared_pointer const &);
void addChannelMonitor(epics::pvData::Monitor::shared_pointer const &);
void addChannelRPC(epics::pvAccess::ChannelRPC::shared_pointer const &);
void addChannelArray(epics::pvAccess::ChannelArray::shared_pointer const &);
void removeChannelProcess(epics::pvAccess::ChannelProcess::shared_pointer const &);
void removeChannelGet(epics::pvAccess::ChannelGet::shared_pointer const &);
void removeChannelPut(epics::pvAccess::ChannelPut::shared_pointer const &);
void removeChannelPutGet(epics::pvAccess::ChannelPutGet::shared_pointer const &);
void removeChannelMonitor(epics::pvData::Monitor::shared_pointer const &);
void removeChannelRPC(epics::pvAccess::ChannelRPC::shared_pointer const &);
void removeChannelArray(epics::pvAccess::ChannelArray::shared_pointer const &);
protected:
shared_pointer getPtrSelf()
{
return shared_from_this();
}
private:
ChannelProviderLocalPtr provider;
epics::pvAccess::ChannelRequester::shared_pointer requester;
PVRecordPtr pvRecord;
bool beingDestroyed;
std::set<epics::pvAccess::ChannelProcess::shared_pointer> channelProcessList;
std::set<epics::pvAccess::ChannelGet::shared_pointer> channelGetList;
std::set<epics::pvAccess::ChannelPut::shared_pointer> channelPutList;
std::set<epics::pvAccess::ChannelPutGet::shared_pointer> channelPutGetList;
std::set<epics::pvData::Monitor::shared_pointer> channelMonitorList;
std::set<epics::pvAccess::ChannelRPC::shared_pointer> channelRPCList;
std::set<epics::pvAccess::ChannelArray::shared_pointer> channelArrayList;
epics::pvData::Mutex mutex;
};
}}
#endif /* CHANNELPROVIDERLOCAL_H */

View File

@ -2,5 +2,6 @@ TOP = ..
include $(TOP)/configure/CONFIG
DIRS += record
DIRS += pvCopy
DIRS += server
include $(TOP)/configure/RULES_DIRS

17
test/server/Makefile Normal file
View File

@ -0,0 +1,17 @@
TOP=../..
include $(TOP)/configure/CONFIG
PROD_HOST += testExampleServer
testExampleServer_SRCS += testExampleServerMain.cpp
testExampleServer_LIBS += pvDatabase pvAccess pvData Com
PROD_HOST += exampleCounter
exampleCounter_SRCS += exampleCounterMain.cpp
exampleCounter_LIBS += pvDatabase pvAccess pvData Com
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

View File

@ -0,0 +1,102 @@
/*testExampleRecordMain.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/powerSupplyRecordTest.h>
#include <pv/channelProviderLocal.h>
using namespace std;
using std::tr1::static_pointer_cast;
using namespace epics::pvData;
using namespace epics::pvAccess;
using namespace epics::pvDatabase;
static PVStructurePtr createPowerSupply()
{
FieldCreatePtr fieldCreate = getFieldCreate();
StandardFieldPtr standardField = getStandardField();
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(
fieldCreate->createStructure(names,powerSupply));
}
int main(int argc,char *argv[])
{
PVDatabasePtr master = PVDatabase::getMaster();
ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
StandardPVFieldPtr standardPVField = getStandardPVField();
String properties;
ScalarType scalarType;
String recordName;
properties = "alarm,timeStamp";
scalarType = pvDouble;
recordName = "exampleDouble";
PVStructurePtr pvStructure;
pvStructure = standardPVField->scalar(scalarType,properties);
PVRecordPtr pvRecord = PVRecord::create(recordName,pvStructure);
{
pvRecord->lock_guard();
pvRecord->process();
}
pvStructure.reset();
bool result = master->addRecord(pvRecord);
pvRecord.reset();
recordName = "powerSupplyExample";
pvStructure = createPowerSupply();
PowerSupplyRecordTestPtr psr =
PowerSupplyRecordTest::create(recordName,pvStructure);
if(psr.get()==NULL) {
cout << "PowerSupplyRecordTest::create failed" << endl;
return 1;
}
pvStructure.reset();
result = master->addRecord(psr);
cout << "result of addRecord " << recordName << " " << result << endl;
psr.reset();
cout << "exampleServer\n";
string str;
while(true) {
cout << "Type exit to stop: \n";
getline(cin,str);
if(str.compare("exit")==0) break;
}
return 0;
}