From 7a0fda88ef1806499913c3a4b2debcd4ef3f29b1 Mon Sep 17 00:00:00 2001 From: Marty Kraimer Date: Wed, 12 Dec 2012 08:49:06 -0500 Subject: [PATCH] still early version --- documentation/pvDatabaseCPP.html | 293 +++++++-- documentation/pvDatabaseCPP_20121127.html | 733 ++++++++++++++++++++++ example/record/exampleRecord.cpp | 3 +- src/Makefile | 4 +- src/database/pvDatabase.cpp | 420 ------------- src/database/pvDatabase.h | 51 +- src/database/pvRecord.cpp | 634 +++++++++++++++++++ test/record/testRecord.cpp | 30 +- test/record/testRecord.h | 11 +- test/record/testRecordMain.cpp | 110 +++- 10 files changed, 1767 insertions(+), 522 deletions(-) create mode 100644 documentation/pvDatabaseCPP_20121127.html create mode 100644 src/database/pvRecord.cpp diff --git a/documentation/pvDatabaseCPP.html b/documentation/pvDatabaseCPP.html index ce9f458..a61cfe3 100644 --- a/documentation/pvDatabaseCPP.html +++ b/documentation/pvDatabaseCPP.html @@ -38,18 +38,20 @@

pvDatabaseCPP

-

EPICS v4 Working Group, Working Draft, 27-Nov-2012

+

EPICS v4 Working Group, Working Draft, 11-Dec-2012

Latest version:
pvDatabaseCPP.html + href="http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvDatabaseCPP/raw-file/tip/documentation/pvDatabaseCPP.html">pvDatabaseCPP.html
This version:
pvDatabaseCPP.html + href="http://epics-pvdata.hg.sourceforge.net/hgweb/epics-pvdata/pvDatabaseCPP/raw-file/tip/documentation/pvDatabaseCPP_20121211.html">pvDatabaseCPP20121211.html
Previous version:
-
None
+
pvDatabaseCPP_20121127.html +
Editors:
Marty Kraimer, BNL
@@ -95,12 +97,12 @@ V4 control system programming environment:

Status of this Document

-

This is the 27-Nov-2012 version of the definition of pvDatabaseCPP. -This is the original version. +

This is the 11-Dec-2012 version of the definition of pvDatabaseCPP.

This is the beginning of the implementation of pvDataBaseCPP. It describes the features that will be provided. -The class definitions for PVRecord and PVDatabase are defined but not implemented.

+The class definitions for PVRecord are implemented. +The class definition for PVDatabase are defined but not implemented.

@@ -231,11 +233,14 @@ ExampleRecord::ExampleRecord( bool ExampleRecord::isSynchronous() {return true;} void ExampleRecord::process( - RecordProcessRequesterPtr const &processRequester) + RecordProcessRequesterPtr const &processRequester,bool alreadyLocked) { + if(!alreadyLocked) lock(); pvValue->put(pvValue->get() + 1); processRequester->recordProcessResult(Status::Ok); + unlock(); processRequester->recordProcessComplete(); + dequeueProcessRequest(processRequester); }

where

@@ -354,29 +359,72 @@ phase.

database

The classes in pvDatabase.h implement a database of memory resident -smart records. The next subsection has the definitions for all the classes -defined in this header file. +smart records. It describes the following classes:

PVRecord
This provides the methods required by localChannelProvider to implement Channel.
PVRecordField
PVRecordStructure
-
These wrap PVField and PVStructure so that pvCopy and monitor can be implemented.
+
These wrap PVField and PVStructure so that pvCopy and monitor + can be implemented.
+
PVRecordClient
+
This is called by anything that acceses PVRecord.
PVListener
This is implemented by anything that wants to trap calls to the PVRecord::message.
RecordProcessRequester
This is implemented by anything that calls PVRecord::queueProcessRequest.
-
PVRecordClient
-
This is called by anything that acceses PVRecord.
+
RecordPutRequester
+
This is implemented by anything that calls PVRecord::queuePutRequest.
PVDatabase
This is a database of PVRecords.

Each class is described in a separate subsection.

+

C++ namespace and typedefs

+
+namespace epics { namespace pvDatabase {
+
+class PVRecord;
+typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
+
+class PVRecordField;
+typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
+typedef std::vector<PVRecordFieldPtr> PVRecordFieldPtrArray;
+typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;
+
+class PVRecordStructure;
+typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;
+
+class PVRecordClient;
+typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
+
+class PVListener;
+typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
+
+class RecordProcessRequester;
+typedef std::tr1::shared_ptr<RecordProcessRequester> RecordProcessRequesterPtr;
+
+class RecordPutRequester;
+typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;
+
+class PVDatabase;
+typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
+

class PVRecord

+

NOTES: +

+
PVRecord Methods
 class PVRecord
+     public epics::pvData::Requester,
+     public std::tr1::enable_shared_from_this<PVRecord>
 {
 public:
     POINTER_DEFINITIONS(PVRecord);
@@ -385,32 +433,48 @@ public:
         epics::pvData::PVStructurePtr const & pvStructure);
     virtual ~PVRecord();
     virtual void process(
-        RecordProcessRequesterPtr const &recordProcessRequester) = 0;
+        RecordProcessRequesterPtr const &recordProcessRequester,
+        bool alreadyLocked) = 0;
     virtual bool isSynchronous() = 0;
+    virtual bool requestImmediatePut(epics::pvData::PVFieldPtr const &pvField);
+    virtual void immediatePutDone();
+    virtual void destroy();
     epics::pvData::String getRecordName();
     PVRecordStructurePtr getPVRecordStructure();
     PVRecordFieldPtr findPVRecordField(
         epics::pvData::PVFieldPtr const & pvField);
+    bool addRequester(epics::pvData::RequesterPtr const & requester);
+    bool removeRequester(epics::pvData::RequesterPtr const & requester);
     void lock();
     void unlock();
-    void registerClient(PVRecordClientPtr const & pvRecordClient);
-    void unregisterClient(PVRecordClientPtr const & pvRecordClient);
+    bool tryLock();
+    void lockOtherRecord(PVRecordPtr const & otherRecord);
+    void addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
+    void removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
     void detachClients();
     void beginGroupPut();
     void endGroupPut();
-    void registerListener(PVListenerPtr const & pvListener);
-    void unregisterListener(PVListenerPtr const & pvListener);
-    void removeEveryListener();
-    epics::pvData::Status processRequest();
     void queueProcessRequest(
         RecordProcessRequesterPtr const &recordProcessRequester);
-    void addRequester(epics::pvData::RequesterPtr const & requester);
-    void removeRequester(epics::pvData::RequesterPtr const & requester);
+    void dequeueProcessRequest(
+        RecordProcessRequesterPtr const &recordProcessRequester);
+    void queuePutRequest(
+        RecordPutRequesterPtr const &recordPutRequester);
+    void putDone(
+        RecordPutRequesterPtr const &recordPutRequester);
+    virtual epics::pvData::String getRequesterName();
     void message(
         epics::pvData::String const & message,
         epics::pvData::MessageType messageType);
-    epics::pvData::String toString();
-    epics::pvData::String toString(int indentLevel);
+    void message(
+        PVRecordFieldPtr const & pvRecordField,
+        epics::pvData::String const & message,
+        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();
+
 };
 

The methods are: @@ -418,62 +482,132 @@ public:

PVRecord
The constructor. It requires a recordName and a top level PVStructure.
~PVRecord
-
The desctructor which must be virtual. A derived class must also have +
The destructor which must be virtual. A derived class must also have a virtual destructor.
process
-
Pure virtual method. Derived classes must implement this method.
+
Pure virtual method. +

Derived classes must implement this method.

+

A client must only call this method when + RecordProcessRequester::becomeProcessor is called as a result + of a queueProcessRequest. + 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.

+

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.

+

When processing is done the record calls two client callbacks:

+
+
RecordProcessRequester::recordProcessResult
+
This is called with the record still locked. + The clients can get data from the record.
+
RecordProcessRequester::recordProcessComplete
+
This is called with the record unlocked. + The client can no longer access the record.
+
+
isSynchronous
Pure virtual method. Derived classes must implement this method.
+
requestImmediatePut
+
This is a virtual method. +

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 stop

+

The default always returns false.

+

A record implementation can override the default and return true. + In it does requestImmediatePut it returns with the record locked.

+

The client can change the value of the associated field and then call + immediatePutDone

+
+
immediatePutDone
+
This is a virtual method. +

The default does nothing.

+

Must be called by client as a result of a call to requestImmediatePut + that returns true.

+
+
destroy
+
This is a virtual method. +

The default does nothing.

+
getRecordName
Return the recordName.
getPVRecordStructure
Get the top level PVStructure.
findPVRecordField
Given a PVFieldPtr return the PVRecordFieldPtr for the field.
+
addRequester
+
Add a requester to receive messages.
+
removeRequester
+
Remove a message requester.
lock
unlock
Lock and Unlock the record. Any code accessing the data in the record or calling other PVRecord methods must have the record locked.
-
registerClient
+
tryLock
+
If true then just like lock. + If falseclient can not access record. + A client can try to simultaneously hold the lock for more than two records + by calling this method. But must be willing to accept failure. +
+
lockOtherRecord
+
A client that holds the lock for one record can lock one other record. + A client must not call this if the client already has the lock for + more then one record. +
+
addPVRecordClient
Every client that accesses the record must call this so that the client can be notified when the record is deleted.
-
unregisterClient
+
removePVRecordClient
Client is no longer accessing the record.
detachClients
Ask all clients to detach from the record
+
addListener
+
Add a PVListener. This must be called before calling pvRecordField.addListener.
+
removeListener
+
Removes a listener. The listener will also be removed from all fields to which it is attached.
beginGroupPut
-
Begin a group of puts. This results in all registered PVListeners being called
+
Begin a group of puts. + This results in all registered PVListeners being called
endGroupPut
-
End a group of puts. This results in all registered PVListeners being called.
-
registerListener
-
Register a PVListener. This must be called before calling pvRecordField.addListener.
-
unregisterListener
-
Unregister a listener. The listener will also be removed from all fields to which it is attached.
-
removeEveryListener
-
This must be called by any code that is deleting or changing the structure of a record.
-
processRequest
-
This is a convenience method for clients that are willing to block if - process is asynchronous. It implements RecordProcessRequester. - If process is synchronous it just calls process and returns the result - to the caller. If process is asynchronous it calls queueProcessRequest, - and process and waits for completion and then returns the result to the caller.
+
End a group of puts. + This results in all registered PVListeners being called.
queueProcessRequest
Queue a process request.
-
addRequester
-
Add a requester to receive messages.
-
removeRequester
-
Remove a message requester.
+
dequeueProcessRequest
+
This must be called by record implementation after it has + completed a process request. +
+
queuePutRequest
+
Queue a put request. +

This is for code that wants to change data in a record without processing. + If RecordPutRequester::requestResult is called with result true + then the record is locked and the client can make changes. + When done the client must call putDone

+
+
putDone
+
Called by RecordPutRequester after changing values in record. + This method unlocks the record
+
getRequesterName
+
virtual method of Requester +
message
Can be called by implementation code. The message will be sent to every requester.
+
init
+
This method must be called by derived class + after class is completely constructed.

class PVRecordField

 class PVRecordField {
+     public virtual epics::pvData::PostHandler,
+     public std::tr1::enable_shared_from_this<PVRecordField>
 public:
     POINTER_DEFINITIONS(PVRecordField);
     PVRecordField(
         epics::pvData::PVFieldPtr const & pvField,
+        PVRecordStructurePtr const &parent,
         PVRecordPtr const & pvRecord);
     virtual ~PVRecordField();
     PVRecordStructurePtr getParent();
@@ -482,8 +616,8 @@ public:
     epics::pvData::String getFullName();
     PVRecordPtr getPVRecord();
     bool addListener(PVListenerPtr const & pvListener);
-    void removeListener(PVListenerPtr const & pvListener);
-    void postPut();
+    virtual void removeListener(PVListenerPtr const & pvListener);
+    virtual void postPut();
     virtual void message(
         epics::pvData::String const & message,
         epics::pvData::MessageType messageType);
@@ -533,9 +667,8 @@ public:
     virtual ~PVRecordStructure();
     PVRecordFieldPtrArrayPtr getPVRecordFields();
     epics::pvData::PVStructurePtr getPVStructure();
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
+    virtual void removeListener(PVListenerPtr const & pvListener);
+    virtual void postPut();
 };
 

When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure @@ -550,13 +683,31 @@ that holds the data. It has the following methods:

Get the PVRecordField array for the subfields
getPVStructure
Get the PVStructure for this field.
-
message
-
Called by implementation code. It calls PVRecord::message after prepending the full - fieldname.
+
removeListener
+
Remove a PVListener.
+
postPut
+
This is called by the code that implements the data interface. + It is called whenever the put method is called.
+

class PVRecordClient

+
+class PVRecordClient {
+    POINTER_DEFINITIONS(PVRecordClient);
+    virtual ~PVRecordClient();
+    virtual void detach(PVRecordPtr const & pvRecord);
+};
+
+

where

+
+
~PVRecordClient
+
The destructor.
+
detach
+
The record is being removed from the master database,
+

class PVListener

 class PVListener {
+    virtual public PVRecordClient
 public:
     POINTER_DEFINITIONS(PVListener);
     virtual ~PVListener();
@@ -566,7 +717,6 @@ public:
         requested,PVRecordFieldPtr const & pvRecordField) = 0;
     virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
     virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void unlisten(PVRecordPtr const & pvRecord) = 0;
 };
 

where

@@ -586,19 +736,17 @@ public:
A related set of changes is being started.
endGroupPut
A related set of changes is done.
-
unlisten
-
The PVLister is being removed from the record. - This is called when the record is being destroyed or when the record structure - (not the data values) is being changed.

class RecordProcessRequester

 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;
@@ -608,6 +756,8 @@ public:
 
~RecordProcessRequester
The destructor.
+
recordDestroyed
+
Record is being destroyed.
becomeProcessor
Called as a result of queueRequeProcessst. The requester can the call process.
recordProcessResult
@@ -620,20 +770,29 @@ public: If the process requester called process with leaveActive true then the requester must call setInactive.
-

class PVRecordClient

+

class RecordPutRequester

-class PVRecordClient {
-    POINTER_DEFINITIONS(PVRecordClient);
-    virtual ~PVRecordClient();
-    virtual void detach(PVRecordPtr const & pvRecord);
+class RecordPutRequester :
+    virtual public PVRecordClient
+{
+public:
+    POINTER_DEFINITIONS(RecordPutRequester);
+    virtual ~RecordPutRequester();
+    virtual void requestResult(bool result) = 0;
 };
 

where

-
~PVRecordClient
+
~RecordPutRequester
The destructor.
-
detach
-
The record is being removed from the master database,
+
requestResult
+
Result of a call to queuePutRequest. If requestResult is false + then the caller can not access the record. + If requestResult is true + then the record is locked and the caller can get and put data in the record. + When done the caller must call PVRecord::putDone, which will unlock the + record. +

class PVDatabase

diff --git a/documentation/pvDatabaseCPP_20121127.html b/documentation/pvDatabaseCPP_20121127.html
new file mode 100644
index 0000000..ce9f458
--- /dev/null
+++ b/documentation/pvDatabaseCPP_20121127.html
@@ -0,0 +1,733 @@
+
+
+
+
+  
+  pvDatabaseCPP
+  
+  
+  
+  
+  
+
+
+
+
+
+

pvDatabaseCPP

+ + +

EPICS v4 Working Group, Working Draft, 27-Nov-2012

+
+
Latest version:
+
pvDatabaseCPP.html +
+
This version:
+
pvDatabaseCPP.html +
+
Previous version:
+
None
+
Editors:
+
Marty Kraimer, BNL
+
+ + +
+
+ +

Abstract

+ +

This document describes pvDatabaseCPP, +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 minimum that an extenson must provide is a top level PVStructure and a process method +but the framework provides for complex extensions.

+ +

EPICS version 4 is a set of related products in the EPICS +V4 control system programming environment:

+
+
pvData
+
pvData (Process Variable Data) defines and implements an efficent way + to store, access, and communicate memory resident structured data
+
pvAccess
+
pvAccess is a software library for high speed controls network communications, + optimized for pvData
+
pvIOC
+
pvIOC is a software framework for building network accessable "smart" real time + databases, suitable for interfacing devices in a distributed control system, + that can exchange pvData over pvAccess. +
+
pvService
+
A middle layer for implementing data services.
+
+ +

Each of these products has a Java and a C++ implementation.

+ +

Status of this Document

+ +

This is the 27-Nov-2012 version of the definition of pvDatabaseCPP. +This is the original version. +

+

This is the beginning of the implementation of pvDataBaseCPP. +It describes the features that will be provided. +The class definitions for PVRecord and PVDatabase are defined but not implemented.

+ + +
+

Table of Contents

+
+
+ + +

Introduction

+

Overview

+

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 +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. +

+ +

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 +Channel interfaces as defined by pvAccess. +This local provider is accessed by the remote pvAccess server. +A record is smart because code can be attached to a record.

+

This document describes components that provides the following features: +

+
database
+
This encapsulates the concept of a database of memory resident smart records. + The two main components are: +
+
pvRecord
+
This encapsulates the concept of a smart record. It can be processed. + Changes to field values can be trapped. A record can be locked.
+
pvDatabase
+
This is a database of pvRecords. + Records can be added and removed from a database.
+
+
localChannelProvider
+
This is a complete implementation of ChannelProvider and Channel as defined by pvAccess. + It is used by the server side of pvAccess to attach to pvRecords. + This component also includes the monitor and pvCopy components from pvIOCJava
+
+

database 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 +level PVStructure and the following two methods:

+
+
process
+
This is what makes a record smart. + 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 not complete. + Instead process invokes other threads that will complete the processing at a later time.
+
isSynchronous
+
Which execution model is being implemented.
+
+

Example PVRecord Extension

+

Directory example/record has an example PVRecord implementation. +It implements a counter. +The top level structure is:

+
+structure
+    long value
+
+

NOTE: The example compiles but does not build because nothing +is implemented.

+ +

exampleRecord.h

+

This is the class description. +The example extends PVRecord.

+
+class ExampleRecord :
+  public virtual PVRecord
+{
+public:
+    POINTER_DEFINITIONS(ExampleRecord);
+    static PVRecordPtr create(epics::pvData::String const & recordName);
+    virtual ~ExampleRecord();
+    virtual bool isSynchronous();
+    virtual void process(
+        epics::pvDatabase::RecordProcessRequesterPtr const &processRequester);
+private:
+    ExampleRecord(epics::pvData::String const & recordName,
+        epics::pvData::PVStructurePtr const & pvStructure,
+        epics::pvData::PVLongPtr const &pvValue);
+    epics::pvData::PVLongPtr pvValue;
+};
+
+

where

+
+
create
+
This is example specific. See the implemention for details.
+
~ExampleRecord
+
The destructor must be declared virtual.
+
isSynchronous
+
The implementation must say if process is synchronous or asynchronous.
+
process
+
The implementation.
+
ExampleRecord
+
For the example this is private.
+
+ +

exampleRecord.cpp

+

This is the class implementation.

+
+ExampleRecord::~ExampleRecord(){}
+
+PVRecordPtr ExampleRecord::create(String const & recordName)
+{
+    String properties;
+    PVStructurePtr pvStructure = getStandardPVField()->scalar(pvLong,properties);
+    PVLongPtr pvValue =  pvStructure->getLongField("value");
+    PVRecordPtr pvRecord(new ExampleRecord(recordName,pvStructure,pvValue));
+    return pvRecord;
+}
+
+ExampleRecord::ExampleRecord(
+    String const & recordName,
+    PVStructurePtr const & pvStructure,
+    PVLongPtr const &pvValue)
+: PVRecord(recordName,pvStructure),
+  pvValue(pvValue)
+{}
+
+bool ExampleRecord::isSynchronous() {return true;}
+
+void ExampleRecord::process(
+    RecordProcessRequesterPtr const &processRequester)
+{
+    pvValue->put(pvValue->get() + 1);
+    processRequester->recordProcessResult(Status::Ok);
+    processRequester->recordProcessComplete();
+}
+
+

where

+
+
create
+
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. +
+
~ExampleRecord
+
Does not have to do anything because of shared pointers.
+
ExampleRecord
+
Calls the base class constructor and sets pvValue.
+
isSynchronous
+
The example is synchronous.
+
process
+
Gets the curent value, increments it, and puts the new value. + It than calls two processRequester callbacks.
+
+ +

exampleRecordMain.cpp

+

This is a main for creating and running the example.

+
+int main(int argc,char *argv[])
+{
+    String recordName("exampleRecord");
+    PVRecordPtr pvRecord = ExampleRecord::create(recordName);
+    PVDatabasePtr pvDatabase = PVDatabase::getMaster();
+    pvDatabase->addRecord(pvRecord);
+    cout << recordName << "\n";
+    string str;
+    while(true) {
+        cout << "Type exit to stop: \n";
+        getline(cin,str);
+        if(str.compare("exit")==0) break;
+
+    }
+    return 0;
+}
+
+

The main program creates an example record and adds it to the database. +It then runs until the process is stopped by typing exit. +

Until the process is stopped, +pvAccess clients can put and get the value field. +For example

+
+pvget exampleRecord
+pvput exampleRecord 5
+
+

Will both work.

+

Phased Development

+

This documentation describes the first phase of a phased implementation of pvDatabaseCPP: +

+
pvRecord +
Wrapper on PVStructure that implements methods required by Local Channel Provider.
+
pvDatabase +
Database of PVRecords. Has methods find, add, and remove.
+
Local Channel Provider
+
These two features will be the first phase. + But only synchronous record processing will be supported.
+
+

Future phases of pvDatabaseCPP should include:

+
+
Install
+
This provides on-line add and delete.
+
Field support
+
Add ability to optionally add support to fields. + In addition some of the basic support defined in pvIOCJava will also be implemented.
+
XML parser
+
This provides the ability to create record instances without writing any code.
+
+

The completion of each phase provides useful features that can be used without waiting for the +completion of later phases. +The rest of this document discusses only the first phase.

+

Features Required for localChannelProvider

+
+
pvCopy
+
Creates a PVStructure that contains a copy of an arbitary + subset of the fields of another top level PVStructure. + It can copy data between the two and maintains a bitSet that show + which fields are changed.
+
monitor
+
This provides the ability to monitor changes to fields of a record.
+
PVRecord and PVDatabase
+
Defined below.
+
local ChannelProvider
+
This is the pvAccess package in pvIOCJava. + The localChannelProvider will access data from PVRecords. + It will implement all channel methods except channelRPC.
+
+

Minumum Features Required for pvRecord

+

The first phase will only implement record processing, i. e. +the process method has to do everything itself without any generic field support. +This will be sufficient for starting to implement services. +The following are the minimium features required

+
+
PVDatabase
+
This holds a set of PVRecords. It has methods to find, add, and remove records.
+
PVRecord
+
This, and a set of related interfaces, provide the following: +
+
PVStructure
+
PVRecord is a wrapper on a top level pvStructure.
+
Record locking
+
A record can be locked and unlocked. + A record must be locked whenever data in the pvStructure is accessed.
+
Trapping data changes
+
A client can request to be notified when data in the pvStructure is modified. + It can do this on a field by field basis.
+
+
+
+

The following sections provide a first attempt to describe the classes required for the first +phase.

+

The last section gives a brief overview of the features provided by pvIOCJava.

+ +

database

+

The classes in pvDatabase.h implement a database of memory resident +smart records. The next subsection has the definitions for all the classes +defined in this header file. +It describes the following classes:

+
+
PVRecord
+
This provides the methods required by localChannelProvider to implement Channel.
+
PVRecordField
+
PVRecordStructure
+
These wrap PVField and PVStructure so that pvCopy and monitor can be implemented.
+
PVListener
+
This is implemented by anything that wants to trap calls to the PVRecord::message.
+
RecordProcessRequester
+
This is implemented by anything that calls PVRecord::queueProcessRequest.
+
PVRecordClient
+
This is called by anything that acceses PVRecord.
+
PVDatabase
+
This is a database of PVRecords.
+
+

Each class is described in a separate subsection.

+ +

class PVRecord

+
+class PVRecord
+{
+public:
+    POINTER_DEFINITIONS(PVRecord);
+    PVRecord(
+        epics::pvData::String const & recordName,
+        epics::pvData::PVStructurePtr const & pvStructure);
+    virtual ~PVRecord();
+    virtual void process(
+        RecordProcessRequesterPtr const &recordProcessRequester) = 0;
+    virtual bool isSynchronous() = 0;
+    epics::pvData::String getRecordName();
+    PVRecordStructurePtr getPVRecordStructure();
+    PVRecordFieldPtr findPVRecordField(
+        epics::pvData::PVFieldPtr const & pvField);
+    void lock();
+    void unlock();
+    void registerClient(PVRecordClientPtr const & pvRecordClient);
+    void unregisterClient(PVRecordClientPtr const & pvRecordClient);
+    void detachClients();
+    void beginGroupPut();
+    void endGroupPut();
+    void registerListener(PVListenerPtr const & pvListener);
+    void unregisterListener(PVListenerPtr const & pvListener);
+    void removeEveryListener();
+    epics::pvData::Status processRequest();
+    void queueProcessRequest(
+        RecordProcessRequesterPtr const &recordProcessRequester);
+    void addRequester(epics::pvData::RequesterPtr const & requester);
+    void removeRequester(epics::pvData::RequesterPtr const & requester);
+    void message(
+        epics::pvData::String const & message,
+        epics::pvData::MessageType messageType);
+    epics::pvData::String toString();
+    epics::pvData::String toString(int indentLevel);
+};
+
+

The methods are: +

+
PVRecord
+
The constructor. It requires a recordName and a top level PVStructure.
+
~PVRecord
+
The desctructor which must be virtual. A derived class must also have + a virtual destructor.
+
process
+
Pure virtual method. Derived classes must implement this method.
+
isSynchronous
+
Pure virtual method. Derived classes must implement this method.
+
getRecordName
+
Return the recordName.
+
getPVRecordStructure
+
Get the top level PVStructure.
+
findPVRecordField
+
Given a PVFieldPtr return the PVRecordFieldPtr for the field.
+
lock
+
unlock
+
Lock and Unlock the record. + Any code accessing the data in the record or calling other PVRecord methods + must have the record locked.
+
registerClient
+
Every client that accesses the record must call this so that the client can be notified when the record is deleted.
+
unregisterClient
+
Client is no longer accessing the record.
+
detachClients
+
Ask all clients to detach from the record
+
beginGroupPut
+
Begin a group of puts. This results in all registered PVListeners being called
+
endGroupPut
+
End a group of puts. This results in all registered PVListeners being called.
+
registerListener
+
Register a PVListener. This must be called before calling pvRecordField.addListener.
+
unregisterListener
+
Unregister a listener. The listener will also be removed from all fields to which it is attached.
+
removeEveryListener
+
This must be called by any code that is deleting or changing the structure of a record.
+
processRequest
+
This is a convenience method for clients that are willing to block if + process is asynchronous. It implements RecordProcessRequester. + If process is synchronous it just calls process and returns the result + to the caller. If process is asynchronous it calls queueProcessRequest, + and process and waits for completion and then returns the result to the caller.
+
queueProcessRequest
+
Queue a process request.
+
addRequester
+
Add a requester to receive messages.
+
removeRequester
+
Remove a message requester.
+
message
+
Can be called by implementation code. + The message will be sent to every requester.
+
+

class PVRecordField

+
+class PVRecordField {
+public:
+    POINTER_DEFINITIONS(PVRecordField);
+    PVRecordField(
+        epics::pvData::PVFieldPtr const & pvField,
+        PVRecordPtr const & pvRecord);
+    virtual ~PVRecordField();
+    PVRecordStructurePtr getParent();
+    epics::pvData::PVFieldPtr getPVField();
+    epics::pvData::String getFullFieldName();
+    epics::pvData::String getFullName();
+    PVRecordPtr getPVRecord();
+    bool addListener(PVListenerPtr const & pvListener);
+    void removeListener(PVListenerPtr const & pvListener);
+    void postPut();
+    virtual void message(
+        epics::pvData::String const & message,
+        epics::pvData::MessageType messageType);
+};
+
+

When PVRecord is created it creates a PVRecordField for every field in the PVStructure +that holds the data. It has the following methods: +

+ +
+
PVRecordField
+
The constructor.
+
~PVRecordField
+
The destructor.
+
getParent
+
Get the parent PVRecordStructure for this field.
+
getPVField
+
Get the PVField associated with this PVRecordField.
+
getFullFieldName
+
This gets the full name of the field, i.e. field,field,..
+
getFullName
+
This gets recordName plus the full name of the field, i.e. recordName.field,field,..
+
getPVRecord
+
Returns the PVRecord to which this field belongs.
+
addListener
+
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.
+
removeListener
+
Remove a PVListener.
+
postPut
+
This is called by the code that implements the data interface. + It is called whenever the put method is called.
+
message
+
Called by implementation code. It calls PVRecord::message after prepending the full + fieldname.
+
+

class PVRecordStructure

+
+class PVRecordStructure : public PVRecordField {
+public:
+    POINTER_DEFINITIONS(PVRecordStructure);
+    PVRecordStructure(
+        epics::pvData::PVStructurePtr const & pvStructure,
+        PVRecordFieldPtrArrayPtr const & pvRecordField);
+    virtual ~PVRecordStructure();
+    PVRecordFieldPtrArrayPtr getPVRecordFields();
+    epics::pvData::PVStructurePtr getPVStructure();
+    virtual void message(
+        epics::pvData::String const & message,
+        epics::pvData::MessageType messageType);
+};
+
+

When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure +that holds the data. It has the following methods: +

+
+
PVRecordStructure
+
The constructor.
+
~PVRecordStructure
+
The destructor.
+
getPVRecordFields
+
Get the PVRecordField array for the subfields
+
getPVStructure
+
Get the PVStructure for this field.
+
message
+
Called by implementation code. It calls PVRecord::message after prepending the full + fieldname.
+
+

class PVListener

+
+class PVListener {
+public:
+    POINTER_DEFINITIONS(PVListener);
+    virtual ~PVListener();
+    virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
+    virtual void dataPut(
+        PVRecordStructurePtr const &
+        requested,PVRecordFieldPtr const & pvRecordField) = 0;
+    virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
+    virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
+    virtual void unlisten(PVRecordPtr const & pvRecord) = 0;
+};
+
+

where

+
+
~PVListener
+
The destructor.
+
dataPut(PVRecordFieldPtr const & pvRecordField)
+
pvField has been modified. + This is called if the listener has called PVRecordField::addListener for pvRecordField.
+
dataPut( + PVRecordStructurePtr const & + requested,PVRecordFieldPtr const & pvRecordField)
+
pvField has been modified. + Requested is the field to which the requester issued a pvField-&addListener. + This is called if the listener has called PVRecordField-&addListener for requested.
+
beginGroupPut
+
A related set of changes is being started.
+
endGroupPut
+
A related set of changes is done.
+
unlisten
+
The PVLister is being removed from the record. + This is called when the record is being destroyed or when the record structure + (not the data values) is being changed.
+
+

class RecordProcessRequester

+
+class RecordProcessRequester :
+    virtual public epics::pvData::Requester
+{
+public:
+    POINTER_DEFINITIONS(RecordProcessRequester);
+    virtual ~RecordProcessRequester();
+    virtual void becomeProcessor() = 0;
+    virtual void recordProcessResult(epics::pvData::Status status) = 0;
+    virtual void recordProcessComplete() = 0;
+};
+
+

where

+
+
~RecordProcessRequester
+
The destructor.
+
becomeProcessor
+
Called as a result of queueRequeProcessst. The requester can the call process.
+
recordProcessResult
+
The results of record processing. + This is called with the record locked so that the process requester + can access data from the record.
+
recordProcessComplete
+
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.
+
+

class PVRecordClient

+
+class PVRecordClient {
+    POINTER_DEFINITIONS(PVRecordClient);
+    virtual ~PVRecordClient();
+    virtual void detach(PVRecordPtr const & pvRecord);
+};
+
+

where

+
+
~PVRecordClient
+
The destructor.
+
detach
+
The record is being removed from the master database,
+
+

class PVDatabase

+
+class PVDatabase : virtual public epics::pvData::Requester {
+public:
+    POINTER_DEFINITIONS(PVDatabase);
+    static PVDatabasePtr getMaster();
+    virtual ~PVDatabase();
+    PVRecordPtr findRecord(epics::pvData::String const& recordName);
+    bool addRecord(PVRecordPtr const & record);
+    bool removeRecord(PVRecordPtr const & record);
+private:
+    PVDatabase();
+};
+
+

where

+
+
getMaster
+
Get the master database. This is the database that localChannelProvider access.
+
~PVDatabase
+
The destructor.
+
findRecord
+
Find a record. An empty pointer is returned if the record is not in the database.
+
addRecord
+
Add a record to the database. + If the record already exists it is not modified and false is returned.
+
removeRecord
+
Remove a record from the database. + If the record was not in the database false is returned.
+
+

Local Channel Provider

+

Not yet described.

+

A brief description is that it must implement the following components of pvIOCJava:

+
+
pvCopy
+
monitor
+
pvAccess
+
See the next section for a description
+
+

Summary of Packages in pvIOCJAVA

+

The following are the direct sub packages of pvIOCJava/src/org/epics/pvioc:

+
+
pvCopy
+
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.
+
monitor
+
This provides the ability to monitor changes to a PVRecord. It is required for pvAccess monitors.
+
pvAccess
+
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.
+
database
+
This defines and implements PVRecord, PVDatabase , and PVListener. + It supports the basic feature required the implement a local Channel Provider.
+
support
+
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.
+
install
+
This provides the ability to dynamically initialize and add new PVRecords. It also provides + the ability to dynamicall delete PVRecords. +
xml
+
This provides the ability to configure record instances without writing code.
+
util
+
This is misnamed since it is code related to scanning.
+
pdrv
+
This is portDriver, which is a proposed sucessor to the asynManager component of asynDriver.
+
swtshell
+
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. +
+
caV3
+
This has two components: +
+
ClientFactory
+
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.
+
ServerFactory
+
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.
+
+
+
v3a
+
I do not know what this is.
+
+

In addition there is one class file JavaIOC.java. +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.

+ +
+ + diff --git a/example/record/exampleRecord.cpp b/example/record/exampleRecord.cpp index d30dcf5..fd5950d 100644 --- a/example/record/exampleRecord.cpp +++ b/example/record/exampleRecord.cpp @@ -41,7 +41,8 @@ PVRecordPtr ExampleRecord::create(String const & recordName) pvRecord->init(); return pvRecord; } - ExampleRecord::ExampleRecord( + +ExampleRecord::ExampleRecord( String const & recordName, PVStructurePtr const & pvStructure, PVLongPtr const &pvValue) diff --git a/src/Makefile b/src/Makefile index 76bb9ad..5959855 100644 --- a/src/Makefile +++ b/src/Makefile @@ -11,8 +11,8 @@ SRC_DIRS += $(DATABASE)/database INC += pvDatabase.h #INC += pvCopy.h #INC += monitor.h -LIBSRCS += pvDatabase.cpp -#LIBSRCS += pvRecord.cpp +#LIBSRCS += pvDatabase.cpp +LIBSRCS += pvRecord.cpp #LIBSRCS += recordProcess.cpp #LIBSRCS += pvCopy.cpp #LIBSRCS += monitor.cpp diff --git a/src/database/pvDatabase.cpp b/src/database/pvDatabase.cpp index d1cad60..49f3b4f 100644 --- a/src/database/pvDatabase.cpp +++ b/src/database/pvDatabase.cpp @@ -17,426 +17,6 @@ using namespace epics::pvAccess; namespace epics { namespace pvDatabase { -PVRecord::PVRecord( - String const & recordName, - PVStructurePtr const & pvStructure) -: recordName(recordName), - pvStructure(pvStructure), - convert(getConvert()), - thelock(mutex), - depthGroupPut(0) -{ - thelock.unlock(); -} - -void PVRecord::init() -{ - PVRecordStructurePtr parent; - pvRecordStructure = PVRecordStructurePtr( - new PVRecordStructure(pvStructure,parent,getPtrSelf())); - pvRecordStructure->init(); - pvStructure->setRequester(getPtrSelf()); -} - -PVRecord::~PVRecord() {} - -String PVRecord::getRecordName() {return recordName;} - -PVRecordStructurePtr PVRecord::getPVRecordStructure() {return pvRecordStructure;} - -PVRecordFieldPtr PVRecord::findPVRecordField(PVFieldPtr const & pvField) -{ - return findPVRecordField(pvRecordStructure,pvField); -} - -PVRecordFieldPtr PVRecord::findPVRecordField( - PVRecordStructurePtr const & pvrs, - PVFieldPtr const & pvField) -{ - size_t desiredOffset = pvField->getFieldOffset(); - PVFieldPtr pvf = pvrs->getPVField(); - size_t offset = pvf->getFieldOffset(); - if(offset==desiredOffset) return pvrs; - PVRecordFieldPtrArrayPtr pvrfpap = pvrs->getPVRecordFields(); - PVRecordFieldPtrArray::iterator iter; - for (iter = pvrfpap.get()->begin(); iter!=pvrfpap.get()->end(); iter++ ) { - PVRecordFieldPtr pvrf = *iter; - pvf = pvrf->getPVField(); - offset = pvf->getFieldOffset(); - if(offset==desiredOffset) return pvrf; - size_t nextOffset = pvf->getNextFieldOffset(); - if(nextOffset<=desiredOffset) continue; - return findPVRecordField( - static_pointer_cast(pvrf), - pvField); - } - throw std::logic_error( - recordName + " pvField " - + pvField->getFieldName() + " not in PVRecord"); -} - -void PVRecord::lock() {thelock.lock();} - -void PVRecord::unlock() {thelock.unlock();} - -bool PVRecord::tryLock() {return thelock.tryLock();} - -bool PVRecord::addPVRecordClient(PVRecordClientPtr const & pvRecordClient) -{ - lock(); - std::list::iterator iter; - for (iter = pvRecordClientList.begin(); - iter!=pvRecordClientList.end(); - iter++ ) - { - if((*iter).get()==pvRecordClient.get()) { - unlock(); - return false; - } - } - pvRecordClientList.push_back(pvRecordClient); - unlock(); - return true; -} - -bool PVRecord::removePVRecordClient(PVRecordClientPtr const & pvRecordClient) -{ - lock(); - std::list::iterator iter; - for (iter = pvRecordClientList.begin(); - iter!=pvRecordClientList.end(); - iter++ ) - { - if((*iter).get()==pvRecordClient.get()) { - pvRecordClientList.erase(iter); - unlock(); - return true; - } - } - unlock(); - return false; -} - -void PVRecord::detachClients() -{ - lock(); - std::list::iterator iter; - for (iter = pvRecordClientList.begin(); - iter!=pvRecordClientList.end(); - iter++ ) - { - (*iter)->detach(getPtrSelf()); - } - pvRecordClientList.clear(); - unlock(); -} - -void PVRecord::beginGroupPut() -{ - if(++depthGroupPut>1) return; - std::list::iterator iter; - for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++) - { - (*iter).get()->beginGroupPut(getPtrSelf()); - } -} - -void PVRecord::endGroupPut() -{ - if(--depthGroupPut>0) return; - std::list::iterator iter; - for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++) - { - (*iter).get()->endGroupPut(getPtrSelf()); - } -} - -bool PVRecord::addListener(PVListenerPtr const & pvListener) -{ - lock(); - std::list::iterator iter; - for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ ) - { - if((*iter).get()==pvListener.get()) { - unlock(); - return false; - } - } - pvListenerList.push_back(pvListener); - unlock(); - return true; -} - -bool PVRecord::removeListener(PVListenerPtr const & pvListener) -{ - lock(); - std::list::iterator iter; - for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ ) - { - if((*iter).get()==pvListener.get()) { - pvListenerList.erase(iter); - unlock(); - return true; - } - } - unlock(); - return false; -} - -void PVRecord::queueProcessRequest( - RecordProcessRequesterPtr const &recordProcessRequester) -{ - bool isFirst = false; - lock(); - processRequesterQueue.push_back(recordProcessRequester); - if(processRequesterQueue.size()==1) isFirst = true; - unlock(); - if(isFirst) recordProcessRequester->becomeProcessor(); -} - -void PVRecord::dequeueProcessRequest( - RecordProcessRequesterPtr const &recordProcessRequester) -{ - RecordProcessRequesterPtr next; - lock(); - RecordProcessRequesterPtr processRequester = - processRequesterQueue[0]; - if(processRequester.get()!=recordProcessRequester.get()) { - message( - "PVRecord::dequeueProcessRequest illegal requester", - errorMessage); - return; - } - processRequesterQueue.pop_front(); - if(processRequesterQueue.size()>0) next = processRequesterQueue[0]; - unlock(); - if(next.get()!=NULL) next->becomeProcessor(); -} - -bool PVRecord::addRequester(epics::pvData::RequesterPtr const & requester) -{ - lock(); - std::list::iterator iter; - for (iter = requesterList.begin(); iter!=requesterList.end(); iter++ ) { - if((*iter).get()==requester.get()) { - unlock(); - return false; - } - } - requesterList.push_back(requester); - unlock(); - return true; -} - -bool PVRecord::removeRequester(epics::pvData::RequesterPtr const & requester) -{ - lock(); - std::list::iterator iter; - for (iter = requesterList.begin(); iter!=requesterList.end(); iter++ ) { - if((*iter).get()==requester.get()) { - requesterList.erase(iter); - unlock(); - return true; - } - } - unlock(); - return false; -} - -void PVRecord::message(String const & message,MessageType messageType) -{ - if(requesterList.size()==0 ) { - String xxx(getMessageTypeName(messageType) + " " + message); - printf("%s\n",xxx.c_str()); - return; - } - std::list::iterator iter; - for(iter = requesterList.begin(); iter != requesterList.end(); ++iter) { - (*iter)->message(message,messageType); - } -} - -void PVRecord::message( - PVRecordFieldPtr const & pvRecordField, - String const & message, - MessageType messageType) -{ - this->message(pvRecordField->getFullName() + " " + message,messageType); -} - -void PVRecord::toString(StringBuilder buf) -{ - toString(buf,0); -} - -void PVRecord::toString(StringBuilder buf,int indentLevel) -{ - *buf += "\nrecord " + recordName + " "; - pvRecordStructure->getPVStructure()->toString(buf, indentLevel); -} - -PVRecordField::PVRecordField( - PVFieldPtr const & pvField, - PVRecordStructurePtr const &parent, - PVRecordPtr const & pvRecord) -: pvField(pvField), - parent(parent), - pvRecord(pvRecord) -{ -} - -void PVRecordField::init() -{ - fullFieldName = pvField->getFieldName(); - PVRecordStructurePtr pvParent = parent; - while(pvParent.get()!= NULL) { - String parentName = pvParent->getPVField()->getFieldName(); - if(parentName.size()>0) { - fullFieldName = pvParent->getPVField()->getFieldName() - + '.' + fullFieldName; - } - pvParent = pvParent->getParent(); - } - if(fullFieldName.size()>0) { - fullName = pvRecord->getRecordName() + '.' + fullFieldName; - } else { - fullName = pvRecord->getRecordName(); - } - pvField->setPostHandler(getPtrSelf()); -} - -PVRecordField::~PVRecordField() {} - -PVRecordStructurePtr PVRecordField::getParent() {return parent;} - -PVFieldPtr PVRecordField::getPVField() {return pvField;} - -String PVRecordField::getFullFieldName() {return fullFieldName; } - -String PVRecordField::getFullName() {return fullName; } - -PVRecordPtr PVRecordField::getPVRecord() {return pvRecord;} - -bool PVRecordField::addListener(PVListenerPtr const & pvListener) -{ - std::list::iterator iter; - for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ ) { - if((*iter).get()==pvListener.get()) { - return false; - } - } - pvListenerList.push_back(pvListener); - return true; -} - -void PVRecordField::removeListener(PVListenerPtr const & pvListener) -{ - std::list::iterator iter; - for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ ) { - if((*iter).get()==pvListener.get()) { - pvListenerList.erase(iter); - return; - } - } -} - -void PVRecordField::postPut() -{ - callListener(); - std::list::iterator iter; - PVRecordStructurePtr pvParent = getParent(); - while(pvParent.get()!=NULL) { - std::list list = pvParent->pvListenerList; - for (iter = list.begin(); iter!=list.end(); iter++ ) { - (*iter)->dataPut(pvParent,getPtrSelf()); - } - pvParent = pvParent->getParent(); - } -} - -void PVRecordField::message(String const & message,MessageType messageType) -{ - pvRecord->message(getPtrSelf(),message,messageType); -} - -void PVRecordField::callListener() -{ - std::list::iterator iter; - for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ ) { - (*iter)->dataPut(getPtrSelf()); - } -} - -PVRecordStructure::PVRecordStructure( - PVStructurePtr const &pvStructure, - PVRecordStructurePtr const &parent, - PVRecordPtr const & pvRecord) -: - PVRecordField(pvStructure,parent,pvRecord), - pvStructure(pvStructure), - pvRecordFields(new PVRecordFieldPtrArray) -{ -} - -PVRecordStructure::~PVRecordStructure() {} - -void PVRecordStructure::init() -{ - PVRecordField::init(); - const PVFieldPtrArray & pvFields = pvStructure->getPVFields(); - size_t numFields = pvFields.size(); - pvRecordFields->reserve( numFields); - PVRecordStructurePtr self = - static_pointer_cast(getPtrSelf()); - PVRecordPtr pvRecord = getPVRecord(); - for(size_t i=0; igetField()->getType()==structure) { - PVStructurePtr xxx = static_pointer_cast(pvField); - PVRecordStructurePtr pvRecordStructure( - new PVRecordStructure(xxx,self,pvRecord)); - pvRecordFields->push_back(pvRecordStructure); - pvRecordStructure->init(); - } else { - PVRecordFieldPtr pvRecordField( - new PVRecordField(pvField,self,pvRecord)); - pvRecordFields->push_back(pvRecordField); - pvRecordField->init(); - } - } -} - -PVRecordFieldPtrArrayPtr PVRecordStructure::getPVRecordFields() -{ - return pvRecordFields; -} - -PVStructurePtr PVRecordStructure::getPVStructure() {return pvStructure;} - -void PVRecordStructure::removeListener(PVListenerPtr const & pvListener) -{ - PVRecordField::removeListener(pvListener); - size_t numFields = pvRecordFields->size(); - for(size_t i=0; iremoveListener(pvListener); - } -} - -void PVRecordStructure::postPut() -{ - PVRecordField::postPut(); - size_t numFields = pvRecordFields->size(); - for(size_t i=0; icallListener(); - } -} - -PVDatabase::PVDatabase() -{ -} - PVDatabase::~PVDatabase() {} PVDatabasePtr PVDatabase::getMaster() diff --git a/src/database/pvDatabase.h b/src/database/pvDatabase.h index e2bad0d..94a5d62 100644 --- a/src/database/pvDatabase.h +++ b/src/database/pvDatabase.h @@ -30,14 +30,17 @@ typedef std::tr1::shared_ptr PVRecordFieldPtrArrayPtr; class PVRecordStructure; typedef std::tr1::shared_ptr PVRecordStructurePtr; +class PVRecordClient; +typedef std::tr1::shared_ptr PVRecordClientPtr; + class PVListener; typedef std::tr1::shared_ptr PVListenerPtr; class RecordProcessRequester; typedef std::tr1::shared_ptr RecordProcessRequesterPtr; -class PVRecordClient; -typedef std::tr1::shared_ptr PVRecordClientPtr; +class RecordPutRequester; +typedef std::tr1::shared_ptr RecordPutRequesterPtr; class PVDatabase; typedef std::tr1::shared_ptr PVDatabasePtr; @@ -57,26 +60,35 @@ public: RecordProcessRequesterPtr const &recordProcessRequester, bool alreadyLocked) = 0; virtual bool isSynchronous() = 0; + virtual bool requestImmediatePut( + epics::pvData::PVFieldPtr const &pvField); + virtual void immediatePutDone(); + virtual void destroy(); epics::pvData::String getRecordName(); PVRecordStructurePtr getPVRecordStructure(); PVRecordFieldPtr findPVRecordField( epics::pvData::PVFieldPtr const & pvField); + bool addRequester(epics::pvData::RequesterPtr const & requester); + bool removeRequester(epics::pvData::RequesterPtr const & requester); void lock(); void unlock(); bool tryLock(); + void lockOtherRecord(PVRecordPtr const & otherRecord); bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient); bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient); void detachClients(); - void beginGroupPut(); - void endGroupPut(); bool addListener(PVListenerPtr const & pvListener); bool removeListener(PVListenerPtr const & pvListener); + void beginGroupPut(); + void endGroupPut(); void queueProcessRequest( RecordProcessRequesterPtr const &recordProcessRequester); void dequeueProcessRequest( RecordProcessRequesterPtr const &recordProcessRequester); - bool addRequester(epics::pvData::RequesterPtr const & requester); - bool removeRequester(epics::pvData::RequesterPtr const & requester); + void queuePutRequest( + RecordPutRequesterPtr const &recordPutRequester); + void putDone( + RecordPutRequesterPtr const &recordPutRequester); virtual epics::pvData::String getRequesterName() {return getRecordName();} virtual void message( epics::pvData::String const & message, @@ -103,12 +115,16 @@ private: epics::pvData::ConvertPtr convert; PVRecordStructurePtr pvRecordStructure; std::deque processRequesterQueue; + std::deque putRequesterQueue; std::list pvListenerList; std::list pvRecordClientList; std::list requesterList; epics::pvData::Mutex mutex; epics::pvData::Lock thelock; int depthGroupPut; + bool processActive; + bool putActive; + bool isDestroyed; }; class PVRecordField : @@ -172,7 +188,16 @@ private: friend class PVRecord; }; -class PVListener { +class PVRecordClient { +public: + POINTER_DEFINITIONS(PVRecordClient); + virtual ~PVRecordClient() {} + virtual void detach(PVRecordPtr const & pvRecord) = 0; +}; + +class PVListener : + virtual public PVRecordClient +{ public: POINTER_DEFINITIONS(PVListener); virtual ~PVListener() {} @@ -182,25 +207,27 @@ public: PVRecordFieldPtr const & pvRecordField) = 0; virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0; virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0; - virtual void unlisten(PVRecordPtr const & pvRecord) = 0; }; 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; }; -class PVRecordClient { +class RecordPutRequester +{ public: - POINTER_DEFINITIONS(PVRecordClient); - virtual ~PVRecordClient() {} - virtual void detach(PVRecordPtr const & pvRecord) = 0; + POINTER_DEFINITIONS(RecordPutRequester); + virtual ~RecordPutRequester() {} + virtual void requestResult(bool result) = 0; }; class PVDatabase : virtual public epics::pvData::Requester { diff --git a/src/database/pvRecord.cpp b/src/database/pvRecord.cpp new file mode 100644 index 0000000..dbd1899 --- /dev/null +++ b/src/database/pvRecord.cpp @@ -0,0 +1,634 @@ +/* pvRecord.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 + * @data 2012.11.21 + */ + +#include + +using std::tr1::static_pointer_cast; +using namespace epics::pvData; +using namespace epics::pvAccess; + +namespace epics { namespace pvDatabase { + +PVRecord::PVRecord( + String const & recordName, + PVStructurePtr const & pvStructure) +: recordName(recordName), + pvStructure(pvStructure), + convert(getConvert()), + thelock(mutex), + depthGroupPut(0), + processActive(false), + putActive(false), + isDestroyed(false) +{ + thelock.unlock(); +} + +void PVRecord::init() +{ + PVRecordStructurePtr parent; + pvRecordStructure = PVRecordStructurePtr( + new PVRecordStructure(pvStructure,parent,getPtrSelf())); + pvRecordStructure->init(); + pvStructure->setRequester(getPtrSelf()); +} + +PVRecord::~PVRecord() {} + +bool PVRecord::requestImmediatePut(PVFieldPtr const &pvField) +{ + return false; +} + +void PVRecord::immediatePutDone() {} + +void PVRecord::destroy() +{ + lock(); + isDestroyed = true; + + std::list::iterator requesterIter; + for ( + requesterIter = requesterList.begin(); + requesterIter!=requesterList.end(); + requesterIter++ ) { + (*requesterIter)->message("record destroyed",fatalErrorMessage); + } + requesterList.clear(); + + std::list::iterator clientIter; + for (clientIter = pvRecordClientList.begin(); + clientIter!=pvRecordClientList.end(); + clientIter++ ) + { + (*clientIter)->detach(getPtrSelf()); + } + pvRecordClientList.clear(); + + pvListenerList.clear(); + + std::deque::iterator processRequesterIter; + for ( + processRequesterIter = processRequesterQueue.begin(); + processRequesterIter != processRequesterQueue.end(); + processRequesterIter++ ) + { + (*processRequesterIter)->recordDestroyed(); + } + processRequesterQueue.clear(); + + std::deque::iterator putRequesterIter; + for ( + putRequesterIter = putRequesterQueue.begin(); + putRequesterIter != putRequesterQueue.end(); + putRequesterIter++ ) + { + (*putRequesterIter)->requestResult(false); + } + putRequesterQueue.clear(); + + + unlock(); +} + +String PVRecord::getRecordName() {return recordName;} + +PVRecordStructurePtr PVRecord::getPVRecordStructure() {return pvRecordStructure;} + +PVRecordFieldPtr PVRecord::findPVRecordField(PVFieldPtr const & pvField) +{ + return findPVRecordField(pvRecordStructure,pvField); +} + +bool PVRecord::addRequester(epics::pvData::RequesterPtr const & requester) +{ + lock(); + if(isDestroyed) { + unlock(); + return false; + } + std::list::iterator iter; + for (iter = requesterList.begin(); iter!=requesterList.end(); iter++ ) { + if((*iter).get()==requester.get()) { + unlock(); + return false; + } + } + requesterList.push_back(requester); + unlock(); + return true; +} + +bool PVRecord::removeRequester(epics::pvData::RequesterPtr const & requester) +{ + lock(); + if(isDestroyed) { + unlock(); + return false; + } + std::list::iterator iter; + for (iter = requesterList.begin(); iter!=requesterList.end(); iter++ ) { + if((*iter).get()==requester.get()) { + requesterList.erase(iter); + unlock(); + return true; + } + } + unlock(); + return false; +} + +PVRecordFieldPtr PVRecord::findPVRecordField( + PVRecordStructurePtr const & pvrs, + PVFieldPtr const & pvField) +{ + size_t desiredOffset = pvField->getFieldOffset(); + PVFieldPtr pvf = pvrs->getPVField(); + size_t offset = pvf->getFieldOffset(); + if(offset==desiredOffset) return pvrs; + PVRecordFieldPtrArrayPtr pvrfpap = pvrs->getPVRecordFields(); + PVRecordFieldPtrArray::iterator iter; + for (iter = pvrfpap.get()->begin(); iter!=pvrfpap.get()->end(); iter++ ) { + PVRecordFieldPtr pvrf = *iter; + pvf = pvrf->getPVField(); + offset = pvf->getFieldOffset(); + if(offset==desiredOffset) return pvrf; + size_t nextOffset = pvf->getNextFieldOffset(); + if(nextOffset<=desiredOffset) continue; + return findPVRecordField( + static_pointer_cast(pvrf), + pvField); + } + throw std::logic_error( + recordName + " pvField " + + pvField->getFieldName() + " not in PVRecord"); +} + +void PVRecord::lock() {thelock.lock();} + +void PVRecord::unlock() {thelock.unlock();} + +bool PVRecord::tryLock() {return thelock.tryLock();} + +void PVRecord::lockOtherRecord(PVRecordPtr const & otherRecord) +{ + if(thislock(); + return; + } + unlock(); + otherRecord->lock(); + lock(); +} + +bool PVRecord::addPVRecordClient(PVRecordClientPtr const & pvRecordClient) +{ + lock(); + if(isDestroyed) { + unlock(); + return false; + } + std::list::iterator iter; + for (iter = pvRecordClientList.begin(); + iter!=pvRecordClientList.end(); + iter++ ) + { + if((*iter).get()==pvRecordClient.get()) { + unlock(); + return false; + } + } + pvRecordClientList.push_back(pvRecordClient); + unlock(); + return true; +} + +bool PVRecord::removePVRecordClient(PVRecordClientPtr const & pvRecordClient) +{ + lock(); + std::list::iterator iter; + for (iter = pvRecordClientList.begin(); + iter!=pvRecordClientList.end(); + iter++ ) + { + if((*iter).get()==pvRecordClient.get()) { + pvRecordClientList.erase(iter); + unlock(); + return true; + } + } + unlock(); + return false; +} + +void PVRecord::detachClients() +{ + lock(); + if(isDestroyed) { + unlock(); + return; + } + std::list::iterator iter; + for (iter = pvRecordClientList.begin(); + iter!=pvRecordClientList.end(); + iter++ ) + { + (*iter)->detach(getPtrSelf()); + } + pvRecordClientList.clear(); + unlock(); +} + +bool PVRecord::addListener(PVListenerPtr const & pvListener) +{ + lock(); + if(isDestroyed) { + unlock(); + return false; + } + std::list::iterator iter; + for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ ) + { + if((*iter).get()==pvListener.get()) { + unlock(); + return false; + } + } + pvListenerList.push_back(pvListener); + unlock(); + return true; +} + +bool PVRecord::removeListener(PVListenerPtr const & pvListener) +{ + lock(); + if(isDestroyed) { + unlock(); + return false; + } + std::list::iterator iter; + for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ ) + { + if((*iter).get()==pvListener.get()) { + pvListenerList.erase(iter); + pvRecordStructure->removeListener(pvListener); + unlock(); + return true; + } + } + unlock(); + return false; +} + +void PVRecord::beginGroupPut() +{ + if(++depthGroupPut>1) return; + std::list::iterator iter; + for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++) + { + (*iter).get()->beginGroupPut(getPtrSelf()); + } +} + +void PVRecord::endGroupPut() +{ + if(--depthGroupPut>0) return; + std::list::iterator iter; + for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++) + { + (*iter).get()->endGroupPut(getPtrSelf()); + } +} + +void PVRecord::queueProcessRequest( + RecordProcessRequesterPtr const &recordProcessRequester) +{ + lock(); + if(isDestroyed) { + unlock(); + recordProcessRequester->recordDestroyed(); + return; + } + bool isFirst = false; + processRequesterQueue.push_back(recordProcessRequester); + if(processRequesterQueue.size()==1 && !putActive) isFirst = true; + unlock(); + if(isFirst) recordProcessRequester->becomeProcessor(); +} + +void PVRecord::dequeueProcessRequest( + RecordProcessRequesterPtr const &recordProcessRequester) +{ + lock(); + if(isDestroyed) { + unlock(); + recordProcessRequester->recordDestroyed(); + return; + } + RecordProcessRequesterPtr next; + RecordProcessRequesterPtr processRequester = + processRequesterQueue[0]; + if(processRequester.get()!=recordProcessRequester.get()) { + message( + "PVRecord::dequeueProcessRequest illegal requester", + errorMessage); + return; + } + processRequesterQueue.pop_front(); + if(putRequesterQueue.size() > 0) { + RecordPutRequesterPtr putRequester = putRequesterQueue[0]; + processActive = false; + putActive = true; + putRequester->requestResult(true); + return; // WITH LOCK HELD + } + if(processRequesterQueue.size()>0) next = processRequesterQueue[0]; + if(next.get()!=NULL) processActive = true; + unlock(); + if(next.get()!=NULL) next->becomeProcessor(); +} + + +void PVRecord::queuePutRequest( + RecordPutRequesterPtr const &recordPutRequester) +{ + lock(); + if(isDestroyed) { + unlock(); + recordPutRequester->requestResult(false); + return; + } + if(processRequesterQueue.size()==0 && !processActive) { + putRequesterQueue.push_back(recordPutRequester); + putActive = true; + recordPutRequester->requestResult(true); + return; // WITH lock held + } + putRequesterQueue.push_back(recordPutRequester); + unlock(); +} + +void PVRecord::putDone( + RecordPutRequesterPtr const &recordPutRequester) +{ + // Note that this is called with lock held + RecordPutRequesterPtr putRequester = + putRequesterQueue[0]; + if(putRequester.get()!=recordPutRequester.get()) { + message( + "PVRecord::putDone illegal requester", + errorMessage); + return; + } + putRequesterQueue.pop_front(); + if(putRequesterQueue.size()>0){ + RecordPutRequesterPtr next = putRequesterQueue[0]; + next->requestResult(true); + return; // WITH lock held + } + putActive = false; + RecordProcessRequesterPtr next = processRequesterQueue[0]; + if(next.get()!=NULL) processActive = true; + unlock(); + if(next.get()!=NULL) next->becomeProcessor(); +} + + +void PVRecord::message(String const & message,MessageType messageType) +{ + if(isDestroyed) return; + if(requesterList.size()==0 ) { + String xxx(getMessageTypeName(messageType) + " " + message); + printf("%s\n",xxx.c_str()); + return; + } + std::list::iterator iter; + for(iter = requesterList.begin(); iter != requesterList.end(); ++iter) { + (*iter)->message(message,messageType); + } +} + +void PVRecord::message( + PVRecordFieldPtr const & pvRecordField, + String const & message, + MessageType messageType) +{ + this->message(pvRecordField->getFullName() + " " + message,messageType); +} + +void PVRecord::toString(StringBuilder buf) +{ + toString(buf,0); +} + +void PVRecord::toString(StringBuilder buf,int indentLevel) +{ + *buf += "\nrecord " + recordName + " "; + pvRecordStructure->getPVStructure()->toString(buf, indentLevel); +} + +PVRecordField::PVRecordField( + PVFieldPtr const & pvField, + PVRecordStructurePtr const &parent, + PVRecordPtr const & pvRecord) +: pvField(pvField), + parent(parent), + pvRecord(pvRecord) +{ +} + +void PVRecordField::init() +{ + fullFieldName = pvField->getFieldName(); + PVRecordStructurePtr pvParent = parent; + while(pvParent.get()!= NULL) { + String parentName = pvParent->getPVField()->getFieldName(); + if(parentName.size()>0) { + fullFieldName = pvParent->getPVField()->getFieldName() + + '.' + fullFieldName; + } + pvParent = pvParent->getParent(); + } + if(fullFieldName.size()>0) { + fullName = pvRecord->getRecordName() + '.' + fullFieldName; + } else { + fullName = pvRecord->getRecordName(); + } + pvField->setPostHandler(getPtrSelf()); +} + +PVRecordField::~PVRecordField() {} + +PVRecordStructurePtr PVRecordField::getParent() {return parent;} + +PVFieldPtr PVRecordField::getPVField() {return pvField;} + +String PVRecordField::getFullFieldName() {return fullFieldName; } + +String PVRecordField::getFullName() {return fullName; } + +PVRecordPtr PVRecordField::getPVRecord() {return pvRecord;} + +bool PVRecordField::addListener(PVListenerPtr const & pvListener) +{ + std::list::iterator iter; + for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ ) { + if((*iter).get()==pvListener.get()) { + return false; + } + } + pvListenerList.push_back(pvListener); + return true; +} + +void PVRecordField::removeListener(PVListenerPtr const & pvListener) +{ + std::list::iterator iter; + for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ ) { + if((*iter).get()==pvListener.get()) { + pvListenerList.erase(iter); + return; + } + } +} + +void PVRecordField::postPut() +{ + callListener(); + std::list::iterator iter; + PVRecordStructurePtr pvParent = getParent(); + while(pvParent.get()!=NULL) { + std::list list = pvParent->pvListenerList; + for (iter = list.begin(); iter!=list.end(); iter++ ) { + (*iter)->dataPut(pvParent,getPtrSelf()); + } + pvParent = pvParent->getParent(); + } +} + +void PVRecordField::message(String const & message,MessageType messageType) +{ + pvRecord->message(getPtrSelf(),message,messageType); +} + +void PVRecordField::callListener() +{ + std::list::iterator iter; + for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ ) { + (*iter)->dataPut(getPtrSelf()); + } +} + +PVRecordStructure::PVRecordStructure( + PVStructurePtr const &pvStructure, + PVRecordStructurePtr const &parent, + PVRecordPtr const & pvRecord) +: + PVRecordField(pvStructure,parent,pvRecord), + pvStructure(pvStructure), + pvRecordFields(new PVRecordFieldPtrArray) +{ +} + +PVRecordStructure::~PVRecordStructure() {} + +void PVRecordStructure::init() +{ + PVRecordField::init(); + const PVFieldPtrArray & pvFields = pvStructure->getPVFields(); + size_t numFields = pvFields.size(); + pvRecordFields->reserve( numFields); + PVRecordStructurePtr self = + static_pointer_cast(getPtrSelf()); + PVRecordPtr pvRecord = getPVRecord(); + for(size_t i=0; igetField()->getType()==structure) { + PVStructurePtr xxx = static_pointer_cast(pvField); + PVRecordStructurePtr pvRecordStructure( + new PVRecordStructure(xxx,self,pvRecord)); + pvRecordFields->push_back(pvRecordStructure); + pvRecordStructure->init(); + } else { + PVRecordFieldPtr pvRecordField( + new PVRecordField(pvField,self,pvRecord)); + pvRecordFields->push_back(pvRecordField); + pvRecordField->init(); + } + } +} + +PVRecordFieldPtrArrayPtr PVRecordStructure::getPVRecordFields() +{ + return pvRecordFields; +} + +PVStructurePtr PVRecordStructure::getPVStructure() {return pvStructure;} + +void PVRecordStructure::removeListener(PVListenerPtr const & pvListener) +{ + PVRecordField::removeListener(pvListener); + size_t numFields = pvRecordFields->size(); + for(size_t i=0; iremoveListener(pvListener); + } +} + +void PVRecordStructure::postPut() +{ + PVRecordField::postPut(); + size_t numFields = pvRecordFields->size(); + for(size_t i=0; icallListener(); + } +} + +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) +{ +} + +}} diff --git a/test/record/testRecord.cpp b/test/record/testRecord.cpp index 794b8dc..5db2121 100644 --- a/test/record/testRecord.cpp +++ b/test/record/testRecord.cpp @@ -32,13 +32,17 @@ using std::tr1::static_pointer_cast; TestRecord::~TestRecord(){} -PVRecordPtr TestRecord::create(String const & recordName) +PVRecordPtr TestRecord::create( + String const & recordName) { String properties("alarm,timeStamp,display,control,valueAlarm"); PVStructurePtr pvStructure = getStandardPVField()->scalar(pvLong,properties); PVLongPtr pvValue = pvStructure->getLongField("value"); - PVRecordPtr pvRecord(new TestRecord(recordName,pvStructure,pvValue)); + TestRecordPtr pvRecord( + new TestRecord(recordName,pvStructure,pvValue)); pvRecord->init(); + PVFieldPtr pvField = pvStructure->getSubField("display.description"); + pvRecord->immediatePutOK = pvField; return pvRecord; } @@ -50,8 +54,6 @@ TestRecord::TestRecord( pvValue(pvValue) {} -bool TestRecord::isSynchronous() {return true;} - void TestRecord::process( RecordProcessRequesterPtr const &processRequester,bool alreadyLocked) { @@ -63,6 +65,26 @@ void TestRecord::process( dequeueProcessRequest(processRequester); } +bool TestRecord::isSynchronous() {return true;} + +void TestRecord::destroy() +{ + PVRecord::destroy(); + pvValue.reset(); + immediatePutOK.reset(); +} + +bool TestRecord::requestImmediatePut(PVFieldPtr const &pvField) +{ + if(pvField!=immediatePutOK) return false; + lock(); + return true; +} + +void TestRecord::immediatePutDone() +{ + unlock(); +} }} diff --git a/test/record/testRecord.h b/test/record/testRecord.h index 094e395..2d5446f 100644 --- a/test/record/testRecord.h +++ b/test/record/testRecord.h @@ -19,23 +19,30 @@ namespace epics { namespace pvDatabase { class TestRecord; +typedef std::tr1::shared_ptr TestRecordPtr; class TestRecord : public virtual PVRecord { public: POINTER_DEFINITIONS(TestRecord); - static PVRecordPtr create(epics::pvData::String const & recordName); + static PVRecordPtr create( + epics::pvData::String const & recordName); virtual ~TestRecord(); - virtual bool isSynchronous(); virtual void process( epics::pvDatabase::RecordProcessRequesterPtr const &processRequester, bool alreadyLocked); + virtual bool isSynchronous(); + virtual void destroy(); + virtual bool requestImmediatePut(epics::pvData::PVFieldPtr const &pvField); + virtual void immediatePutDone(); private: TestRecord(epics::pvData::String const & recordName, epics::pvData::PVStructurePtr const & pvStructure, epics::pvData::PVLongPtr const &pvValue); + epics::pvData::PVLongPtr pvValue; + epics::pvData::PVFieldPtr immediatePutOK; }; }} diff --git a/test/record/testRecordMain.cpp b/test/record/testRecordMain.cpp index 4483926..550b479 100644 --- a/test/record/testRecordMain.cpp +++ b/test/record/testRecordMain.cpp @@ -44,6 +44,9 @@ typedef std::tr1::shared_ptr MyPVListenerPtr; class MyProcessRequester; typedef std::tr1::shared_ptr MyProcessRequesterPtr; +class MyPutRequester; +typedef std::tr1::shared_ptr MyPutRequesterPtr; + class MyPVListener : public PVListener, public std::tr1::enable_shared_from_this @@ -61,6 +64,10 @@ public: return pvPVListener; } virtual ~MyPVListener() {} + virtual void detach(PVRecordPtr const & pvRecord) + { + printf("%s MyPVListener::detach\n",requesterName.c_str()); + } virtual void dataPut(PVRecordFieldPtr const & pvRecordField) { String fieldName = pvRecordField->getFullFieldName(); @@ -107,6 +114,7 @@ private: pvRecordField(pvRecordField) {} void init() { + pvRecord->addPVRecordClient(getPtrSelf()); pvRecord->addListener(getPtrSelf()); pvRecordField->addListener(getPtrSelf()); } @@ -120,7 +128,6 @@ private: class MyProcessRequester : public virtual Requester, public virtual RecordProcessRequester, - public PVRecordClient, public std::tr1::enable_shared_from_this { public: @@ -160,6 +167,11 @@ public: message.c_str(), messageTypeName.c_str()); } + virtual void recordDestroyed() + { + printf("%s MyProcessRequester::recordDestroyed\n", + requesterName.c_str()); + } virtual void becomeProcessor() { pvRecord->process(getPtrSelf(),false); @@ -198,6 +210,37 @@ private: PVRecordPtr pvRecord; }; +class MyPutRequester : + public virtual RecordPutRequester, + public std::tr1::enable_shared_from_this +{ +public: + MyPutRequester(PVRecordPtr const & pvRecord) + : pvRecord(pvRecord), + result(false) + {} + virtual ~MyPutRequester() {} + virtual void requestResult(bool result) + { + this->result = result; + event.signal(); + } + bool makeRequest() + { + pvRecord->queuePutRequest(getPtrSelf()); + event.wait(); + return result; + } +private: + MyPutRequesterPtr getPtrSelf() + { + return shared_from_this(); + } + Event event; + PVRecordPtr pvRecord; + bool result; +}; + void dumpPVRecordField(PVRecordFieldPtr pvRecordField) @@ -253,17 +296,22 @@ int main(int argc,char *argv[]) "listenTop", pvRecord, pvRecord->getPVRecordStructure()); MyPVListenerPtr listenValue = MyPVListener::create( "listenValue", pvRecord,recordFieldValue); - PVFieldPtr pvValueAlarm = pvStructure->getSubField("valueAlarm"); - PVRecordFieldPtr recordFieldValueAlarm = - pvRecord->findPVRecordField(pvValueAlarm); - MyPVListenerPtr listenValueAlarm = MyPVListener::create( - "listenValueAlarm", pvRecord,recordFieldValueAlarm); - PVIntPtr pvHighAlarmSeverity = - pvStructure->getIntField("valueAlarm.highAlarmSeverity"); - PVRecordFieldPtr recordFieldHighAlarmSeverity = - pvRecord->findPVRecordField(pvHighAlarmSeverity); - MyPVListenerPtr listenHighAlarmSeverity = MyPVListener::create( - "listenHighAlarmSeverity", pvRecord,recordFieldHighAlarmSeverity); + + PVFieldPtr pvDisplay = pvStructure->getSubField("display"); + PVRecordFieldPtr recordFieldDisplay = + pvRecord->findPVRecordField(pvDisplay); + MyPVListenerPtr listenDisplay = MyPVListener::create( + "listenDisplay", pvRecord,recordFieldDisplay); + + PVStringPtr pvDisplayDescription = + pvStructure->getStringField("display.description"); + PVRecordFieldPtr recordFieldDisplayDescription = + pvRecord->findPVRecordField(pvDisplayDescription); + MyPVListenerPtr listenDisplayDescription = MyPVListener::create( + "listenDisplayDescription", pvRecord,recordFieldDisplayDescription); + + recordFieldDisplayDescription->message("test message",infoMessage); + MyProcessRequesterPtr process1 = MyProcessRequester::create("process1",pvRecord); MyProcessRequesterPtr process2 = @@ -276,8 +324,42 @@ int main(int argc,char *argv[]) builder.clear(); pvValue->toString(&builder); printf("%s\n",builder.c_str()); - pvHighAlarmSeverity->put(3); - recordFieldHighAlarmSeverity->message("test message",infoMessage); + bool requestResult; + requestResult = pvRecord->requestImmediatePut(pvValue); + if(requestResult) { + printf("error requestImmediatePut for pvValue returned true"); + pvRecord->immediatePutDone(); + } + requestResult = pvRecord->requestImmediatePut(pvDisplayDescription); + if(!requestResult) { + printf("error requestImmediatePut for pvDisplayDescription returned false"); + } else { + pvDisplayDescription->put("this is description"); + pvRecord->immediatePutDone(); + } + + MyPutRequesterPtr myPut(new MyPutRequester(pvRecord)); + requestResult = myPut->makeRequest(); + if(!requestResult) { + printf("error myPut->makeRequest() returned false"); + } else { + pvDisplayDescription->put("this is new description"); + pvValue->put(1000); + PVIntPtr pvSeverity = pvStructure->getIntField("alarm.severity"); + pvSeverity->put(3); + PVIntPtr pvStatus = pvStructure->getIntField("alarm.status"); + pvStatus->put(2); + PVStringPtr pvMessage = pvStructure->getStringField("alarm.message"); + pvMessage->put("alarmMessage"); + pvRecord->putDone(myPut); + } + + builder.clear(); + pvStructure->toString(&builder); + printf("pvStructure\n%s\n",builder.c_str()); + builder.clear(); + + pvRecord->destroy(); printf("all done\n"); #ifdef XXXXXX PVDatabasePtr pvDatabase = PVDatabase::getMaster();