diff --git a/arrayPerformance/src/Makefile b/arrayPerformance/src/Makefile index a9cef9a..362d0c6 100644 --- a/arrayPerformance/src/Makefile +++ b/arrayPerformance/src/Makefile @@ -2,8 +2,8 @@ TOP = .. include $(TOP)/configure/CONFIG LIBRARY_IOC += pvDatabaseExample +pvDatabaseExample_LIBS += pvDatabase pvAccess pvData Com pvDatabaseExample_LIBS += $(EPICS_BASE_IOC_LIBS) -pvDatabaseExample_LIBS += Com pvData pvAccess pvDatabase INC+= arrayPerformance.h diff --git a/configure/ExampleRELEASE.local b/configure/ExampleRELEASE.local new file mode 100644 index 0000000..7e5e5d7 --- /dev/null +++ b/configure/ExampleRELEASE.local @@ -0,0 +1,8 @@ + +TEMPLATE_TOP=$(EPICS_BASE)/templates/makeBaseApp/top + +EPICS_BASE=/home/install/epics/base +PVCOMMON=/home/hg/pvCommonCPP +PVDATA=/home/hg/pvDataCPP +PVACCESS=/home/hg/pvAccessCPP +PVASRV=/home/hg/pvaSrv diff --git a/documentation/pvDatabaseCPP.html b/documentation/pvDatabaseCPP.html index 9b20656..62fd254 100644 --- a/documentation/pvDatabaseCPP.html +++ b/documentation/pvDatabaseCPP.html @@ -38,7 +38,7 @@

pvDatabaseCPP

-

EPICS v4 Working Group, Working Draft, 07-Feb-2014

+

EPICS v4 Working Group, Working Draft, 19-Feb-2014

Latest version:
This version:
pvDatabaseCPP20140207.html + href= "pvDatabaseCPP_20140219.html">pvDatabaseCPP20140219.html
Previous version:
pvDatabaseCPP20131121.html + href= "pvDatabaseCPP_20140207.html">pvDatabaseCPP20140207.html
Editors:
Marty Kraimer, BNL
@@ -79,7 +79,7 @@ V4 control system programming environment:

Status of this Document

-

This is the 07-Feb-2014 version of of pvDatabaseCPP.

+

This is the 19-Feb-2014 version of of pvDatabaseCPP.

@@ -399,9 +399,11 @@ mrk> pwd /home/hg/pvDatabaseCPP mrk> make -

This builds pvDatabaseCPP and also the tests.

-

Each example and arrayPerformance is build as a completely separate top. -This is done so that:

+

This builds pvDatabaseCPP and also the tests and all examples.

+

Each example and arrayPerformance is a completely separate top, +but is also built when make is run in pvDatabaseCPP itself.

+

+Each is a separate top for the following reasons:

  1. It is easier to understand each example including how it is built so that @@ -412,8 +414,9 @@ This is done so that:

    for creating a new service.

-

Thus to build each example just follow the same instructions as for -building pvDatabaseCPP itself. +

+If it is desired to build an example all by itself, +just follow the same instructions as for building pvDatabaseCPP itself. For example:

 mrk> pwd
@@ -422,7 +425,7 @@ mrk> cp ExampleRELEASE.local RELEASE.local
 

Then edit RELEASE.local so that it has the correct location of each product the example requires. -Than at the top level just execute make:

+Than at the top level of the example just execute make:

 mrk> cd ..
 mrk> pwd
diff --git a/documentation/pvDatabaseCPP_20140219.html b/documentation/pvDatabaseCPP_20140219.html
new file mode 100644
index 0000000..62fd254
--- /dev/null
+++ b/documentation/pvDatabaseCPP_20140219.html
@@ -0,0 +1,2043 @@
+
+
+
+
+  
+  pvDatabaseCPP
+  
+  
+  
+  
+  
+
+
+
+
+
+

pvDatabaseCPP

+ + +

EPICS v4 Working Group, Working Draft, 19-Feb-2014

+
+
Latest version:
+
pvDatabaseCPP.html +
+
This version:
+
pvDatabaseCPP20140219.html +
+
Previous version:
+
pvDatabaseCPP20140207.html +
+
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 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. +

+ +

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

+ + +

Status of this Document

+ +

This is the 19-Feb-2014 version of of pvDatabaseCPP.

+

+ +

+Since the last version of the documentation: +

+
examples
+
The examples have been moved to separate top level build areas.
+
test
+
The regression tests have been moved to a separate top level build area. + It is built from the top but nothing from the tests appears in the top + level bin directory. +
exampleServer
+
This example now also includes pvaSrv, i. e. the pvAccess server for + interfacing to iocCore V3 records. +
+
+

+This project is ready for alpha users. +

+ +

+I have not had time to look at +two unresolved problems reported in the previous version of this document: +

+
memory leak
+
arrayPerformanceMain shows a slight memory leak at termination.
+
channel destroy and recreate
+
longArrayGet and longArrayPut fail if the channel is destroyed and + immediately recreated. + If epicsThreadSleep(1.0) is called between destroy and recreate then they work. + The current version of each does wait. +
+
+

+

Future enhancements in priority order:

+
+
channelArray
+
The arguments that have type int should be changed to size_t. + This requires changes to pvAccessCPP. +
+
Monitor Algorithms
+
Monitor algorithms have not been implemented. + Thus all monitors are onPut.
+
Create more regression tests
+
Currently only some simple tests exist. + Most of the testing has been via the examples.
+
+ +
+

Table of Contents

+
+
+ + +

Introduction

+

Overview

+

The main purpose of this project to make it easier to implement services that are accessed via pvAccess. +This project supplies is a complete implementation of the server side of pvAccess. +All that a service has to provide is a top level PVStructure and a process method. +A service can be run as a main process or can be part of a V3 IOC. +Thus services can be developed that interact with V3 records, asynDriver, +areaDetector, etc. +

+

A brief description of a pvDatabase is that it is a set of network accessible, 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. +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, which is accessed via a method named process.

+ +

This document describes components that provide 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.
+
+
pvAccess
+
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 provides a C++ implementation + of the monitor and pvCopy components from pvIOCJava
+
Main and V3IOC
+
The pvDatabase can be provided via a Main program or can be part + of a V3IOC. In the later case the IOC has both a database of V3 Records + and a pvDatabase.
+
+

Base classes make it easy to create record instances. +The code attached to each record must create the top +level PVStructure and the following three methods:

+
+
init
+
This is a method for initializing the support. + It returns true if successful and false otherwise. +
+
process
+
This is what makes a record smart. +
+
destroy
+
This releases and resources used by the implementation.
+
+

Getting started

+

The first step is to build pvDatabaseCPP as described in the next section.

+

One of the examples is exampleServer. +It can be started either via a main program or as part of a V3 IOC. +

+

To start it as a main program do the following:

+
+mrk> pwd
+/home/hg/pvDatabaseCPP/exampleServer
+mrk> bin/linux-x86_64/exampleServerMain 
+
+

You should see something like the following:

+
+result of addRecord exampleServer 1
+VERSION : pvAccess Server v3.0.5-SNAPSHOT
+PROVIDER_NAMES : local
+BEACON_ADDR_LIST : 
+AUTO_BEACON_ADDR_LIST : 1
+BEACON_PERIOD : 15
+BROADCAST_PORT : 5076
+SERVER_PORT : 5075
+RCV_BUFFER_SIZE : 16384
+IGNORE_ADDR_LIST: 
+STATE : INITIALIZED
+exampleServer
+Type exit to stop: 
+
+

Then in another window execute a pvput and pvget as follows:

+
+mrk> pvput -r "field(argument.value)" exampleServer World
+...
+mrk> pvget -r "record[process=true]field(result.value)" exampleServer
+exampleServer
+structure 
+    string value Hello World
+mrk> 
+
+

To run the example as part of a V3 IOC do the following:

+
+mrk> pwd
+/home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
+mrk> ../../bin/linux-x86_64/exampleServer st.cmd 
+
+

You will see the following:

+
+> envPaths
+epicsEnvSet("ARCH","linux-x86_64")
+epicsEnvSet("IOC","exampleServer")
+epicsEnvSet("TOP","/home/hg/pvDatabaseCPP/exampleServer")
+epicsEnvSet("EPICS_BASE","/home/install/epics/base")
+epicsEnvSet("EPICSV4HOME","/home/hg")
+cd /home/hg/pvDatabaseCPP/exampleServer
+## Register all support components
+dbLoadDatabase("dbd/exampleServer.dbd")
+exampleServer_registerRecordDeviceDriver(pdbbase)
+## Load record instances
+dbLoadRecords("db/dbScalar.db","name=pvdouble,type=ao")
+dbLoadRecords("db/dbArray.db","name=pvdoubleArray,type=DOUBLE")
+dbLoadRecords("db/dbStringArray.db","name=pvstringArray")
+dbLoadRecords("db/dbEnum.db","name=pvenum")
+dbLoadRecords("db/dbCounter.db","name=pvcounter");
+cd /home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
+iocInit()
+Starting iocInit
+############################################################################
+## EPICS R3.14.12.3 $Date: Mon 2012-12-17 14:11:47 -0600$
+## EPICS Base built Dec 21 2013
+############################################################################
+iocRun: All initialization complete
+dbl
+pvdouble
+pvcounter
+pvenum
+pvdoubleArray
+pvstringArray
+epicsThreadSleep(1.0)
+exampleServerCreateRecord pvaServer
+startPVAServer
+VERSION : pvAccess Server v3.0.5-SNAPSHOT
+PROVIDER_NAMES : dbPv local
+BEACON_ADDR_LIST : 
+AUTO_BEACON_ADDR_LIST : 1
+BEACON_PERIOD : 15
+BROADCAST_PORT : 5076
+SERVER_PORT : 5075
+RCV_BUFFER_SIZE : 16384
+IGNORE_ADDR_LIST: 
+STATE : INITIALIZED
+pvdbl
+pvaServer
+epics> 
+
+

Just like previously you can then execute a pvput and pvget and see Hello World. +

+

The examples, i. e. exampleServer, exampleLink, examplePowerSupply, +and exampleDatabase, are described in separate sections below. +In addition arrayPerformance can be used to measure that performance of big +arrays. It is also described in a later section.

+

Reading section exampleServer and looking at it's code is a good way +to learn how to implement a service.

+ +

Relationship with pvIOCJava.

+

This document descibes a C++ implementation of some of the components in pvIOCJava, +which also implements a pvDatabase. +PVDatabaseCPP implements 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.

+

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.

+

+

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
+
Complete implementation of ChannelProvider and Channel. + This means that pvCopy and monitor are also implemented.
+
+

Future phases of pvDatabaseCPP might include:

+
+
Install
+
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. +
+
Field support
+
Add the ability to optionally add support to fields. + In addition some of the basic support defined in pvIOCJava could 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.
+
The localChannelProvider itself
+
This is the C++ implementation of package pvAccess in pvIOCJava. + The localChannelProvider accesses data from PVRecords. + It implements all channel methods except channelRPC, which is implemented by pvAccessCPP.
+
+

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 implementing many 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, provides the following: +
+
Access to top level 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 describes the classes required for the first phase.

+ +

Building pvDatabaseCPP

+

To build pvDatabaseCPP You must provide a file RELEASE.local +in directory configure. +Thus do the following:

+
+mrk> pwd
+/home/hg/pvDatabaseCPP/configure
+mrk> cp ExampleRELEASE.local RELEASE.local
+
+

Then edit RELEASE.local so that it has the correct location of each +product pvDatabaseCPP requires. +Than at the top level just execute make:

+
+mrk> cd ..
+mrk> pwd
+/home/hg/pvDatabaseCPP
+mrk> make
+
+

This builds pvDatabaseCPP and also the tests and all examples.

+

Each example and arrayPerformance is a completely separate top, +but is also built when make is run in pvDatabaseCPP itself.

+

+Each is a separate top for the following reasons:

+
    +
  1. + It is easier to understand each example including how it is built so that + it can be run as a main or as part of a V3 IOC. +
  2. +
  3. + Each example can be copied somewhere else and used as the basis + for creating a new service. +
  4. +
+

+If it is desired to build an example all by itself, +just follow the same instructions as for building pvDatabaseCPP itself. +For example:

+
+mrk> pwd
+/home/hg/pvDatabaseCPP/exampleServer/configure
+mrk> cp ExampleRELEASE.local RELEASE.local
+
+

Then edit RELEASE.local so that it has the correct location of each +product the example requires. +Than at the top level of the example just execute make:

+
+mrk> cd ..
+mrk> pwd
+/home/hg/pvDatabaseCPP/exampleServer
+mrk> make
+
+

This builds the example.

+ +

iocshell commands

+

The following iocsh commands are provided for a V3IOC:

+
+
startPVAClient
+
Starts the client side of pvAccess.s + It makes channel provider pvAccess available. + After startPVAServer is called the channel provider local will + also be available. +
+
stopPVAClient
+
Stops pvAccess.
+
startPVAServer
+
Starts the local channel provider

+
stopPVAServer
+
Stop the local channel provider
+
pvdbl
+
Provides a list of all the pvRecords in database master +
+
+

The client commands are provided via PVAClientRegister.dbd and the other commands +via PVAServerRegister.dbd.

+

In addition any code that implements a PVRecord must implement an ioc command. +The directory example has examples of how to implement the registration code. +See example/V3IOC/exampleCounter/src/ for a simple example.

+ + +

database

+

src/database

+

This Directory has the following files:

+
+
pvDatabase.h
+
+ This is what is described in this section. +
+
pvDatabase.cpp
+
+ The implementation of PVDatabase. +
+
pvRecord.cpp
+
+ The implementation of the base class for PVRecord. + It can also implement record instances with a process + method does nothing. + This can be used to create a "dumb" record where all changes are + done by clients. +
+
+

src/special

+

This directory has the following files:

+
+
recordList.h
+
This implements a PVRecord that provides a list of the names + of the records in the PVDatabase. + It also serves as an example of how to implement a service. + The exampleDatabase creates an instance via the following code: +
+recordName = "laptoprecordListPGRPC";
+pvRecord = RecordListRecord::create(recordName);
+result = master->addRecord(pvRecord);
+      
+
+
traceRecord.h
+
This implements a PVRecord that can set the trace level for + another record. See below for a discussion of trace level.
+ +
+

pvDatabase.h

+

The classes in pvDatabase.h describe a database of memory resident +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.
+
PVRecordClient
+
This is called by anything that acceses PVRecord.
+
PVListener
+
This is implemented by anything that wants to trap calls to PVRecord::message.
+
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;
+typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
+
+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 RecordPutRequester;
+typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;
+
+class PVDatabase;
+typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
+
+ +

class PVRecord

+

NOTES: +

    +
  • This section uses the name record instead of "an instance of PVRecord".
  • +
  • Most clients will access a record via the local channel provider, + i. e. via pvAccess. + Thus this section is mainly of interest to + the local channel provider and record implementers.
  • +
  • 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 init and process. + These are described first. +
+
PVRecord Methods +
+class PVRecord
+     public epics::pvData::Requester,
+     public std::tr1::enable_shared_from_this<PVRecord>
+{
+public:
+    POINTER_DEFINITIONS(PVRecord);
+
+    virtual bool init() {initPVRecord(); return true;}
+    virtual void process() {}
+    virtual void destroy();
+
+    static PVRecordPtr create(
+        epics::pvData::String const & recordName,
+        epics::pvData::PVStructurePtr const & pvStructure);
+    virtual ~PVRecord();
+    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);
+    inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
+    void lock();
+    void unlock();
+    bool tryLock();
+    void lockOtherRecord(PVRecordPtr const & otherRecord);
+    bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
+    bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
+    void detachClients();
+    bool addListener(PVListenerPtr const & pvListener);
+    bool removeListener(PVListenerPtr const & pvListener);
+    void beginGroupPut();
+    void endGroupPut();
+    epics::pvData::String getRequesterName() {return getRecordName();}
+    virtual void message(
+        epics::pvData::String const & message,
+        epics::pvData::MessageType messageType);
+    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);
+    int getTraceLevel();
+    void setTraceLevel(int level);
+protected:
+    PVRecord(
+        epics::pvData::String const & recordName,
+        epics::pvData::PVStructurePtr const & pvStructure);
+    void initPVRecord();
+    epics::pvData::PVStructurePtr getPVStructure();
+    PVRecordPtr getPtrSelf()
+    {
+        return shared_from_this();
+    }
+private:
+...
+}
+
+

The methods are:

+
+
init
+
Virtual method. + Derived classes must implement this method. + This method Must call initPVRecord. +
+
process
+
Virtual method. + Derived classes must implement this method. + The base implementation does nothing. +
+
destroy
+
This is a virtual method. + A derived class must call the base class destroy method after it + has released any resources it uses.
+
create
+
Static method to create dumb records, + i.e. records with a process method that does nothing. + A derived class should have it';s own static create method. +
+
~PVRecord
+
The destructor which must be virtual. A derived class must also have + a virtual destructor.
+
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_guard
+
This is an inline method that locks the record. The record will automatically + be unlocked when control leaves the block that has the call. +
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.
+
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.
+
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
+
endGroupPut
+
End a group of puts. + This results in all registered PVListeners being called.
+
getRequesterName
+
virtual method of Requester +
+
message
+
Can be called by implementation code. + The message will be sent to every requester.
+
toString
+
Just calls the top level PVStructure toString method.
+
getTraceLevel
+
This can be used for debugging. There are currently three + levels that are used by existing code. +
+
0
+
Produce no trace messages.
+
1
+
Issue a message to std::cout whenever anything is created + or destroyed.
+
2
+
In addition to lifetime messages also issue a message + whenever the record is accessed by pvAccess client.
+
+
+
setTraceLevel
+
Set the trace level. Note that special, described below. + provides a record support that allows a pvAccess client + to set the trace level of a record.
+
+

The protected methods are:

+
+
PVRecord
+
The constructor. It requires a recordName and a top level PVStructure.
+
initPVRecord
+
This method must be called by derived class.
+
getPVStructure
+
Called by derived class.
+
+

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();
+    virtual void destroy();
+    PVRecordStructurePtr getParent();
+    epics::pvData::PVFieldPtr getPVField();
+    epics::pvData::String getFullFieldName();
+    epics::pvData::String getFullName();
+    PVRecordPtr getPVRecord();
+    bool addListener(PVListenerPtr const & pvListener);
+    virtual void removeListener(PVListenerPtr const & pvListener);
+    virtual void postPut();
+    virtual void message(
+        epics::pvData::String const & message,
+        epics::pvData::MessageType messageType);
+protected:
+    PVRecordFieldPtr getPtrSelf()
+    {
+        return shared_from_this();
+    }
+    virtual void init();
+    virtual void postParent(PVRecordFieldPtr const & subField);
+    virtual void postSubField();
+private:
+...
+};
+
+

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.
+
destroy
+
Called by PVRecordStructure when it's destroy method is called.
+
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();
+    virtual void destroy();
+    PVRecordFieldPtrArrayPtr getPVRecordFields();
+    epics::pvData::PVStructurePtr getPVStructure();
+    virtual void removeListener(PVListenerPtr const & pvListener);
+    virtual void postPut();
+protected:
+    virtual void init();
+private:
+...
+};
+
+

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.
+
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();
+    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);
+};
+
+

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 record is being destroyed. The listener must release all + access to the record.
+
+

class PVDatabase

+
+class PVDatabase : virtual public epics::pvData::Requester {
+public:
+    POINTER_DEFINITIONS(PVDatabase);
+    static PVDatabasePtr getMaster();
+    virtual ~PVDatabase();
+    virtual void destroy();
+    PVRecordPtr findRecord(epics::pvData::String const& recordName);
+    bool addRecord(PVRecordPtr const & record);
+    epics::pvData::PVStringArrayPtr getRecordNames();
+    bool removeRecord(PVRecordPtr const & record);
+    virtual epics::pvData::String getRequesterName();
+    virtual void message(
+        epics::pvData::String const &message,
+        epics::pvData::MessageType messageType);
+private:
+    PVDatabase();
+};
+
+

where

+
+
getMaster
+
Get the master database. This is the database that localChannelProvider access.
+
~PVDatabase
+
The destructor.
+
destroy
+
This is called by remote channelAccess when process exits. + This destroys and removes all records in the PVDatabase.
+
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.
+
getRecordNames
+
Returns an array of all the record names.
+
removeRecord
+
Remove a record from the database. + If the record was not in the database false is returned.
+
getRequesterName
+
Virtual method of Requester
+
message
+
Virtual message of Requester.
+
+

pvAccess

+

This is code that provides an implementation of channelProvider as +defined by pvAccess. +It provides access to PVRecords and is access by the server side of remote pvAccess.

+

channelProviderLocal

+

This is a complete implementation of channelProvider and , +except for channelRPC, provides a complete implementation of Channel +as defined by pvAccess. +For monitors it calls the code described in the following sections.

+

pvCopy

+

This provides code that creates a top level PVStructure that is an arbitrary +subset of the fields in the PVStructure from a PVRecord. +In addition it provides code that monitors changes to the fields in a PVRecord. +A client configures the desired set of subfields and monitoring options +via a pvRequest structure. +pvAccess provides a class CreatePVRequest that creates a pvRequest. +The pvCopy code provides the same functionality as the pvCopy code in pvIOCJava. +

+

monitorAlgorithm

+

Currently all that is implemented is a header file. +The only algorithm currently implemented is onPut +

+

monitorFactory

+

Overview

+

epics::pvData::monitor defines the monitor interfaces +as seen by a client. +See + pvDatabaseCPP.html + For details.

+

+monitorFactory implements the +monitoring interfaces for a PVRecord. +It implements queueSize=0 and queueSize>=2. +

+ +

+The implementation uses PVCopy and PVCopyMonitor which are implemented in pvCopy. +When PVCopyMonitor tells monitor that changes +have occurred, monitor applies the appropriate algorithm to each changed field.

+ +

Currently only algorithm onPut is implemented but, +like pvIOCJava there are plans to support for the following monitor algorithms:

+
+
onPut
+
A monitor is issued whenever a put is issued to the field. This is the + default unless the record defines deadbands for a field. An exception is + the top level timeStamp which by default is made onChange and monitor + will not be raised.
+
onChange
+
This provides two options: 1) A monitor is raised whenever a field + changes value, and 2) A monitor will never be raised for the field.
+
deadband
+
The field must be a numeric scalar. Whenever the absolute or percentage + value of the field changes by more than a deadband a monitor is issued. + The record instance can also define deadbands.
+
periodic
+
A monitor is issued at a periodic rate if a put was issued to any field + being monitored.
+
+

MonitorFactory

+

MonitorFactory provides the following methods:

+
class MonitorFactory
+{
+    static MonitorPtr create(
+        PVRecordPtr const & pvRecord,
+        MonitorRequester::shared_pointer const & monitorRequester,
+        PVStructurePtr const & pvRequest);
+    static void registerMonitorAlgorithmCreater(
+        MonitorAlgorithmCreatePtr const & monitorAlgorithmCreate,
+        String const & algorithmName);
+}
+ +

where

+
+
create
+
Create a monitor. The arguments are: +
+
pvRecord
+
The record being monitored.
+
monitorRequester
+
The monitor requester. This is the code to which monitot events + will be delivered.
+
pvRequest
+
The request options
+
+
+
registerMonitorAlgorithmCreater
+
Called by code that implements a monitor algorithm.
+
+

special

+

This section provides two useful record support modules +and one that is used for testing.

+

traceRecord

+

This implements a PVRecord that allows a client to set +the trace level of a record. It follows the pattern of a channelPutGet +record: +

+traceRecord
+    structure arguments
+        string recordName 
+        int level 0
+    structure result
+        string status 
+
+where: +
+
recordName
+
The name of the record to set the trace level.
+
level
+
The level to set. The meaning is: +
+
0
+
No trace messages generated
+
1
+
Lifecycle messages will be generated. + This all channel create and destroy instances will be shown.
+
2
+
In addition to lifecycle messages a message will be generted + for each get and put request.
+
>2
+
Currently no definition
+
+
+
result
+
The result of a cannelPutGet request
+
+

testExampleServerMain.cpp has an example of how to create a traceRecord: +

+PVDatabasePtr master = PVDatabase::getMaster();
+PVRecordPtr pvRecord;
+String recordName;
+bool result(false);
+recordName = "traceRecordPGRPC";
+pvRecord = TraceRecord::create(recordName);
+result = master->addRecord(pvRecord);
+if(!result) cout<< "record " << recordName << " not added" << endl;
+
+

+

recordList

+

This implements a PVRecord that allows a client to set +the trace level of a record. It follows the pattern of a channelPutGet +record: +

+traceRecord
+    structure arguments
+        string database master
+        string regularExpression .*
+    structure result
+        string status 
+        string[] names
+
+where: +
+
database
+
The name of the datbase. The default is "master"
+
regularExpression
+
For now this is ignored and the complete list of names is always + returned.
+
status
+
The status of a putGet request.
+
names
+
The list of record names.
+
+

Note that swtshell, which is a Java GUI tool, has a command channelList that +requires that a record of this type is present and calls it. +Thus user code does not have to use a channelGetPut to get the list +of record names.

+

testExampleServerMain.cpp has an example of how to create a traceRecord: +

+recordName = "laptoprecordListPGRPC";
+pvRecord = RecordListRecord::create(recordName);
+result = master->addRecord(pvRecord);
+if(!result) cout<< "record " << recordName << " not added" << endl;
+
+

+ +

exampleServer

+

Overview

+

The example implements a simple service that has a top level pvStructure: +

+structure
+    structure argument
+        string value
+    structure result
+        string value
+        time_t timeStamp
+            long secondsPastEpoch
+            int nanoSeconds
+            int userTag
+
+

It is designed to be accessed via a channelPutGet request. +The client sets argument.value +When the record processes it sets result.value to "Hello " +concatenated with argument.value. +Thus if the client sets argument.value equal to "World" +result.value will be "Hello World". +In addition the timeStamp is set to the time when process is called.

+

+The example can be run on linux as follows:

+
+mrk> pwd
+/home/hg/pvDatabaseCPP/exampleService
+mrk> bin/linux-x86_64/exampleService
+
+

Directory Layout

+

+The directory layout is: +

+
+exampleServer
+    configure
+       ExampleRELEASE.local
+       ...
+    src
+       exampleServer.h
+       exampleServer.cpp
+       exampleServerInclude.dbd
+       exampleServerMain.cpp
+       exampleServerRegister.cpp
+    ioc
+       Db
+          ...
+       src
+          exampleServerInclude.dbd
+          exampleServerMain.cpp
+   iocBoot
+      exampleServer
+         st.cmd
+         ...
+
+where +
+
ExampleRELEASE.local
+
This is the file that must be copied to RELEASE.local + and edited.
+
exampleServer.h
+
The header file for the service.
+
exampleServer.cpp
+
The service implementation.
+
exampleServerMain.cpp
+
A main program that runs the example so that it can be accessed + by a pvAccess client. +
+
exampleServerInclude.dbd
+
This has a register command so that the service can be started + on a V3 IOC via iocsh. +
+
exampleServerRegister.cpp
+
This has the code to start the service via the following iocsh + command. +
+exampleServerCreateRecord exampleServer
+
+ Multiple commands can be issued to create multiple service records. +
+
ioc
+
This is for building a V3 IOC application.
+
ioc/Db
+
This has template files for creating V3 records.
+
ioc/src
+
The files for running a V3 IOC.
+
iocBoot/exampleServer
+
A place to start exampleServer as part of a V3IOC. + It has a st.cmd file that starts the ioc and also starts pvAccess + and the example.
+
+

If only a main program is desired then the directory layout is:

+
+exampleServer
+    configure
+       ExampleRELEASE.local
+       ...
+    src
+       exampleServer.h
+       exampleServer.cpp
+       exampleServerMain.cpp
+
+

Thus if only a main program is required the directory layout is simple.

+

Also many sites will want to build the src directory in an area +separate from where the iocs are build.

+

exampleServer.h

+

The example resides in src +The implementation is in exampleServer.cpp. +

+

The description consists of

+
+class ExampleServer;
+typedef std::tr1::shared_ptr<ExampleServer> ExampleServerPtr;
+
+class ExampleServer :
+    public PVRecord
+{
+public:
+    POINTER_DEFINITIONS(ExampleServer);
+    static ExampleServerPtr create(
+        epics::pvData::String const & recordName);
+    virtual ~ExampleServer();
+    virtual void destroy();
+    virtual bool init();
+    virtual void process();
+private:
+    ExampleServer(epics::pvData::String const & recordName,
+        epics::pvData::PVStructurePtr const & pvStructure);
+
+    epics::pvData::PVStringPtr pvArgumentValue;
+    epics::pvData::PVStringPtr pvResultValue;
+    epics::pvData::PVTimeStamp pvTimeStamp;
+    epics::pvData::TimeStamp timeStamp;
+};
+
+

where

+
+
create
+
This is example specific but each support could provide + a similar static method. +
+
~ExampleServer
+
The destructor must be declared virtual.
+
destroy
+
Called when the record is being destroyed. + This must call the base class destroy method. +
init
+
A method to initialize the support. It returns true + if initialization is successful and false if not. + NOTE that this is a virtual method of PVRecord itself.
+
process
+
+ This again is a virtual method of PVRecord. +
+
ExampleServer
+
For the example this is private.
+
pvValue
+
This is the field of the top level structure that process + accesses. +
+
+

The implementation of create method is:

+
+ExampleServerPtr ExampleServer::create(
+    epics::pvData::String const & recordName)
+{
+    StandardPVFieldPtr standardPVField = getStandardPVField();
+    PVDataCreatePtr pvDataCreate = getPVDataCreate();
+    PVStructurePtr pvArgument = standardPVField->scalar(pvString,"");
+    PVStructurePtr pvResult = standardPVField->scalar(pvString,"timeStamp");
+    StringArray names;
+    names.reserve(2);
+    PVFieldPtrArray fields;
+    fields.reserve(2);
+    names.push_back("argument");
+    fields.push_back(pvArgument);
+    names.push_back("result");
+    fields.push_back(pvResult);
+    PVStructurePtr pvStructure = pvDataCreate->createPVStructure(names,fields);
+    ExampleServerPtr pvRecord(
+        new ExampleServer(recordName,pvStructure));
+    if(!pvRecord->init()) pvRecord.reset();
+    return pvRecord;
+}
+
+This: +
    +
  • Creates the top level structure.
  • +
  • Creates a ExampleServerPtr via the constructor.
  • +
  • Calls init and if it fails resets the shared pointer.
  • +
  • Returns the shared pointer to the newly created record.
  • +
+

The private constructor method is:

+
+ExampleServer::ExampleServer(
+    epics::pvData::String const & recordName,
+    epics::pvData::PVStructurePtr const & pvStructure)
+: PVRecord(recordName,pvStructure)
+{
+}
+
+The example is very simple. Note that it calls the base class constructor. +

The destructor and destroy methods are:

+
+ExampleServer::~ExampleServer()
+{
+}
+
+void ExampleServer::destroy()
+{
+    PVRecord::destroy();
+}
+
+The destructor has nothing to do. +The destroy method, which is virtual, just calls the destroy method of the base class. +A more complicated example can clean up any resources it used but must call the base +class destroy method. +

The implementation of init is:

+
+bool ExampleServer::init()
+{
+    initPVRecord();
+    PVFieldPtr pvField;
+    pvArgumentValue = getPVStructure()->getStringField("argument.value");
+    if(pvArgumentValue.get()==NULL) return false;
+    pvResultValue = getPVStructure()->getStringField("result.value");
+    if(pvResultValue.get()==NULL) return false;
+    pvTimeStamp.attach(getPVStructure()->getSubField("result.timeStamp"));
+    return true;
+}
+
+

The implementation of process is:

+
+void ExampleServer::process()
+{
+    pvResultValue->put(String("Hello ") + pvArgumentValue->get());
+    timeStamp.getCurrent();
+    pvTimeStamp.set(timeStamp);
+}
+
+It gives a value to result.value and +then sets the timeStamp to the current time. +

src/exampleServerMain.cpp

+

NOTE: +This is a shorter version of the actual code. +It shows the essential code. +The actual example shows how create an additional record. +

+

The main program is:

+
+int main(int argc,char *argv[])
+{
+    PVDatabasePtr master = PVDatabase::getMaster();
+    ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
+    String recordName("exampleServer");
+    PVRecordPtr pvRecord = ExampleServer::create(recordName);
+    bool result = master->addRecord(pvRecord);
+    cout << "result of addRecord " << recordName << " " << result << endl;
+    pvRecord.reset();
+    startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
+    cout << "exampleServer\n";
+    string str;
+    while(true) {
+        cout << "Type exit to stop: \n";
+        getline(cin,str);
+        if(str.compare("exit")==0) break;
+
+    }
+    return 0;
+}
+
+This: +
    +
  • Gets a pointer to the master database.
  • +
  • Creates the local Channel Provider. This starts the pvAccess server.
  • +
  • Creates a ExampleServer record with the name exampleServer +
  • +
  • Prints exampleServer on standard out.
  • +
  • Runs forever until the user types exit on standard in.
  • +
+

V3IOC exampleServer

+

To start exampleServer as part of a V3IOC: +

+mrk> pwd
+/home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
+mrk> ../../../bin/linux-x86_64/exampleServer st.cmd
+

+

You can then issue the commands dbl and pvdbl: +

+epics> dbl
+double01
+epics> pvdbl
+exampleServer
+epics> 
+
+double01 is a v3Record. +exampleServer is a pvRecord. +

+

+It starts pvASrv so that the V3 records can be accessed via Channel Access +or via PVAccess.

+ +

exampleDatabase

+

The exampleServer pvDatabase has many records including the following:

+
+
exampleDouble
+
A record that is an instance of a record with a process method + that does nothing. To test it start a channelPut and a channelGet and/or monitor.
+
exampleDoubleArray
+
An array record that is an instance of a record with a process method + that does nothing. It can be tested like exampleDouble. In addition channelArray can + also be used.
+
laptoprecordListPGRPC
+
Implements the record expected by swtshell channelList. + It can also be used via channelPutGet.
+
traceRecordPGRPC
+
This can be used via channelPutGet to set the trace level of another record.
+
+

It also has a number of other scalar and array records.

+

exampleDatabase can be started as a main program or as a V3 IOIC. +If started as a V3 IOC it also has a number of V3 records, +and starts pvaSrv so that the V3 records can be accessed via Channel Access +or via PVAccess.

+

exampleLink

+

This example show how a service can access other PVRecords. +This section 1) starts with a discussion of accessing data via pvAccess +and 2) gives a brief description of an example that gets data for an array of doubles.

+

Discussion

+

Access Alternatives

+

The process routine of a PVRecord can access other PVRecords in two ways:

+
+
Directly accessing local pvDatabase
+
+ If the other PVRecord is accessed via the master PVDatabase then + threading issues are up to the implementation. + For now this method will not be discussed.
+
Access via pvAccess
+
+ If access is via pvAccess then locking is handled by pvAccess.
+
+

Access via pvAccess can be done either by local or remote channel provider.

+
+
+
Access via channelProviderLocal
+
+ If the local pvAccess server is used the implementation must be careful that it does not + cause deadlocks. + When the process method is called the pvRecord for the process method is locked. + When it makes a pvAccess get, put, etc request the other record is locked. + Thus if a set of pvAccess links are implemented the possibility of deadlocks + exists. A simple example is two records that have links to each other. + More complex sets are easily created. + Unless the developer has complete control of the set of records then remote pvAccess should + be used. + But this results in more context switches. +
+
Access via remote pvAccess
+
If remote pvAccess is used then all locking issues are handled by pvAccess. + The linked channel can be a pvRecord in the local pvDatabase or can be implemented + by a remote pvAccess server.
+
+

Data synchronization

+

If pvAccess is used then it handles data synchronization. +This is done by making a copy of the data that is transfered between the two pvRecords. +This is true if either remote or local pvAccess is used. +Each get, put, etc request results in data being copied between the two records.

+

+If the linked channel is a local pvRecord then, +for scalar and structure arrays, +raw data is NOT copied for gets. +This is because pvData uses shared_vector to hold the raw data. +Instead of copying the raw data the reference count is incremented.

+

For puts the linked array will force a new allocation of the raw data in the linked record, +i. e. copy on write semantics are enforced. This is done automatically +by pvData and not by pvDatabase.

+

Some details

+

As mentioned before a pvDatabase server can be either a separate process, +i. e. a main program, or can be part of a V3IOC.

+

A main pvDatabase server issues the following calls:

+
+ ClientFactory::start();
+ ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
+ ...
+ ServerContext::shared_pointer serverContext = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
+
+

The first call is only necessary if some of the pvRecords +have pvAccess links. +These must be called before any code that uses links is initialized. +After these two calls there will be two channel providers: local, and pvAccess. + +

+

A pvDatabase that is part of a V3IOC has the following in the st.cmd file.

+
+...
+iocInit()
+startPVAClient
+startPVAServer
+## commands to create pvRecords
+
+

+Once the client and local provider code has started then the following creates a channel access link. +

+
+PVDatabasePtr master = PVDatabase::getMaster();
+ChannelAccess::shared_pointer channelAccess = getChannelAccess();
+ChannelProvider::shared_pointer provider = channelAccess->getProvider(providerName);
+Channel::shared_pointer channel = provider->createChannel(channelName,channelRequester);
+
+ +

Directory Layout

+
+exampleLink
+    configure
+       ExampleRELEASE.local
+       ...
+    src
+       exampleLink.h
+       exampleLink.cpp
+       exampleLinkInclude.dbd
+       exampleLinkRegister.cpp
+    ioc
+       Db
+       src
+          exampleLinkInclude.dbd
+          exampleLinkMain.cpp
+   iocBoot
+      exampleLink
+         st.local
+         st.remote
+         ...
+
+

This example is only built to be run as part of a V3 IOC. +Note that two startup files are available: st.local and st.remote. +st.local has two records: doubleArray and exampleLink. +doubleArray is a record that can be changed via a call to pvput. +exampleLink is a record that, when processed, gets the value from doubleArray and sets its value equal +to the value read. +st.local has both records. +st.remote has only one record named exampleLinkRemote. +

+

To start the example:

+
+mrk> pwd
+/home/hg/pvDatabaseCPP/exampleLink/iocBoot/exampleLink
+mrk> ../../bin/linux-x86_64/exampleLink st.local 
+
+

then in another window:

+
+mrk> pvput doubleArray 4 100 200 300 400
+Old : doubleArray 0
+New : doubleArray 4 100 200 300 400
+mrk> pvget -r "record[process=true]field(value)" exampleLink
+exampleLink
+structure 
+    double[] value [100,200,300,400]
+mrk> 
+
+

exampleLink Implementation

+

exampleLink.h contains the following:

+
+...
+class ExampleLink :
+    public PVRecord,
+    public epics::pvAccess::ChannelRequester,
+    public epics::pvAccess::ChannelGetRequester
+{
+public:
+    POINTER_DEFINITIONS(ExampleLink);
+    static ExampleLinkPtr create(
+        epics::pvData::String const & recordName,
+        epics::pvData::String const & providerName,
+        epics::pvData::String const & channelName
+        );
+    virtual ~ExampleLink() {}
+    virtual void destroy();
+    virtual bool init();
+    virtual void process();
+    virtual void channelCreated(
+        const epics::pvData::Status& status,
+        epics::pvAccess::Channel::shared_pointer const & channel);
+    virtual void channelStateChange(
+        epics::pvAccess::Channel::shared_pointer const & channel,
+        epics::pvAccess::Channel::ConnectionState connectionState);
+    virtual void channelGetConnect(
+        const epics::pvData::Status& status,
+        epics::pvAccess::ChannelGet::shared_pointer const & channelGet,
+        epics::pvData::PVStructure::shared_pointer const & pvStructure,
+        epics::pvData::BitSet::shared_pointer const & bitSet);
+    virtual void getDone(const epics::pvData::Status& status);
+private:
+...
+
+

All the non-static methods are either PVRecord, PVChannel, or PVChannelGet methods +and will not be discussed further. +The create method is called to create a new PVRecord instance with code that will issue +a ChannelGet::get request every time the process method of the instance is called. +Some other pvAccess client can issue a channelGet, to the record instance, with a request +to process in order to test the example.

+

All of the initialization is done by a combination of the create and init methods so +lets look at them:

+
+ExampleLinkPtr ExampleLink::create(
+    String const & recordName,
+    String const & providerName,
+    String const & channelName)
+{
+    PVStructurePtr pvStructure = getStandardPVField()->scalarArray(
+        pvDouble,"alarm.timeStamp");
+    ExampleLinkPtr pvRecord(
+        new ExampleLink(
+           recordName,providerName,channelName,pvStructure));
+    if(!pvRecord->init()) pvRecord.reset();
+    return pvRecord;
+}
+
+

This first creates a new ExampleLink instance, +and then calls the init method and the returns a ExampleLinkPtr. +Note that if init returns false it returns a pointer to NULL.

+

The init method is:

+
+bool ExampleLink::init()
+{
+    initPVRecord();
+
+    PVStructurePtr pvStructure = getPVRecordStructure()->getPVStructure();
+    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
+    pvAlarm.attach(pvStructure->getSubField("alarm"));
+    pvValue = static_pointer_cast<PVDoubleArray>(
+        pvStructure->getScalarArrayField("value",pvDouble));
+    if(pvValue==NULL) {
+        return false;
+    }
+    ChannelAccess::shared_pointer channelAccess = getChannelAccess();
+    ChannelProvider::shared_pointer provider =
+        channelAccess->getProvider(providerName);
+    if(provider==NULL) {
+         cout << getRecordName() << " provider "
+              << providerName << " does not exist" << endl;
+        return false;
+    }
+    ChannelRequester::shared_pointer channelRequester =
+        dynamic_pointer_cast<ChannelRequester>(getPtrSelf());
+    channel = provider->createChannel(channelName,channelRequester);
+    event.wait();
+    if(!status.isOK()) {
+        cout << getRecordName() << " createChannel failed "
+             << status.getMessage() << endl;
+        return false;
+    }
+    ChannelGetRequester::shared_pointer channelGetRequester =
+        dynamic_pointer_cast<ChannelGetRequester>(getPtrSelf());
+    PVStructurePtr pvRequest = getCreateRequest()->createRequest(
+        "value,alarm,timeStamp",getPtrSelf());
+    channelGet = channel->createChannelGet(channelGetRequester,pvRequest);
+    event.wait();
+    if(!status.isOK()) {
+        cout << getRecordName() << " createChannelGet failed "
+             << status.getMessage() << endl;
+        return false;
+    }
+    getPVValue = static_pointer_cast<PVDoubleArray>(
+        getPVStructure->getScalarArrayField("value",pvDouble));
+    if(getPVValue==NULL) {
+        cout << getRecordName() << " get value not  PVDoubleArray" << endl;
+        return false;
+    }
+    return true;
+}
+
+

This first makes sure the pvStructure has the fields it requires:

+
+
timeStamp
+
A timeStamp structure. This will be set to the current time when process is called.
+
alarm
+
An alarm structure. This will be used to pass status information to the client when + process is called.
+
value
+
This must be a scalarArray of type double. + It is where data is copied when the channelGet is issued.
+
+

Next it makes sure the channelProvider exists.

+

Next it creates the channel and waits until it connects.

+

Next it creates the channelGet and waits until it is created.

+

Next it makes sure it has connected to a double array field.

+

If anything goes wrong during initialization it returns false. +This a return of true means that it has successfully created a channelGet and is ready +to issue gets when process is called.

+

Look at the code for more details.

+

examplePowerSupply

+

This is an example of creating a service that requires a somewhat complicated +top level PVStructure. +It is similar to the powerSupply example that is provided with pvIOCJava. +Look at the code for details. +

+

Array Performance and Memory Example

+

This section describes main programs that demonstrate performance +of large arrays and can also be used to check for memory leaks. +Checking for memory leaks can be accomplished by running the programs with valgrind +or some other memory check program. +

+

Brief Summary

+

The programs are:

+
+
arrayPerformanceMain
+
This is server and also a configurable number of longArrayMonitor clients. + The clients can use either the local or + remote providers. The moitor code is the same code that is used by longArrayMonitorMain. +
+
longArrayMonitorMain
+
Remote client that monitors the array served by arrayPerformanceMain.
+
longArrayGetMain
+
Remote client that uses channelGet to access the array served by arrayPerformanceMain.
+
longArrayPutMain
+
Remote client that uses channelPut to access the array served by arrayPerformanceMain.
+
+

Each has support for -help.

+
+mrk> pwd
+/home/hg/pvDatabaseCPP-md
+mrk> bin/linux-x86_64/arrayPerformanceMain -help
+arrayPerformanceMain recordName size delay providerName nMonitor queueSize waitTime
+default
+arrayPerformance arrayPerformance 10000000 0.0001 local 1 2 0.0
+
+mrk> bin/linux-x86_64/longArrayMonitorMain -help
+longArrayMonitorMain channelName queueSize waitTime
+default
+longArrayMonitorMain arrayPerformance 2 0.0
+
+mrk> bin/linux-x86_64/longArrayGetMain -help
+longArrayGetMain channelName iterBetweenCreateChannel iterBetweenCreateChannelGet delayTime
+default
+longArrayGetMain arrayPerformance 0 0 1
+
+mrk> bin/linux-x86_64/longArrayPutMain -help
+longArrayPutMain channelName arraySize iterBetweenCreateChannel iterBetweenCreateChannelPut delayTime
+default
+longArrayPutMain arrayPerformance 10 0 0 1
+
+mrk> 
+
+

Example output

+

Note: These may fail if run on a platform that does not have sufficent memory,

+

To see an example just execute the following commands in four different terminal windows:

+
+bin/linux/<arch>/arrayPerformanceMain
+bin/linux/<arch>/longArrayMonitorMain
+bin/linux/<arch>/longArrayGetMain
+bin/linux/<arch>/longArrayPutMain
+
+

Each program generates a report every second when it has somthing to report. +Examples are: +

+mrk> bin/linux-x86_64/arrayPerformanceMain
+arrayPerformance arrayPerformance 10000000 0.0001 local 1 2 0
+...
+ monitors/sec 66 first 131 last 131 changed {1, 2} overrun {} megaElements/sec 656.999
+arrayPerformance value 132 time 1.00486 Iterations/sec 65.681 megaElements/sec 656.81
+ monitors/sec 66 first 197 last 197 changed {1, 2} overrun {} megaElements/sec 656.304
+arrayPerformance value 198 time 1.00563 Iterations/sec 65.6307 megaElements/sec 656.307
+ monitors/sec 66 first 263 last 263 changed {1, 2} overrun {} megaElements/sec 654.824
+...
+
+
+mrk> bin/linux-x86_64/longArrayMonitorMain
+longArrayMonitorMain arrayPerformance 2 0
+...
+ monitors/sec 6 first 2357 last 2357 changed {1, 2} overrun {} megaElements/sec 68.6406
+ monitors/sec 13 first 2385 last 2385 changed {1, 2} overrun {} megaElements/sec 118.72
+ monitors/sec 9 first 2418 last 2418 changed {1, 2} overrun {1, 2} megaElements/sec 85.0984
+...
+
+
+mrk> bin/linux-x86_64/longArrayPutMain
+longArrayPutMain arrayPerformance 10 0 0 1
+...
+put numChannelPut 0 time 1.00148 Elements/sec 79.8819
+put numChannelPut 1 time 1.00176 Elements/sec 79.8598
+...
+
+
+mrk> bin/linux-x86_64/longArrayGetMain
+longArrayGetMain arrayPerformance 0 0 1
+...
+get kiloElements/sec 7384.61
+get kiloElements/sec 8726.34
+...
+
+ +

arrayPerformance

+

The arguments for arrayPerforamanceMain are:

+
+
recordName
+
The name for the arrayPerform record.
+
size
+
The size for the long array of the value field.
+
delay
+
The time in seconds to sleep after each iteration.
+
providerName
+
The name of the channel provider for the longArrayMonitors + created by the main program. This must be either local + or pvAccess. +
+
nMonitor
+
The number of longArrayMonitors to create.
+
queueSize
+
The queueSize for the element queue. + A value less than 1 will become 1. +
+
waitTime
+
The time that longArrayMonitor will sleep after poll returns a monitorElement.
+
+

+arrayPerformance creates a PVRecord that has the structure:. +

+recordName
+    long[] value
+    timeStamp timeStamp
+    alarm alarm
+
+Thus it holds an array of 64 bit integers.

+

The record has support that consists of a separate thread that runs +until the record is destroyed executing the following algorithm:

+
+
report
+
Once a second it produces a report. + In the above example output each line starting with + ArrayPerformance is an arrayPerformance report. +
+
create array
+
A new shared_vector is created and each element is set equal + to the interation count.
+
lock
+
The arrayPerformance record is locked.
+
Begin group put
+
beginGroupReport is called.
+
replace
+
The value field of the record is replaced + with the newly created shared_vector.
+
process
+
The record is then processed. This causes the timeStamp to + be set to the current time.
+
End group put
+
endGroupPut is called.
+
unlock
+
The arrayPerformance record is unlocked.
+
delay
+
If delay is greater than zero epicsThreadSleep is called.
+
+

longArrayMonitor

+

This is a pvAccess client that monitors an arrayPerformance record. +It generates a report every second showing how many elements has received. +For every monitor it also checks that the number of alements is >0 and the +the first element equals the last element. It reports an error if either +of these conditions is not true.

+

The arguments for longArrayMonitorMain are:

+
+
channelName
+
The name for the arrayPerform record.
+
queueSize
+
The queueSize. Note that any size <2 is made 2.
+
waitTime
+
The time to wait after a poll request returns a monitorElement. + This can be used to force an overrun of the client even if there is no + overrun on the server.
+
+

longArrayGet

+

This is a pvAccess client that uses channelGet to access an arrayPerformance record. +Every second it produces a report.

+ +

The arguments for longArrayGetMain are:

+
+
channelName
+
The name for the arrayPerform record.
+
iterBetweenCreateChannel
+
The number of iterations between destroying and recreating the channel. + A value of 0 means never destroy and recreate. +
+
iterBetweenCreateChannelGet
+
The number of iterations between destroying and recreating the channelGet. + A value of 0 means never destroy and recreate. +
+
delayTime
+
The time to dalay between gets.
+
+

longArrayPut

+

This is a pvAccess client that uses channelPut to access an arrayPerformance record. +Every second it produces a report.

+ +

The arguments for longArrayPutMain are:

+
+
channelName
+
The name for the arrayPerform record.
+
arraySize
+
The capacity and length of the array to put to the server.
+
iterBetweenCreateChannel
+
The number of iterations between destroying and recreating the channel. + A value of 0 means never destroy and recreate. +
+
iterBetweenCreateChannelPut
+
The number of iterations between destroying and recreating the channelPut. + A value of 0 means never destroy and recreate. +
+
delayTime
+
The time to dalay between gets.
+
+ +

Some results

+

array performance

+

The results were from my laptop. +It has a 2.2Ghz intel core i7 with 4Gbytes of memory. +The operating system is linux fedora 16.

+

When test are performed with large arrays it is a good idea to also +run a system monitor facility and check memory and swap history. +If a test configuration causes physical memory to be exhausted +then performance becomes very poor. +You do not want to do this.

+

arrayPerformance results

+

The simplest test to run arrayPerformance with the defaults:

+
+mrk> pwd
+/home/hg/pvDatabaseCPP-md
+mrk> bin/linux-x86_64/arrayPerformanceMain
+
+

This means that the array will hold 10 million elements. +The delay will be a millisecond. +There will be a single monitor and it will connect directly +to the local channelProvider, i. e. it will not use any network +connection.

+

The report shows that arrayPerformance can perform about 50 iterations per second +and is putting about 500million elements per second. +Since each element is an int64 this means about 4gigaBytes per second. +

+

When no monitors are requested and a remote longArrayMonitorMain is run:

+

+mr> pwd
+/home/hg/pvDatabaseCPP-md
+mrk> bin/linux-x86_64/longArrayMonitorMain
+
+

The performance drops to about 25 interations per second and 250 million elements per second. +The next section has an example that demonstrates what happens. +Note that if the array size is small enough to fit in the local cache then running longArrayMonitor +has almost no effect of arrayPerforance. +

+

memory leaks

+

Running longArrayMonitorMain, longArrayPutMain, and longArrayGetMain +under valgrind shows no memory leaks.

+

arrayPerformanceMain shows the following:

+
+==9125== LEAK SUMMARY:
+==9125==    definitely lost: 0 bytes in 0 blocks
+==9125==    indirectly lost: 0 bytes in 0 blocks
+==9125==      possibly lost: 576 bytes in 2 blocks
+
+

The possibly leaked is either 1 or 2 blocks. +It seems to be the same if clients are connected. +

+

Vector Performance

+

This example demonstrates how array size effects performance. +The example is run as:

+
+bin/linux-x86_64/vectorPerformanceMain -help
+vectorPerformanceMain size delay nThread
+default
+vectorPerformance 50000000 0.01 1 
+
+

Consider the following:

+
+bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 1
+...
+thread0 value 20 time 1.01897 iterations/sec 19.6277 elements/sec 981.383million
+thread0 value 40 time 1.01238 iterations/sec 19.7554 elements/sec 987.772million
+thread0 value 60 time 1.00878 iterations/sec 19.826 elements/sec 991.299million
+...
+bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 2
+...
+thread0 value 21 time 1.00917 iterations/sec 9.90911 elements/sec 495.455million
+thread1 value 31 time 1.05659 iterations/sec 9.46443 elements/sec 473.221million
+thread0 value 31 time 1.07683 iterations/sec 9.28648 elements/sec 464.324million
+thread1 value 41 time 1.0108 iterations/sec 9.89312 elements/sec 494.656million
+...
+bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 3
+thread0 value 7 time 1.0336 iterations/sec 6.77244 elements/sec 338.622million
+thread1 value 7 time 1.03929 iterations/sec 6.73534 elements/sec 336.767million
+thread2 value 7 time 1.04345 iterations/sec 6.70852 elements/sec 335.426million
+thread0 value 14 time 1.03335 iterations/sec 6.77406 elements/sec 338.703million
+thread1 value 14 time 1.03438 iterations/sec 6.76734 elements/sec 338.367million
+thread2 value 14 time 1.04197 iterations/sec 6.71805 elements/sec 335.903million
+...
+bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 4
+thread2 value 5 time 1.00746 iterations/sec 4.96298 elements/sec 248.149million
+thread1 value 5 time 1.02722 iterations/sec 4.86751 elements/sec 243.376million
+thread3 value 5 time 1.032 iterations/sec 4.84496 elements/sec 242.248million
+thread0 value 6 time 1.18882 iterations/sec 5.04703 elements/sec 252.351million
+thread2 value 10 time 1.00388 iterations/sec 4.98068 elements/sec 249.034million
+thread3 value 10 time 1.02755 iterations/sec 4.86592 elements/sec 243.296million
+thread1 value 10 time 1.04836 iterations/sec 4.76936 elements/sec 238.468million
+thread0 value 11 time 1.01575 iterations/sec 4.92249 elements/sec 246.124million
+
+

As more threads are running the slower each thread runs.

+

But now consider a size that fits in a local cache.

+

+bin/linux-x86_64/vectorPerformanceMain 5000 0.00n/linux-x86_64/vectorPerformanceMain 5000 0.00 1
+...
+thread0 value 283499 time 1 iterations/sec 283498 elements/sec 1417.49million
+thread0 value 569654 time 1 iterations/sec 286154 elements/sec 1430.77million
+thread0 value 856046 time 1 iterations/sec 286392 elements/sec 1431.96million
+... 
+bin/linux-x86_64/vectorPerformanceMain 5000 0.00 2
+...
+thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
+thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
+thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
+thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
+thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
+thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
+thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
+thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
+...
+bin/linux-x86_64/vectorPerformanceMain 5000 0.00 3
+...
+thread0 value 257090 time 1 iterations/sec 257089 elements/sec 1285.45million
+thread1 value 256556 time 1 iterations/sec 256556 elements/sec 1282.78million
+thread2 value 514269 time 1 iterations/sec 257839 elements/sec 1289.19million
+thread0 value 514977 time 1 iterations/sec 257887 elements/sec 1289.43million
+thread1 value 514119 time 1 iterations/sec 257563 elements/sec 1287.81million
+thread2 value 770802 time 1 iterations/sec 256532 elements/sec 1282.66million
+
+

Now the number of threads has a far smaller effect on the performance of each thread. +

+ +
+ + diff --git a/exampleDatabase/ioc/src/Makefile b/exampleDatabase/ioc/src/Makefile index fa45582..71626ac 100644 --- a/exampleDatabase/ioc/src/Makefile +++ b/exampleDatabase/ioc/src/Makefile @@ -26,11 +26,13 @@ exampleDatabase_SRCS_vxWorks += -nil- # The following adds support from base/src/vxWorks exampleDatabase_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary -exampleDatabase_LIBS += pvData pvAccess -exampleDatabase_LIBS += pvDatabase -exampleDatabase_LIBS += pvaSrv exampleDatabase_LIBS += exampleDatabase exampleDatabase_LIBS += powerSupply +exampleDatabase_LIBS += pvDatabase +exampleDatabase_LIBS += pvaSrv +exampleDatabase_LIBS += pvAccess +exampleDatabase_LIBS += pvData +exampleDatabase_LIBS += Com exampleDatabase_LIBS += $(EPICS_BASE_IOC_LIBS) #=========================== diff --git a/exampleDatabase/src/Makefile b/exampleDatabase/src/Makefile index 79e571b..2c1f2f0 100644 --- a/exampleDatabase/src/Makefile +++ b/exampleDatabase/src/Makefile @@ -12,12 +12,12 @@ include $(TOP)/configure/CONFIG PROD_HOST += exampleDatabaseMain exampleDatabaseMain_SRCS += exampleDatabaseMain.cpp -exampleDatabaseMain_LIBS += Com -exampleDatabaseMain_LIBS += pvData -exampleDatabaseMain_LIBS += pvAccess -exampleDatabaseMain_LIBS += pvDatabase exampleDatabaseMain_LIBS += exampleDatabase exampleDatabaseMain_LIBS += powerSupply +exampleDatabaseMain_LIBS += pvDatabase +exampleDatabaseMain_LIBS += pvAccess +exampleDatabaseMain_LIBS += pvData +exampleDatabaseMain_LIBS += Com DBD += exampleDatabase.dbd @@ -26,9 +26,10 @@ INC += exampleDatabase.h LIBRARY_IOC += exampleDatabase exampleDatabase_SRCS += exampleDatabase.cpp exampleDatabase_SRCS += exampleDatabaseRegister.cpp -exampleDatabase_LIBS += pvData -exampleDatabase_LIBS += pvAccess exampleDatabase_LIBS += pvDatabase +exampleDatabase_LIBS += pvAccess +exampleDatabase_LIBS += pvData +exampleDatabase_LIBS += Com exampleDatabase_LIBS += $(EPICS_BASE_IOC_LIBS) #=========================== diff --git a/exampleLink/ioc/src/Makefile b/exampleLink/ioc/src/Makefile index 37561a9..ceeef9f 100644 --- a/exampleLink/ioc/src/Makefile +++ b/exampleLink/ioc/src/Makefile @@ -27,9 +27,12 @@ exampleLink_SRCS_vxWorks += -nil- # The following adds support from base/src/vxWorks exampleLink_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary -exampleLink_LIBS += pvData pvAccess -exampleLink_LIBS += pvDatabase exampleLink_LIBS += exampleLink +exampleLink_LIBS += pvDatabase +exampleLink_LIBS += pvaSrv +exampleLink_LIBS += pvAccess +exampleLink_LIBS += pvData +exampleLink_LIBS += Com exampleLink_LIBS += $(EPICS_BASE_IOC_LIBS) #=========================== diff --git a/exampleLink/src/Makefile b/exampleLink/src/Makefile index 1fbc49f..0856c00 100644 --- a/exampleLink/src/Makefile +++ b/exampleLink/src/Makefile @@ -16,9 +16,10 @@ INC += exampleLink.h LIBRARY_IOC += exampleLink exampleLink_SRCS += exampleLink.cpp exampleLink_SRCS += exampleLinkRegister.cpp -exampleLink_LIBS += pvData -exampleLink_LIBS += pvAccess exampleLink_LIBS += pvDatabase +exampleLink_LIBS += pvAccess +exampleLink_LIBS += pvData +exampleLink_LIBS += Com exampleLink_LIBS += $(EPICS_BASE_IOC_LIBS) #=========================== diff --git a/examplePowerSupply/ioc/src/Makefile b/examplePowerSupply/ioc/src/Makefile index b2092c2..3254645 100644 --- a/examplePowerSupply/ioc/src/Makefile +++ b/examplePowerSupply/ioc/src/Makefile @@ -26,10 +26,11 @@ powerSupply_SRCS_vxWorks += -nil- # The following adds support from base/src/vxWorks powerSupply_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary -powerSupply_LIBS += pvData pvAccess +powerSupply_LIBS += powerSupply powerSupply_LIBS += pvDatabase powerSupply_LIBS += pvaSrv -powerSupply_LIBS += powerSupply +powerSupply_LIBS += pvAccess +powerSupply_LIBS += pvData powerSupply_LIBS += $(EPICS_BASE_IOC_LIBS) #=========================== diff --git a/examplePowerSupply/src/Makefile b/examplePowerSupply/src/Makefile index a8f1754..ded3df1 100644 --- a/examplePowerSupply/src/Makefile +++ b/examplePowerSupply/src/Makefile @@ -13,10 +13,10 @@ include $(TOP)/configure/CONFIG PROD_HOST += powerSupplyMain powerSupplyMain_SRCS += powerSupplyMain.cpp -powerSupplyMain_LIBS += Com -powerSupplyMain_LIBS += pvData -powerSupplyMain_LIBS += pvAccess powerSupplyMain_LIBS += pvDatabase +powerSupplyMain_LIBS += pvAccess +powerSupplyMain_LIBS += pvData +powerSupplyMain_LIBS += Com powerSupplyMain_LIBS += powerSupply #=========================== diff --git a/exampleServer/ioc/src/Makefile b/exampleServer/ioc/src/Makefile index 83f50e7..ae94689 100644 --- a/exampleServer/ioc/src/Makefile +++ b/exampleServer/ioc/src/Makefile @@ -26,10 +26,11 @@ exampleServer_SRCS_vxWorks += -nil- # The following adds support from base/src/vxWorks exampleServer_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary -exampleServer_LIBS += pvData pvAccess +exampleServer_LIBS += exampleServer exampleServer_LIBS += pvDatabase exampleServer_LIBS += pvaSrv -exampleServer_LIBS += exampleServer +exampleServer_LIBS += pvAccess +exampleServer_LIBS += pvData exampleServer_LIBS += $(EPICS_BASE_IOC_LIBS) #=========================== diff --git a/exampleServer/src/Makefile b/exampleServer/src/Makefile index 92b6566..a0e18f1 100644 --- a/exampleServer/src/Makefile +++ b/exampleServer/src/Makefile @@ -10,14 +10,6 @@ include $(TOP)/configure/CONFIG # -PROD_HOST += exampleServerMain -exampleServerMain_SRCS += exampleServerMain.cpp -exampleServerMain_LIBS += Com -exampleServerMain_LIBS += pvData -exampleServerMain_LIBS += pvAccess -exampleServerMain_LIBS += pvDatabase -exampleServerMain_LIBS += exampleServer - DBD += exampleServer.dbd INC += exampleServer.h @@ -25,11 +17,20 @@ INC += exampleServer.h LIBRARY_IOC += exampleServer exampleServer_SRCS += exampleServer.cpp exampleServer_SRCS += exampleServerRegister.cpp -exampleServer_LIBS += pvData -exampleServer_LIBS += pvAccess exampleServer_LIBS += pvDatabase +exampleServer_LIBS += pvAccess +exampleServer_LIBS += pvData +exampleServer_LIBS += Com exampleServer_LIBS += $(EPICS_BASE_IOC_LIBS) +PROD_HOST += exampleServerMain +exampleServerMain_SRCS += exampleServerMain.cpp +exampleServerMain_LIBS += exampleServer +exampleServerMain_LIBS += pvDatabase +exampleServerMain_LIBS += pvAccess +exampleServerMain_LIBS += pvData +exampleServerMain_LIBS += Com + #=========================== include $(TOP)/configure/RULES diff --git a/src/Makefile b/src/Makefile index d2a20ec..453a782 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,8 +4,8 @@ include $(TOP)/configure/CONFIG DATABASE = $(TOP)/src/ LIBRARY_IOC += pvDatabase +pvDatabase_LIBS += pvAccess pvData Com pvDatabase_LIBS += $(EPICS_BASE_IOC_LIBS) -pvDatabase_LIBS += Com pvData pvAccess SRC_DIRS += $(DATABASE)/database INC += pvDatabase.h diff --git a/test/src/Makefile b/test/src/Makefile index 55df351..ed4eda8 100644 --- a/test/src/Makefile +++ b/test/src/Makefile @@ -9,9 +9,10 @@ INC += powerSupply.h LIBRARY_IOC += powerSupply powerSupply_SRCS += powerSupply.cpp powerSupply_SRCS += powerSupplyRegister.cpp -powerSupply_LIBS += pvData -powerSupply_LIBS += pvAccess powerSupply_LIBS += pvDatabase +powerSupply_LIBS += pvAccess +powerSupply_LIBS += pvData +powerSupply_LIBS += Com powerSupply_LIBS += $(EPICS_BASE_IOC_LIBS)