pull from epics-base

This commit is contained in:
mrkraimer
2017-07-21 06:22:41 -04:00
132 changed files with 10685 additions and 7161 deletions

2
.gitignore vendored
View File

@@ -5,8 +5,6 @@ include/
db/
dbd/
html/
documentation/html
documentation/*.tag
envPaths
configure/*.local
!configure/ExampleRELEASE.local

26
.travis.yml Normal file
View File

@@ -0,0 +1,26 @@
sudo: false
dist: trusty
language: c++
compiler:
- gcc
addons:
apt:
packages:
- libreadline6-dev
- libncurses5-dev
- perl
- clang
- g++-mingw-w64-i686
env:
- BRBASE=3.16
- BRBASE=3.16 CMPLR=clang
- BRBASE=3.16 USR_CXXFLAGS=-std=c++11
- BRBASE=3.16 USR_CXXFLAGS=-std=c++11 CMPLR=clang
- BRBASE=3.16 WINE=32 TEST=NO STATIC=YES
- BRBASE=3.16 WINE=32 TEST=NO STATIC=NO
- BRBASE=3.15
- BRBASE=3.14
install:
- ./ci/travis-prepare.sh
script:
- ./ci/travis-build.sh

View File

@@ -8,10 +8,19 @@ DIRS := configure
DIRS += src
src_DEPEND_DIRS = configure
DIRS += src/ca
src/ca_DEPEND_DIRS = src
DIRS += src/ioc
src/ioc_DEPEND_DIRS = src
DIRS += pvtoolsSrc
pvtoolsSrc_DEPEND_DIRS = src
pvtoolsSrc_DEPEND_DIRS = src src/ca
DIRS += testApp
testApp_DEPEND_DIRS = src
DIRS += examples
examples_DEPEND_DIRS += src src/ca
include $(TOP)/configure/RULES_TOP

View File

@@ -1,37 +0,0 @@
Release 5.x.x
==========
*
Release 5.0.0
==========
* Remote channel destroy support
* Multiple network inteface support
* Local multicast (repetitor) reimplemented
* Monitor reconnect when channel type changes fix
* C++11 compilation fixes
* Added version to pvaTools
* Memory management improved
* pipeline: ackAny argument percentage support
* Monitor overrun memory issues fixed
* CA provider destruction fixed
* Replaced LGPL wildcard matcher with simplistic EPICS version
Release 4.1.2
==========
* Improved Jenkins build support
* Removed QtCreated IDE configuration files
* Use of getSubField<> instead of e.g. getDoubleField()
* CA support for pvget, pvput and pvinfo.
* vxWorks/RTEMS compiler warnings resolved.
* Transport shutdown improved.
* CA DBR status fix.
* Monitor queue handling improved.
* Fixed bad performance on 10Gbit or faster networks.
* Async RPC service.
Release 4.0.5
==========
(Starting point for release notes.)

10
ci/travis-build.sh Executable file
View File

@@ -0,0 +1,10 @@
#!/bin/sh
set -e -x
make -j2
if [ "$TEST" != "NO" ]
then
make tapfiles
find . -name '*.tap' -print0 | xargs -0 -n1 prove -e cat -f
fi

69
ci/travis-prepare.sh Executable file
View File

@@ -0,0 +1,69 @@
#!/bin/sh
set -e -x
cat << EOF > configure/RELEASE.local
EPICS_BASE=$HOME/.source/epics-base
PVDATA=$HOME/.source/pvDataCPP
EOF
cat configure/RELEASE.local
install -d "$HOME/.source"
cd "$HOME/.source"
git clone --quiet --depth 5 --branch "${BRBASE:-master}" https://github.com/${SRCBASE:-epics-base}/epics-base.git epics-base
git clone --quiet --depth 5 --branch "${BRPVD:-master}" https://github.com/${SRCPVD:-epics-base}/pvDataCPP.git pvDataCPP
(cd epics-base && git log -n1 )
(cd pvDataCPP && git log -n1 )
EPICS_HOST_ARCH=`sh epics-base/startup/EpicsHostArch`
# requires wine and g++-mingw-w64-i686
if [ "$WINE" = "32" ]
then
echo "Cross mingw32"
sed -i -e '/CMPLR_PREFIX/d' epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw
cat << EOF >> epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw
CMPLR_PREFIX=i686-w64-mingw32-
EOF
cat << EOF >> epics-base/configure/CONFIG_SITE
CROSS_COMPILER_TARGET_ARCHS+=win32-x86-mingw
EOF
fi
if [ "$STATIC" = "YES" ]
then
echo "Build static libraries/executables"
cat << EOF >> epics-base/configure/CONFIG_SITE
SHARED_LIBRARIES=NO
STATIC_BUILD=YES
EOF
fi
case "$CMPLR" in
clang)
echo "Host compiler is clang"
cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.$EPICS_HOST_ARCH
GNU = NO
CMPLR_CLASS = clang
CC = clang
CCC = clang++
EOF
# hack
sed -i -e 's/CMPLR_CLASS = gcc/CMPLR_CLASS = clang/' epics-base/configure/CONFIG.gnuCommon
clang --version
;;
*)
echo "Host compiler is default"
gcc --version
;;
esac
cat << EOF > pvDataCPP/configure/RELEASE.local
EPICS_BASE=$HOME/.source/epics-base
EOF
make -j2 -C epics-base
make -j2 -C pvDataCPP

View File

@@ -23,6 +23,13 @@ CHECK_RELEASE = YES
-include $(TOP)/../CONFIG_SITE.local
-include $(TOP)/configure/CONFIG_SITE.local
EPICS_PVA_MAJOR_VERSION = 6
EPICS_PVA_MINOR_VERSION = 0
EPICS_PVA_MAINTENANCE_VERSION = 0
EPICS_PVA_DEVELOPMENT_FLAG = 1
SHRLIB_VERSION ?= $(EPICS_PVA_MAJOR_VERSION).$(EPICS_PVA_MINOR_VERSION).$(EPICS_PVA_MAINTENANCE_VERSION)
ifdef WITH_COVERAGE
USR_CPPFLAGS += --coverage
USR_LDFLAGS += --coverage

5
documentation/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
*.tag
*.db
*.tmp
html/
latex/

2413
documentation/Doxyfile Normal file

File diff suppressed because it is too large Load Diff

14
documentation/Makefile Normal file
View File

@@ -0,0 +1,14 @@
all: gen
clean:
rm -rf doxygen_sqlite3.db html
gen:
doxygen
commit: gen
touch html/.nojekyll
./commit-gh.sh documentation/html/ html/.nojekyll html/*.* html/search/*.*
.PHONY: all clean gen commit

View File

@@ -0,0 +1,74 @@
digraph clientowner {
ChannelProvider;
Channel;
ChannelRequester [shape=box];
External [shape=box];
Channel;
ChannelRequester [shape=box];
ChannelGet;
ChannelGetRequester [shape=box];
ChannelPut;
ChannelPutRequester [shape=box];
Monitor;
MonitorRequester [shape=box];
ChannelRPC;
ChannelRPCRequester [shape=box];
ChannelProcess;
ChannelProcessRequester [shape=box];
ChannelPutGet;
ChannelPutGetRequester [shape=box];
ChannelArray;
ChannelArrayRequester [shape=box];
Channel -> ChannelProvider [color=green, style=dashed];
ChannelProvider -> Channel [color=green, style=dashed];
# Operation -> Requester weak ref
Channel -> ChannelRequester [color=green, style=dashed];
ChannelGet -> ChannelGetRequester [color=green, style=dashed];
ChannelPut -> ChannelPutRequester [color=green, style=dashed];
Monitor -> MonitorRequester [color=green, style=dashed];
ChannelRPC -> ChannelRPCRequester [color=green, style=dashed];
ChannelProcess -> ChannelProcessRequester [color=green, style=dashed];
ChannelPutGet -> ChannelPutGetRequester [color=green, style=dashed];
ChannelArray -> ChannelArrayRequester [color=green, style=dashed];
# Channel -> Operation weak ref
Channel -> ChannelGet [color=green, style=dashed];
Channel -> ChannelPut [color=green, style=dashed];
Channel -> Monitor [color=green, style=dashed];
Channel -> ChannelRPC [color=green, style=dashed];
Channel -> ChannelProcess [color=green, style=dashed];
Channel -> ChannelPutGet [color=green, style=dashed];
Channel -> ChannelArray [color=green, style=dashed];
# Operation -> Channel strong ref
ChannelGet -> Channel [color=red, style=dashed];
ChannelPut -> Channel [color=red, style=dashed];
Monitor -> Channel [color=red, style=dashed];
ChannelRPC -> Channel [color=red, style=dashed];
ChannelProcess -> Channel [color=red, style=dashed];
ChannelPutGet -> Channel [color=red, style=dashed];
ChannelArray -> Channel [color=red, style=dashed];
# user code ownership
External -> ChannelProvider [color=red];
External -> ChannelRequester [color=red];
External -> ChannelGetRequester [color=red];
External -> ChannelPutRequester [color=red];
External -> MonitorRequester [color=red];
External -> ChannelRPCRequester [color=red];
External -> ChannelProcessRequester [color=red];
External -> ChannelPutGetRequester [color=red];
External -> ChannelArrayRequester [color=red];
ChannelRequester -> Channel [color=red];
ChannelGetRequester -> ChannelGet [color=red];
ChannelPutRequester -> ChannelPut [color=red];
MonitorRequester -> Monitor [color=red];
ChannelRPCRequester -> ChannelRPC [color=red];
ChannelProcessRequester -> ChannelProcess [color=red];
ChannelPutGetRequester -> ChannelPutGet [color=red];
ChannelArrayRequester -> ChannelArray [color=red];
}

45
documentation/commit-gh.sh Executable file
View File

@@ -0,0 +1,45 @@
#!/bin/sh
set -e -x
# Usage: commit-gh <sub-directory-prefix> <files...>
#
# Creates a commit containing only the files in the sub-directory provided as an argument
#
# Does not disturb the working copy or index
prefix="$1"
shift
# Commit to this branch
BRANCH=refs/heads/gh-pages
# Use the main branch description as the gh-pages commit message
MSG=`git describe --tags --always`
# Scratch space
TDIR=`mktemp -d -p $PWD`
# Automatic cleanup of scratch space
trap 'rm -rf $TDIR' INT TERM QUIT EXIT
export GIT_INDEX_FILE="$TDIR/index"
# Add listed files to a new (empty) index
git update-index --add "$@"
# Write the index into the repo, get tree hash
TREE=`git write-tree --prefix="$prefix"`
echo "TREE $TREE"
git cat-file -p $TREE
# Create a commit with our new tree
# Reference current branch head as parent (if any)
CMT=`git commit-tree -m "$MSG" $TREE`
echo "COMMIT $CMT"
git cat-file -p $CMT
# Update the branch with the new commit tree hash
git update-ref $BRANCH $CMT
echo "Done"

57
documentation/examples.h Normal file
View File

@@ -0,0 +1,57 @@
/**
@page examples_miniget Simple Client Get Example
The shortest possible PVA get() example.
@include miniget.cpp
*/
/**
@page examples_miniput Simple Client Put Example
The shortest possible PVA put() example.
@include miniput.cpp
*/
/**
@page examples_minimonitor Simple Client Monitor Example
The shortest possible PVA monitor() example.
@include minimonitor.cpp
*/
/**
@page examples_getme Client Get Example
This example demonstrates a client which issues a Get operation on startup,
and again each time a channel becomes Connected.
@include getme.cpp
*/
/**
@page examples_putme Client Put Example
This example demonstrates a client which issues a Put operation on startup,
waits for acknowledgement that the put has been handled,
then exits.
@include putme.cpp
*/
/**
@page examples_monitorme Client Monitor Example
This example demonstrates a client which sets up a persistent Monitor operation.
@include monitorme.cpp
*/

27
documentation/mainpage.h Normal file
View File

@@ -0,0 +1,27 @@
#ifndef MAINPAGE_H
#define MAINPAGE_H
/**
@mainpage pvAccess C++ docs
- [Download](https://sourceforge.net/projects/epics-pvdata/files/)
- @htmlonly <a href="modules.html">API components</a> @endhtmlonly
- @ref pvarelease_notes
- API documentation
- @ref pvac page
- @ref providers page
- @ref pvtools
@section main_examples API usage Examples
- Simple synchronous (blocking) examples
- @ref examples_miniget
- @ref examples_miniput
- @ref examples_minimonitor
- More complete callback based examples
- @ref examples_getme
- @ref examples_putme
- @ref examples_monitorme
*/
#endif /* MAINPAGE_H */

View File

@@ -0,0 +1,8 @@
digraph ownership {
MyClient [shape=box];
Channel;
ChannelRequester [shape=box];
MyClient -> Channel [color=red];
Channel -> ChannelRequester [color=red, style=dashed];
ChannelRequester -> MyClient [color=green];
}

345
documentation/providers.h Normal file
View File

@@ -0,0 +1,345 @@
#ifndef PROVIDERS_H
#define PROVIDERS_H
/**
@page providers ChannelProvider API
@tableofcontents
The epics::pvAccess namespace.
See pv/pvAccess.h header.
@code
#include <pv/configuration.h>
#include <pv/pvAccess.h>
#include <pv/clientFactory.h>
@endcode
@section providers_roles Roles
The Client and Server APIs revolve around the epics::pvAccess::ChannelProvider class.
In the following discussion the @ref providers_client calls methods of ChannelProvider and associated classes.
The @ref providers_server implements ChannelProvider and associated classes and is called by a client.
By convention, instances of ChannelProvider are registered and retrieved through one of
epics::pvAccess::ChannelProviderRegistry::clients()
or
epics::pvAccess::ChannelProviderRegistry::servers()
@subsection provider_roles_requester Operation and Requester
The classes associated with ChannelProvider come in pairs.
eg.
- epics::pvAccess::Channel and epics::pvAccess::ChannelRequester
- epics::pvAccess::ChannelGet and epics::pvAccess::ChannelGetRequester
- epics::pvAccess::ChannelPut and epics::pvAccess::ChannelPutRequester
- epics::pvAccess::Monitor and epics::pvAccess::MonitorRequester
- epics::pvAccess::ChannelRPC and epics::pvAccess::ChannelRPCRequester
- epics::pvAccess::ChannelProcess and epics::pvAccess::ChannelProcessRequester
- epics::pvAccess::ChannelPutGet and epics::pvAccess::ChannelPutGetRequester
- epics::pvAccess::ChannelArray and epics::pvAccess::ChannelArrayRequester
In the following discussions the term "Operation" refers to eg. Channel, ChannelGet, or similar
while "Requester" refers to ChannelRequester, ChannelGetRequester, or similar.
The "Requester" classes are implemented by the Client role
and called by the Server role to give notification to the client of certain events.
For example,
epics::pvAccess::ChannelRequester::channelStateChange()
is called when a Channel becomes (dis)connected.
A "Requester" sub-class must be provided when each "Operation" is created.
This Requester then becomes bound to the Operation.
@note An exception to this is epics::pvAccess::ChannelProvider::createChannel()
Where a epics::pvAccess::ChannelRequester may be omitted.
For convenience each Operation class has a member typedef for it's associated Requester, and vis. versa.
For example ChannelGet::requester_type is ChannelGetRequester
and ChannelGetRequester::operation_type is ChannelGet.
@subsubsection provider_roles_requester_locking
Operations methods may call requester methods, and vis versa.
The following rules must be followed to avoid deadlocks.
- No locks must be held when Requester methods are called.
- Locks may be held when Operation methods are called.
These rules place the burdon of avoiding deadlocks on the ChannelProvider implementation (Server role).
Clients must still be aware when some Operation methods can call some Requester methods recursively,
and consider this when locking.
For example, the following call stack may legitimetly occur for a ChannelProvider
to for a Get which accesses locally stored data.
- Channel::createChannelGet()
- ChannelGetRequester::channelGetConnect()
- ChannelGet::get()
- ChannelGetRequester::getDone()
Thus care should be taken when calling ChannelGet::get() from within ChannelGetRequester::getDone()
to avoid infinite recursion.
@subsection providers_ownership shared_ptr<> and Ownership
"Operations" and "Requesters" are always handled via std::tr1::shared_ptr.
In the following dicussions an instance of std::tr1::shared_ptr is referred to as a "reference",
specifically a strong reference.
The associated std::tr1::weak_ptr is referred to as a weak reference.
shared_ptr instances can exist on the stack (local variables) or as
struct/class members.
Situations where an object contains a reference to itself, either directly or indirectly,
are known as "reference loops".
As long as a reference loop persists, any cleanup of resources associated with
the shared_ptr (s) involved will not be done.
A reference loop which is never broken is called a "reference leak".
In order to avoid reference leaks, required relationships between
various classes will be described, and some rules stated.
In discussing the usage of an API diagrams like the following will be used to illustrate roles and ownership requirements.
The distinction of what is "user" code will depend on context.
For example, when discussing the Client role, epics::pvAccess::Channel will not be implemented by "user code".
When discussing the Server role, user code will implement Channel.
@subsubsection providers_ownership_unique Uniqueness
A shared_ptr is said to be unique if it is the only strong reference to the underlying object.
In this case shared_ptr::unique() returns true.
The general rule is that functions which create/allocate new objects using shared_ptr must yield a unique shared_ptr.
Yielding a non-unique shared_ptr is a sign that an internal reference leak exists.
@dotfile ownership.dot "shared_ptr relationships"
- A box denotes a class implemented by user code
- An oval denotes a class not implemented by user code
- A red line is a shared_ptr<>
- A green line is a weak_ptr<>
- A dashed line indicates a relationship which is outside the control of user code
@code
struct MyClient {
epics::pvAccess::Channel::shared_ptr channel;
};
struct MyChannelRequester : public epics::pvAccess::ChannelRequester
{
std::tr1::weak_ptr<MyClient> client;
virtual void channelStateChange(Channel::shared_pointer const & channel, Channel::ConnectionState connectionState) {
std::tr1::shared_ptr<MyClient> C(client.lock());
if(!C) return;
...
}
};
@endcode
In this example user code implements a custom MyClient class and a sub-class of ChannelRequester in order
to make use of a Channel.
In order to avoid a reference loop, the sub-class of ChannelRequester uses a weak_ptr
to refer to MyClient during channelStateChange() events.
@section providers_client Client Role
A client will by configured with a certain provider name string.
It will begin by passing this string to
epics::pvAccess::ChannelProviderRegistry::createProvider()
to obtain a ChannelProvider instance.
Custom configuration of provider can be accomplished by
passing a
epics::pvAccess::Configuration
to createProvider().
A Configuration is a set of string key/value parameters
which the provider may use as it sees fit.
If a Configuration is not provided (or NULL) then
the provider will use some arbitrary defaults.
By convention default Configuration use the process environment.
@code
#include <pv/pvAccess.h>
#include <pv/clientFactory.h>
// add "pva" to registry
epics::pvAccess::ClientFactory::start();
// create a new client instance.
epics::pvAccess::ChannelProvider::shared_pointer prov;
prov = epics::pvAccess::getChannelProviderRegistry()->createProvider("pva");
// createProvider() returns NULL if the named provider hasn't been registered
if(!prov)
throw std::runtime_error("PVA provider not registered");
@endcode
@subsection providers_client_channel Client Channel
The primary (and in many cases only) ChannelProvider method of interest is
epics::pvAccess::ChannelProvider::createChannel()
from which a new
epics::pvAccess::Channel
can be obtained.
Each call to createChannel() produces a "new" std::shared_ptr<Channel>
which is uniquely owned by the caller (see @ref providers_ownership_unique).
As such, the caller must keep a reference to to the Channel or it will be destroyed.
This may be done explicitly, or implicitly by storing a reference to an Operation.
@note The returned Channel does *not* hold a strong reference for the ChannelProvider
from which it was created.
User code *must* keep a reference to the provider as long as Channels are in use.
All Channels are automatically closed when their provider is destroyed.
A Channel can be created at any time, and shall succeed as long as
the provided name and address are syntactically valid, and the priority is in the valid range.
When created, a Channel may or may not already be in the Connected state.
On creation epics::pvAccess::ChannelRequester::channelCreated will be called
before createChannel() returns.
Notification of connection state changes are made through
epics::pvAccess::ChannelRequester::channelStateChange()
as well as through the *Connect() and channelDisconnect() methods
of Requesters of any Operations on a Channel (eg.
epics::pvAccess::MonitorRequester::channelDisconnect()
).
@subsection providers_client_operations Client Operations
This section describes commonalities between the various Operation supported:
Get, Put, Monitor, RPC, PutGet, Process, and Array.
An Operation is created/allocated with one of the create* methods of Channel.
All behave in a similar way.
- epics::pvAccess::Channel::createChannelGet()
- epics::pvAccess::Channel::createChannelPut()
- epics::pvAccess::Channel::createMonitor()
- epics::pvAccess::Channel::createChannelRPC()
- epics::pvAccess::Channel::createChannelPutGet()
- epics::pvAccess::Channel::createChannelProcess()
- epics::pvAccess::Channel::createChannelArray()
The created Operation is unique (see @ref providers_ownership_unique).
The \*Connect() method of the corresponding Requester will be called when
the Operation is "ready" (underlying Channel is connected).
This may happen before the create* method has returned, or at some time later.
- epics::pvAccess::ChannelGetRequester::channelGetConnect()
- epics::pvAccess::ChannelPutRequester::channelPutConnect()
- epics::pvAccess::MonitorRequester::monitorConnect()
- epics::pvAccess::ChannelRPCRequester::channelRPCConnect()
- epics::pvAccess::ChannelPutGetRequester::channelPutGetConnect()
- epics::pvAccess::ChannelProcessRequester::channelProcessConnect()
- epics::pvAccess::ChannelArrayRequester::channelArrayConnect()
When the underlying Channel becomes disconnected or is destroyed,
then the channelDisconnect() method of each Requester is called (eg.
see epics::pvAccess::ChannelBaseRequester::channelDisconnect()
).
All operations are implicitly cancelled/stopped before channelDisconnect() is called.
@subsubsection providers_client_operations_lifetime Operation Lifetime and (dis)connection
An Operation can be created at any time regardless of whether a Channel is connected or not.
An Operation will remain associated with a Channel through (re)connection and disconnection.
@subsubsection providers_client_operations_exec Executing an Operation
After an Operation becomes ready/connected an additional step is necessary to request data.
- epics::pvAccess::ChannelGet::get()
- epics::pvAccess::ChannelPut::get()
- epics::pvAccess::ChannelPut::put()
- epics::pvAccess::ChannelRPC::request()
- epics::pvAccess::ChannelPutGet::putGet()
- epics::pvAccess::ChannelPutGet::getPut()
- epics::pvAccess::ChannelPutGet::getGet()
- epics::pvAccess::ChannelProcess::process()
- epics::pvAccess::ChannelArray::putArray()
- epics::pvAccess::ChannelArray::getArray()
- epics::pvAccess::ChannelArray::getLength()
- epics::pvAccess::ChannelArray::setLength()
Once one of these methods is called to execute an operation,
none may be again until the corresponding completion callback is called,
or the operation is cancel()ed (or epics::pvAccess::Monitor::stop() ).
- epics::pvAccess::ChannelGetRequester::getDone()
- epics::pvAccess::ChannelPutRequester::getDone()
- epics::pvAccess::ChannelPutRequester::putDone()
- epics::pvAccess::ChannelRPCRequester::requestDone()
- epics::pvAccess::ChannelPutGetRequester::putGetDone()
- epics::pvAccess::ChannelPutGetRequester::getPutDone()
- epics::pvAccess::ChannelPutGetRequester::getGetDone()
- epics::pvAccess::ChannelProcessRequester::processDone()
- epics::pvAccess::ChannelArrayRequester::putArrayDone()
- epics::pvAccess::ChannelArrayRequester::getArrayDone()
- epics::pvAccess::ChannelArrayRequester::getLengthDone()
- epics::pvAccess::ChannelArrayRequester::setLengthDone()
@subsubsection providers_client_operations_monitor Monitor Operation
epics::pvAccess::Monitor operations are handled differently than others as
more than one subscription update may be delivered after start() is called.
During or after epics::pvAccess::MonitorRequester::monitorConnect()
it is necessary to call epics::pvAccess::Monitor::start()
to begin receiving subscription updates.
The epics::pvAccess::Monitor::poll() and epics::pvAccess::Monitor::release()
methods access a FIFO queue of subscription updates which have been received.
The epics::pvAccess::MonitorRequester::monitorEvent() method is called
when this FIFO becomes not empty.
@note The pvAccess::MonitorRequester::monitorEvent() is called from a server internal thread
which may be shared with other operations.
In order to avoid delaying other channels/operations
it is recommended to use monitorEvent() as notification for a client
specific worker thread where poll() and release() are called.
epics::pvAccess::MonitorRequester::unlisten() is called to indicate a subscription
has reached a definite end without an error.
Not all subscription sources will use this.
@warning It is critical that any non-NULL MonitorElement returned by poll()
must be passed to release().
Failure to do this will result in a resource leak and possibly stall
the monitor.
See epics::pvAccess::Monitor::Stats::noutstanding
epics::pvAccess::Monitor::getStats() can help diagnose problems related
to the Monitor FIFO.
See epics::pvAccess::Monitor::Stats.
@subsection provides_client_ownership Client Ownership
The following shows the implicit ownership in classes outside the control of client code,
as well as the expected ownerships of of Client user code.
"External" denotes references stored by Client objects which can't participate in reference cycles.
@dotfile client_ownership.dot Client implicit relationships
- Channel holds weak ref. to ChannelProvider
- ChannelProvider holds weak ref. to Channel
- Channel holds weak ref. to ChannelRequester
- Channel holds weak refs to all Operations
- Operations hold weak refs to the corresponding Requester, and Channel
@subsection provides_client_examples Client Examples
- @ref examples_getme
- @ref examples_monitorme
@section providers_server Server Role
*/
#endif /* PROVIDERS_H */

File diff suppressed because it is too large Load Diff

5
documentation/pvtools.h Normal file
View File

@@ -0,0 +1,5 @@
/** @page pvtools Command Line Utilities
*/

3
documentation/readme Normal file
View File

@@ -0,0 +1,3 @@
get libstdc++ tag file from
https://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/libstdc++.tag

View File

@@ -0,0 +1,181 @@
/** @page pvarelease_notes Release Notes
Release 6.0.0 (UNRELEASED)
==========================
- Incompatible changes
- Requires pvDataCPP >=7.0.0 due to headers moved from pvDataCPP into this module: requester.h, destoryable.h, and monitor.h
- Major changes to shared_ptr ownership rules for epics::pvAccess::ChannelProvider and
associated classes. See
- @ref providers
- @ref providers_changes
- Add new library pvAccessIOC for use with PVAClientRegister.dbd and PVAServerRegister.dbd.
Necessary to avoid having pvAccess library depend on all IOC core libraries.
- Added new library pvAccessCA with "ca" provider. Main pvAccess library no longer depends on libca.
Applications needing the "ca" provider must link against pvAccessCA and pvAccess.
See examples/Makefile in the source tree.
The headers associated with this library are: caChannel.h, caProvider.h, and caStatus.h
- A number of headers which were previously installed, but considered "private", are no longer installed.
- epics::pvAccess::ChannelProviderRegistry may no longer be sub-classed.
- Removed access to singleton registry via getChannelProviderRegistry() and registerChannelProviderFactory()
in favor of epics::pvAccess::ChannelProviderRegistry::clients() and epics::pvAccess::ChannelProviderRegistry::servers().
The "pva" and "ca" providers are registered with the clients() singleton.
epics::pvAccess::ServerContext() looks up names with the servers() singleton.
- Removed deprecated epics::pvAccess::Properties
- Simplifications
- use of the epics::pvAccess::ChannelRequester interface is optional
and may be omitted when calling createChannel().
Channel state change notifications are deliviered
to individual operations via epics::pvAccess::ChannelBaseRequester::channelDisconnect()
- Default implementions added for the following methods
- epics::pvAccess::Lockable::lock() and epics::pvAccess::Lockable::unlock() which do nothing.
- epics::pvAccess::Channel::getConnectionState() returns CONNECTED
- epics::pvAccess::Channel::isConnected() uses getConnectionState()
- epics::pvAccess::Channel::getField() which errors
- epics::pvAccess::Channel::getAccessRights() which returns rw
- Added epics::pvAccess::SimpleChannelProviderFactory template and
epics::pvAccess::ChannelProviderRegistry::add() avoids need for custom
factory.
- Added epics::pvAccess::MonitorElement::Ref iterator/smart-pointer
to ensure proper handling of calls Monitor::poll() and Monitor::release().
- epics::pvAccess::PipelineMonitor "internal" is collapsed into epics::pvAccess::Monitor.
PipelineMonitor becomes a typedef for Monitor.
- epics::pvAccess::RPCService is now a sub-class of epics::pvAccess::RPCServiceAsync
- Additions
- pv/pvAccess.h now provides macros OVERRIDE and FINAL which conditionally expand to the c++11 keywords override and final.
- Deliver disconnect notifications to individual Operations (get/put/rpc/monitor/...) via
new epics::pvAccess::ChannelBaseRequester::channelDisconnect()
- New API for server creation via epics::pvAccess::ServerContext::create() allows direct specification
of configuration and ChannelProvider(s).
- Add epics::pvAccess::ServerContext::getCurrentConfig() to get actual configuration, eg. for use with a client.
- Classes from moved headers requester.h, destoryable.h, and monitor.h added to epics::pvAccess namespace.
Typedefs provided in epics::pvData namespace.
- Added Client API based on pvac::ClientProvider
- pv/pvaVersion.h defines VERSION_INT and PVACCESS_VERSION_INT
- epics::pvAccess::RPCClient may be directly constructed.
- epics::pvAccess::RPCServer allows epics::pvAccess::Configuration to be specified and access to ServerContext.
- Added epics::pvAccess::Configuration::keys() to iterate configuration parameters (excluding environment variables).
- Added epics::pvAccess::Destoryable::cleaner
- Deprecations
- epics::pvAccess::GUID in favor of epics::pvAccess::ServerGUID due to win32 name conflict.
Release 5.0.0 (July 2016)
=========================
- Remote channel destroy support
- Multiple network inteface support
- Local multicast (repetitor) reimplemented
- Monitor reconnect when channel type changes fix
- C++11 compilation fixes
- Added version to pvaTools
- Memory management improved
- pipeline: ackAny argument percentage support
- Monitor overrun memory issues fixed
- CA provider destruction fixed
- Replaced LGPL wildcard matcher with simplistic EPICS version
Release 4.1.2 (Oct 2015)
========================
- Improved Jenkins build support
- Removed QtCreated IDE configuration files
- Use of getSubField<> instead of e.g. getDoubleField()
- CA support for pvget, pvput and pvinfo.
- vxWorks/RTEMS compiler warnings resolved.
- Transport shutdown improved.
- CA DBR status fix.
- Monitor queue handling improved.
- Fixed bad performance on 10Gbit or faster networks.
- Async RPC service.
Release 4.0.5 (Dec 2014)
=========================
(Starting point for release notes.)
*/
/** @page providers_changes Changes to ChannelProvider ownership rules
@tableofcontents
Major series 6.x includes changes to the rules for when user code may
store strong and/or weak references to epics::pvAccess::ChannelProvider
and related classes. These rules exist to prevent strong reference loops from forming.
@section providers_changes_requester Operation <-> Requester
One change is the reversal of the allowed relationship between
an Operation and its associated Requester (see @ref provider_roles_requester for definitions).
Prior to 6.0.0 an Operation was required to hold a strong reference to its Requester.
This prevented the Requester from holding a strong ref to the Operation.
This was found to be inconvienent and frequently violated.
Beginning with 6.0.0 an Operation is prohibited from holding a strong reference to its Requester.
@subsection providers_changes_requester_port Porting
Legecy code does not store a strong reference to Requesters will see that they are immediately destory.
An example where the Operation is a ChannelGet and the Requester is ChannelGetRequester.
@code
// Bad example!
epics::pvAccess::Channel::shared_pointer chan = ...;
epics::pvAccess::ChannelGet::shared_pointer get;
{
epics::pvAccess::ChannelGetRequester::shared_pointer req(new MyRequester(...));
get = chan->createChannelGet(req, epics::pvData::createRequest("field()"));
// 'req' is only strong ref.
// MyRequester::~MyRequester() called here
// MyRequester::getDone() never called!
}
@endcode
It is necessary to maintain a strong reference to the ChannelRequester as long as callbacks are desired.
@note Legacy code could be modified to strong each Requester alongside the associated Operation.
New code may utilize the new ownership rules and store the Operation in a custom Requester.
@code
struct MyRequester : public epics::pvAccess::ChannelGetRequester {
epics::pvAccess::ChannelGet::shared_pointer get;
...
};
epics::pvAccess::ChannelGetRequester::shared_pointer req(new MyRequester(...));
epics::pvAccess::Channel::shared_pointer chan = ...;
req->get = chan->createChannelGet(req, epics::pvData::createRequest("field()"));
@endcode
@section providers_changes_store Must store Operation reference
Beginning with 6.0.0, all create methods of
epics::pvAccess::ChannelProvider and
epics::pvAccess::Channel
are required to return shared_ptr which are uniquely ownered by the caller
and are not stored internally.
Thus the caller of a create method must keep a reference to each Operation
or the Operation will be destoryed.
Prior to 6.0.0 some providers, notibly the "pva" provider, did not do this.
It was thus (sometimes) necessary to explicitly call a destory() method
to fully dispose of an Operation.
Failure to do this resulted in a slow resource leak.
Beginning with 6.0.0 an Operation must not rely on user code to call a destory() method.
@subsection providers_changes_store_port Porting
Legecy code may be relying on these internal references to keep an Operation alive.
Beginning with 6.0.0 the shared_ptr returned by any create method must be stored or the Operation will
be immediately destroy'd.
@note Beginning with 6.0.0 use of the epics::pvAccess::ChannelRequester interface is optional
and may be omitted when calling createChannel().
*/

19
examples/Makefile Normal file
View File

@@ -0,0 +1,19 @@
TOP=..
include $(TOP)/configure/CONFIG
PROD_LIBS += pvAccessCA ca pvAccess pvData Com
TESTPROD_HOST += getme
TESTPROD_HOST += putme
TESTPROD_HOST += monitorme
TESTPROD_HOST += spamme
TESTPROD_HOST += miniget
TESTPROD_HOST += miniput
TESTPROD_HOST += minimonitor
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

170
examples/getme.cpp Normal file
View File

@@ -0,0 +1,170 @@
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* pvAccessCPP is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
#include <set>
#include <vector>
#include <string>
#include <exception>
#if !defined(_WIN32)
#include <signal.h>
#define USE_SIGNAL
#endif
#include <epicsEvent.h>
#include <epicsMutex.h>
#include <epicsGuard.h>
//! [Headers]
#include <pv/configuration.h>
#include <pv/caProvider.h>
#include <pva/client.h>
//! [Headers]
namespace pvd = epics::pvData;
namespace pva = epics::pvAccess;
namespace {
epicsEvent done;
#ifdef USE_SIGNAL
void alldone(int num)
{
(void)num;
done.signal();
}
#endif
struct Getter : public pvac::ClientChannel::GetCallback,
public pvac::ClientChannel::ConnectCallback
{
POINTER_DEFINITIONS(Getter);
const std::string name;
pvac::ClientChannel channel;
pvac::Operation op;
Getter(pvac::ClientProvider& provider, const std::string& name)
:name(name)
,channel(provider.connect(name))
{
channel.addConnectListener(this);
}
virtual ~Getter()
{
channel.removeConnectListener(this);
op.cancel();
}
virtual void getDone(const pvac::GetEvent& event)
{
switch(event.event) {
case pvac::GetEvent::Fail:
std::cout<<"Error "<<name<<" : "<<event.message<<"\n";
break;
case pvac::GetEvent::Cancel:
std::cout<<"Cancel "<<name<<"\n";
break;
case pvac::GetEvent::Success:
pvd::PVField::const_shared_pointer valfld(event.value->getSubField("value"));
if(!valfld)
valfld = event.value;
std::cout<<name<<" : "<<*valfld<<"\n";
break;
}
}
virtual void connectEvent(const pvac::ConnectEvent& evt)
{
if(evt.connected) {
op = channel.get(this);
} else {
std::cout<<"Disconnect "<<name<<"\n";
}
}
};
} // namespace
int main(int argc, char *argv[]) {
try {
double waitTime = -1.0;
std::string providerName("pva");
typedef std::vector<std::string> pvs_t;
pvs_t pvs;
for(int i=1; i<argc; i++) {
if(argv[i][0]=='-') {
if(strcmp(argv[i], "-P")==0 || strcmp(argv[i], "--provider")==0) {
if(i<argc-1) {
providerName = argv[++i];
} else {
std::cerr << "--provider requires value\n";
return 1;
}
} else if(strcmp(argv[i], "-T")==0 || strcmp(argv[i], "--timeout")==0) {
if(i<argc-1) {
waitTime = pvd::castUnsafe<double, std::string>(argv[++i]);
} else {
std::cerr << "--timeout requires value\n";
return 1;
}
} else {
std::cerr<<"Unknown argument: "<<argv[i]<<"\n";
}
} else {
pvs.push_back(argv[i]);
}
}
#ifdef USE_SIGNAL
signal(SIGINT, alldone);
signal(SIGTERM, alldone);
signal(SIGQUIT, alldone);
#endif
// build "pvRequest" which asks for all fields
pvd::PVStructure::shared_pointer pvReq(pvd::createRequest("field()"));
// explicitly select configuration from process environment
pva::Configuration::shared_pointer conf(pva::ConfigurationBuilder()
.push_env()
.build());
// "pva" provider automatically in registry
// add "ca" provider to registry
pva::ca::CAClientFactory::start();
std::cout<<"Use provider: "<<providerName<<"\n";
pvac::ClientProvider provider(providerName, conf);
// need to store references to keep get (and channel) from being closed
typedef std::set<Getter::shared_pointer> gets_t;
gets_t gets;
for(pvs_t::const_iterator it=pvs.begin(); it!=pvs.end(); ++it) {
const std::string& pv = *it;
Getter::shared_pointer get(new Getter(provider, pv));
// addConnectListener() always invokes connectEvent() with current state
gets.insert(get);
}
if(waitTime<0.0)
done.wait();
else
done.wait(waitTime);
} catch(std::exception& e){
std::cerr<<"Error: "<<e.what()<<"\n";
return 1;
}
return 0;
}

29
examples/miniget.cpp Normal file
View File

@@ -0,0 +1,29 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
// The simplest possible PVA get
#include <iostream>
#include "pva/client.h"
int main(int argc, char *argv[])
{
try {
if(argc<=1) {
std::cerr<<"Usage: "<<argv[0]<<" <pvname>\n";
return 1;
}
pvac::ClientProvider provider("pva");
pvac::ClientChannel channel(provider.connect(argv[1]));
std::cout<<channel.name()<<" : "<<channel.get()<<"\n";
}catch(std::exception& e){
std::cerr<<"Error: "<<e.what()<<"\n";
return 1;
}
}

103
examples/minimonitor.cpp Normal file
View File

@@ -0,0 +1,103 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
// The simplest possible PVA monitor
#include <iostream>
#if !defined(_WIN32)
#include <signal.h>
#define USE_SIGNAL
#endif
#include <epicsEvent.h>
#include <pva/client.h>
static volatile bool done;
#ifdef USE_SIGNAL
static pvac::MonitorSync * volatile subscription;
static
void handler(int num)
{
(void)num;
done = true;
pvac::MonitorSync *mon = subscription;
if(mon)
mon->wake();
}
#endif
int main(int argc, char *argv[])
{
try {
if(argc<=1) {
std::cerr<<"Usage: "<<argv[0]<<" <pvname>\n";
return 1;
}
pvac::ClientProvider provider("pva");
pvac::ClientChannel channel(provider.connect(argv[1]));
pvac::MonitorSync mon(channel.monitor());
#ifdef USE_SIGNAL
subscription = &mon;
signal(SIGINT, handler);
signal(SIGTERM, handler);
signal(SIGQUIT, handler);
#endif
int ret = 0;
while(!done) {
if(!mon.wait()) // updates mon.event
continue;
switch(mon.event.event) {
// Subscription network/internal error
case pvac::MonitorEvent::Fail:
std::cerr<<mon.name()<<" : Error : "<<mon.event.message<<"\n";
ret = 1;
done = true;
break;
// explicit call of 'mon.cancel' or subscription dropped
case pvac::MonitorEvent::Cancel:
std::cout<<mon.name()<<" <Cancel>\n";
done = true;
break;
// Underlying channel becomes disconnected
case pvac::MonitorEvent::Disconnect:
std::cout<<mon.name()<<" <Disconnect>\n";
break;
// Data queue becomes not-empty
case pvac::MonitorEvent::Data:
// We drain event FIFO completely
while(mon.poll()) {
std::cout<<mon.name()<<" : "<<mon.root;
}
// check to see if more events might be sent
if(mon.complete()) {
done = true;
std::cout<<mon.name()<<" : <Complete>\n";
}
break;
}
}
#ifdef USE_SIGNAL
signal(SIGINT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
subscription = 0;
#endif
return ret;
}catch(std::exception& e){
std::cerr<<"Error: "<<e.what()<<"\n";
return 1;
}
}

33
examples/miniput.cpp Normal file
View File

@@ -0,0 +1,33 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
// The simplest possible PVA put
#include <iostream>
#include "pva/client.h"
int main(int argc, char *argv[])
{
try {
if(argc<=2) {
std::cerr<<"Usage: "<<argv[0]<<" <pvname> <value>\n";
return 1;
}
pvac::ClientProvider provider("pva");
pvac::ClientChannel channel(provider.connect(argv[1]));
std::cout<<"Before "<<channel.name()<<" : "<<channel.get()<<"\n";
channel.putValue<epics::pvData::pvString>(argv[2]);
std::cout<<"After "<<channel.name()<<" : "<<channel.get()<<"\n";
}catch(std::exception& e){
std::cerr<<"Error: "<<e.what()<<"\n";
return 1;
}
}

285
examples/monitorme.cpp Normal file
View File

@@ -0,0 +1,285 @@
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* pvAccessCPP is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
#include <set>
#include <queue>
#include <vector>
#include <string>
#include <exception>
#if !defined(_WIN32)
#include <signal.h>
#define USE_SIGNAL
#endif
#include <epicsEvent.h>
#include <epicsMutex.h>
#include <epicsGuard.h>
#include <pv/configuration.h>
#include <pv/caProvider.h>
#include <pv/thread.h>
#include <pva/client.h>
namespace pvd = epics::pvData;
namespace pva = epics::pvAccess;
namespace {
typedef epicsGuard<epicsMutex> Guard;
typedef epicsGuardRelease<epicsMutex> UnGuard;
struct Worker {
virtual ~Worker() {}
virtual void process(const pvac::MonitorEvent& event) =0;
};
// simple work queue with thread.
// moves monitor queue handling off of PVA thread(s)
struct WorkQueue : public epicsThreadRunable {
epicsMutex mutex;
typedef std::tr1::shared_ptr<Worker> value_type;
typedef std::tr1::weak_ptr<Worker> weak_type;
// work queue holds only weak_ptr
// so jobs must be kept alive seperately
typedef std::deque<std::pair<weak_type, pvac::MonitorEvent> > queue_t;
queue_t queue;
epicsEvent event;
bool running;
pvd::Thread worker;
WorkQueue()
:running(true)
,worker(pvd::Thread::Config()
.name("Monitor handler")
.autostart(true)
.run(this))
{}
~WorkQueue() {close();}
void close()
{
{
Guard G(mutex);
running = false;
}
event.signal();
worker.exitWait();
}
void push(const weak_type& cb, const pvac::MonitorEvent& evt)
{
bool wake;
{
Guard G(mutex);
if(!running) return; // silently refuse to queue during/after close()
wake = queue.empty();
queue.push_back(std::make_pair(cb, evt));
}
if(wake)
event.signal();
}
virtual void run() OVERRIDE FINAL
{
Guard G(mutex);
while(running) {
if(queue.empty()) {
UnGuard U(G);
event.wait();
} else {
queue_t::value_type ent(queue.front());
value_type cb(ent.first.lock());
queue.pop_front();
if(!cb) continue;
try {
UnGuard U(G);
cb->process(ent.second);
}catch(std::exception& e){
std::cout<<"Error in monitor handler : "<<e.what()<<"\n";
}
}
}
}
};
WorkQueue monwork;
epicsMutex mutex;
epicsEvent done;
volatile size_t waitingFor;
#ifdef USE_SIGNAL
void sigdone(int num)
{
(void)num;
waitingFor = 0;
done.signal();
}
#endif
struct MonTracker : public pvac::ClientChannel::MonitorCallback,
public Worker,
public std::tr1::enable_shared_from_this<MonTracker>
{
POINTER_DEFINITIONS(MonTracker);
MonTracker(const std::string& name) :name(name) {}
virtual ~MonTracker() {mon.cancel();}
const std::string name;
pvac::Monitor mon;
virtual void monitorEvent(const pvac::MonitorEvent& evt) OVERRIDE FINAL
{
// shared_from_this() will fail as Cancel is delivered in our dtor.
if(evt.event==pvac::MonitorEvent::Cancel) return;
// running on internal provider worker thread
// minimize work here.
// TODO: bound queue size
monwork.push(shared_from_this(), evt);
}
virtual void process(const pvac::MonitorEvent& evt) OVERRIDE FINAL
{
// running on our worker thread
switch(evt.event) {
case pvac::MonitorEvent::Fail:
std::cout<<"Error "<<name<<" "<<evt.message<<"\n";
break;
case pvac::MonitorEvent::Cancel:
std::cout<<"Cancel "<<name<<"\n";
break;
case pvac::MonitorEvent::Disconnect:
std::cout<<"Disconnect "<<name<<"\n";
break;
case pvac::MonitorEvent::Data:
{
unsigned n;
for(n=0; n<2 && mon.poll(); n++) {
pvd::PVField::const_shared_pointer fld(mon.root->getSubField("value"));
if(!fld)
fld = mon.root;
std::cout<<"Event "<<name<<" "<<fld
<<" Changed:"<<mon.changed
<<" overrun:"<<mon.overrun<<"\n";
}
if(n==2) {
// too many updates, re-queue to balance with others
monwork.push(shared_from_this(), evt);
}
}
break;
}
}
};
} // namespace
int main(int argc, char *argv[]) {
try {
double waitTime = -1.0;
std::string providerName("pva"),
requestStr("field()");
typedef std::vector<std::string> pvs_t;
pvs_t pvs;
for(int i=1; i<argc; i++) {
if(argv[i][0]=='-') {
if(strcmp(argv[i], "-P")==0 || strcmp(argv[i], "--provider")==0) {
if(i<argc-1) {
providerName = argv[++i];
} else {
std::cout << "--provider requires value\n";
return 1;
}
} else if(strcmp(argv[i], "-T")==0 || strcmp(argv[i], "--timeout")==0) {
if(i<argc-1) {
waitTime = pvd::castUnsafe<double, std::string>(argv[++i]);
} else {
std::cout << "--timeout requires value\n";
return 1;
}
} else if(strcmp(argv[i], "-r")==0 || strcmp(argv[i], "--request")==0) {
if(i<argc-1) {
requestStr = argv[++i];
} else {
std::cout << "--request requires value\n";
return 1;
}
} else {
std::cout<<"Unknown argument: "<<argv[i]<<"\n";
}
} else {
pvs.push_back(argv[i]);
}
}
#ifdef USE_SIGNAL
signal(SIGINT, sigdone);
signal(SIGTERM, sigdone);
signal(SIGQUIT, sigdone);
#endif
// build "pvRequest" which asks for all fields
pvd::PVStructure::shared_pointer pvReq(pvd::createRequest(requestStr));
// explicitly select configuration from process environment
pva::Configuration::shared_pointer conf(pva::ConfigurationBuilder()
.push_env()
.build());
// "pva" provider automatically in registry
// add "ca" provider to registry
pva::ca::CAClientFactory::start();
std::cout<<"Use provider: "<<providerName<<"\n";
pvac::ClientProvider provider(providerName, conf);
std::vector<MonTracker::shared_pointer> monitors;
{
Guard G(mutex);
waitingFor = pvs.size();
}
for(pvs_t::const_iterator it=pvs.begin(); it!=pvs.end(); ++it) {
const std::string& pv = *it;
MonTracker::shared_pointer mon(new MonTracker(pv));
pvac::ClientChannel chan(provider.connect(pv));
mon->mon = chan.monitor(mon.get());
monitors.push_back(mon);
}
{
Guard G(mutex);
while(waitingFor) {
UnGuard U(G);
if(waitTime<0.0) {
done.wait();
} else if(!done.wait(waitTime)) {
std::cerr<<"Timeout\n";
break; // timeout
}
}
}
} catch(std::exception& e){
std::cout<<"Error: "<<e.what()<<"\n";
return 1;
}
monwork.close();
return 0;
}

235
examples/putme.cpp Normal file
View File

@@ -0,0 +1,235 @@
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* pvAccessCPP is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
#include <set>
#include <queue>
#include <vector>
#include <string>
#include <exception>
#if !defined(_WIN32)
#include <signal.h>
#define USE_SIGNAL
#endif
#include <epicsEvent.h>
#include <epicsMutex.h>
#include <epicsGuard.h>
#include <epicsGetopt.h>
//! [Headers]
#include <pv/configuration.h>
#include <pv/caProvider.h>
#include <pva/client.h>
//! [Headers]
namespace pvd = epics::pvData;
namespace pva = epics::pvAccess;
namespace {
typedef epicsGuard<epicsMutex> Guard;
typedef epicsGuardRelease<epicsMutex> UnGuard;
epicsMutex mutex;
epicsEvent done;
size_t waitingFor;
#ifdef USE_SIGNAL
void alldone(int num)
{
(void)num;
done.signal();
}
#endif
struct PutTracker : public pvac::ClientChannel::PutCallback
{
POINTER_DEFINITIONS(PutTracker);
pvac::Operation op;
const std::string value;
PutTracker(pvac::ClientChannel& channel,
const pvd::PVStructure::const_shared_pointer& pvReq,
const std::string& value)
:op(channel.put(this, pvReq)) // put() starts here
,value(value)
{}
virtual ~PutTracker()
{
op.cancel();
}
virtual void putBuild(const epics::pvData::StructureConstPtr &build, pvac::ClientChannel::PutCallback::Args& args) OVERRIDE FINAL
{
// At this point we have the user provided value string 'value'
// and the server provided structure (with types).
// note: an exception thrown here will result in putDone() w/ Fail
// allocate a new structure instance.
// we are one-shot so don't bother to re-use
pvd::PVStructurePtr root(pvd::getPVDataCreate()->createPVStructure(build));
// we only know about writes to scalar 'value' field
pvd::PVScalarPtr valfld(root->getSubFieldT<pvd::PVScalar>("value"));
// attempt convert string to actual field type
valfld->putFrom(value);
args.root = root; // non-const -> const
// mark only 'value' field to be sent.
// other fields w/ default values won't be sent.
args.tosend.set(valfld->getFieldOffset());
std::cout<<"Put value "<<valfld<<" sending="<<args.tosend<<"\n";
}
virtual void putDone(const pvac::PutEvent &evt) OVERRIDE FINAL
{
switch(evt.event) {
case pvac::PutEvent::Fail:
std::cerr<<op.name()<<" Error: "<<evt.message<<"\n";
break;
case pvac::PutEvent::Cancel:
std::cerr<<op.name()<<" Cancelled\n";
break;
case pvac::PutEvent::Success:
std::cout<<op.name()<<" Done\n";
}
{
Guard G(mutex);
waitingFor--;
}
done.signal();
}
};
void usage()
{
std::cout<<"Usage: putme [-h] [-P <provider>] [-w <timeout>] [-r <request>] pvname=value ...\n";
}
std::string strip(const std::string& inp)
{
size_t f=inp.find_first_not_of(" \t\n\r"),
l=inp.find_last_not_of (" \t\n\r");
if(f==inp.npos || f>l)
throw std::invalid_argument("Empty string");
return inp.substr(f, l-f+1);
}
} // namespace
int main(int argc, char *argv[]) {
try {
double waitTime = 5.0;
std::string providerName("pva"), request("field()");
int opt;
while( (opt=getopt(argc, argv, "hP:w:r:"))!=-1)
{
switch(opt) {
case 'P':
providerName = optarg;
break;
case 'w':
waitTime = pvd::castUnsafe<double, std::string>(optarg);
break;
case 'r':
request = optarg;
break;
default:
std::cerr<<"Unknown argument "<<opt<<"\n";
/* fall through */
case 'h':
usage();
return 1;
}
}
typedef std::vector<std::pair<std::string, std::string> > args_t;
args_t args;
for(int i=optind; i<argc; i++)
{
std::string arg(argv[i]);
size_t eq = arg.find('=');
if(eq==arg.npos) {
std::cerr<<"Missing '=' in \""<<arg<<"\"\n";
usage();
return 1;
}
std::string pv (strip(arg.substr(0, eq))),
val(strip(arg.substr(eq+1)));
args.push_back(std::make_pair(pv, val));
}
// build "pvRequest" which asks for all fields
pvd::PVStructure::const_shared_pointer pvReq(pvd::createRequest("field()"));
// explicitly select configuration from process environment
pva::Configuration::shared_pointer conf(pva::ConfigurationBuilder()
.push_env()
.build());
// "pva" provider automatically in registry
// add "ca" provider to registry
pva::ca::CAClientFactory::start();
std::cout<<"Use provider: "<<providerName<<"\n";
pvac::ClientProvider provider(providerName, conf);
std::vector<PutTracker::shared_pointer> ops(args.size());
{
Guard G(mutex);
waitingFor = args.size();
}
for(size_t i=0; i<args.size(); i++)
{
args_t::const_reference arg = args[i];
pvac::ClientChannel chan(provider.connect(arg.first));
PutTracker::shared_pointer op(new PutTracker(chan, pvReq, arg.second));
ops.push_back(op);
}
#ifdef USE_SIGNAL
signal(SIGINT, alldone);
signal(SIGTERM, alldone);
signal(SIGQUIT, alldone);
#endif
{
Guard G(mutex);
while(waitingFor) {
UnGuard U(G);
if(waitTime<0.0) {
done.wait();
} else if(!done.wait(waitTime)) {
std::cerr<<"Timeout\n";
break; // timeout
}
}
}
} catch(std::exception& e){
PRINT_EXCEPTION(e);
std::cerr<<"Error: "<<e.what()<<"\n";
return 1;
}
return 0;
}

316
examples/spamme.cpp Normal file
View File

@@ -0,0 +1,316 @@
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* pvAccessCPP is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
#include <deque>
#include <set>
#include <vector>
#include <string>
#include <exception>
#if !defined(_WIN32)
#include <signal.h>
#define USE_SIGNAL
#endif
#include <epicsEvent.h>
#include <epicsThread.h>
#include <epicsMutex.h>
#include <epicsGuard.h>
#include <pv/configuration.h>
#include <pv/pvAccess.h>
#include <pv/serverContext.h>
namespace pvd = epics::pvData;
namespace pva = epics::pvAccess;
namespace {
typedef epicsGuard<epicsMutex> Guard;
epicsEvent done;
#ifdef USE_SIGNAL
void alldone(int num)
{
(void)num;
done.signal();
}
#endif
pvd::Structure::const_shared_pointer spamtype(pvd::getFieldCreate()->createFieldBuilder()
->add("value", pvd::pvInt)
->createStructure());
struct SpamProvider;
struct SpamChannel;
struct SpamMonitor : public pva::Monitor,
public std::tr1::enable_shared_from_this<SpamMonitor>
{
const std::tr1::shared_ptr<SpamChannel> channel;
const requester_type::weak_pointer requester;
pvd::int32 maxQueue;
bool pipeline;
// Has the client seen poll()==NULL
bool clientEmpty;
epicsMutex mutex;
bool running;
epicsUInt32 remoteQueue;
std::deque<epics::pvData::MonitorElementPtr> filled, empty;
pvd::PVStructure::shared_pointer value;
epicsUInt32 counter;
SpamMonitor(const std::tr1::shared_ptr<SpamChannel>& chan,
const pva::MonitorRequester::shared_pointer &requester,
const pvd::PVStructure::shared_pointer &pvRequest)
:channel(chan)
,requester(requester)
,maxQueue(0)
,pipeline(false)
,clientEmpty(true)
,running(false)
,remoteQueue(0)
,counter(0)
{
pvd::PVScalar::shared_pointer fld;
fld = pvRequest->getSubField<pvd::PVScalar>("record._options.queueSize");
if(fld)
maxQueue = fld->getAs<pvd::int32>();
if(maxQueue<3)
maxQueue = 3;
fld = pvRequest->getSubField<pvd::PVScalar>("record._options.pipeline");
if(fld)
pipeline = fld->getAs<pvd::boolean>();
pvd::PVDataCreatePtr create(pvd::getPVDataCreate());
value = create->createPVStructure(spamtype);
for(pvd::int32 i=0; i<maxQueue; i++)
{
pvd::MonitorElementPtr elem(new pvd::MonitorElement(create->createPVStructure(spamtype)));
empty.push_back(elem);
}
}
virtual ~SpamMonitor() {}
virtual void destroy() OVERRIDE FINAL {(void)stop();}
virtual pvd::Status start() OVERRIDE FINAL
{
{
Guard G(mutex);
running = true;
clientEmpty = true;
}
pushall();
return pvd::Status::Ok;
}
virtual pvd::Status stop() OVERRIDE FINAL
{
{
Guard G(mutex);
running = false;
}
return pvd::Status::Ok;
}
virtual pva::MonitorElementPtr poll() OVERRIDE FINAL
{
Guard G(mutex);
pva::MonitorElementPtr ret;
if(!filled.empty()) {
ret = filled.front();
filled.pop_front();
}
clientEmpty = !ret;
return ret;
}
virtual void release(const pva::MonitorElementPtr& elem) OVERRIDE FINAL
{
if(elem->pvStructurePtr->getField().get()!=spamtype.get())
return;
{
Guard G(mutex);
empty.push_back(elem);
}
pushall();
}
virtual void reportRemoteQueueStatus(pvd::int32 freeElements) OVERRIDE FINAL
{
{
Guard G(mutex);
remoteQueue += freeElements;
}
pushall();
}
void pushall()
{
bool signal;
{
Guard G(mutex);
signal = clientEmpty && filled.empty();
while(!empty.empty() && (!pipeline || remoteQueue>0)) {
pva::MonitorElementPtr elem(empty.front());
pvd::PVIntPtr fld(value->getSubFieldT<pvd::PVInt>("value"));
fld->put(counter++);
elem->pvStructurePtr->copyUnchecked(*value);
elem->changedBitSet->clear();
elem->changedBitSet->set(0);
elem->overrunBitSet->clear();
filled.push_back(elem);
empty.pop_front();
remoteQueue--;
}
signal &= !filled.empty();
if(signal)
clientEmpty = false;
}
if(signal) {
requester_type::shared_pointer req(requester.lock());
if(req)
req->monitorEvent(shared_from_this());
}
}
};
struct SpamChannel : public pva::Channel,
public std::tr1::enable_shared_from_this<SpamChannel>
{
const std::tr1::shared_ptr<pva::ChannelProvider> provider;
const std::string name;
const pva::ChannelRequester::weak_pointer requester;
SpamChannel(const std::tr1::shared_ptr<pva::ChannelProvider>& provider,
const std::string& name,
const pva::ChannelRequester::shared_pointer& requester)
:provider(provider)
,name(name)
,requester(requester)
{}
virtual ~SpamChannel() {}
virtual std::string getChannelName() OVERRIDE FINAL {return name;}
virtual std::string getRemoteAddress() OVERRIDE FINAL {return "";}
virtual ConnectionState getConnectionState() OVERRIDE FINAL {return CONNECTED;}
virtual pva::ChannelRequester::shared_pointer getChannelRequester() OVERRIDE FINAL { return pva::ChannelRequester::shared_pointer(requester); }
virtual void destroy() OVERRIDE FINAL {}
virtual std::tr1::shared_ptr<pva::ChannelProvider> getProvider() OVERRIDE FINAL {return provider;}
virtual pva::AccessRights getAccessRights(pvd::PVField::shared_pointer const & pvField) OVERRIDE FINAL
{
return pva::readWrite;
}
virtual void getField(pva::GetFieldRequester::shared_pointer const & requester,std::string const & subField) OVERRIDE FINAL
{
requester->getDone(pvd::Status::Ok, spamtype);
}
virtual pva::Monitor::shared_pointer createMonitor(const pva::MonitorRequester::shared_pointer &requester,
const pvd::PVStructure::shared_pointer &pvRequest) OVERRIDE FINAL
{
std::tr1::shared_ptr<SpamMonitor> ret(new SpamMonitor(shared_from_this(), requester, pvRequest));
requester->monitorConnect(pvd::Status::Ok, ret, spamtype);
return ret;
}
};
struct SpamProvider : public pva::ChannelProvider,
public pva::ChannelFind,
public std::tr1::enable_shared_from_this<SpamProvider>
{
const std::string channelName;
SpamProvider(const std::string& name) :channelName(name) {}
virtual ~SpamProvider() {}
virtual std::string getProviderName() OVERRIDE FINAL {return "SpamProvider";}
virtual void destroy() OVERRIDE FINAL {}
virtual pva::ChannelFind::shared_pointer channelFind(std::string const & name,
pva::ChannelFindRequester::shared_pointer const & requester) OVERRIDE FINAL
{
std::cerr<<"XXX "<<name<<"\n";
pva::ChannelFind::shared_pointer ret;
if(name==this->channelName) {
ret = shared_from_this();
}
std::cout<<__FUNCTION__<<" "<<name<<" found="<<!!ret<<"\n";
requester->channelFindResult(pvd::Status::Ok, ret, !!ret);
return ret;
}
virtual std::tr1::shared_ptr<pva::ChannelProvider> getChannelProvider() OVERRIDE FINAL { return shared_from_this(); }
virtual void cancel() OVERRIDE FINAL {}
virtual pva::Channel::shared_pointer createChannel(std::string const & name,
pva::ChannelRequester::shared_pointer const & requester,
short priority, std::string const & address) OVERRIDE FINAL
{
std::tr1::shared_ptr<SpamChannel> ret;
if(name==channelName) {
ret.reset(new SpamChannel(shared_from_this(), channelName, requester));
}
std::cout<<__FUNCTION__<<" "<<name<<" connect "<<ret.get()<<"\n";
requester->channelCreated(ret ? pvd::Status::Ok : pvd::Status::error(""), ret);
return ret;
}
};
} // namespace
int main(int argc, char *argv[]) {
try {
std::tr1::shared_ptr<SpamProvider> provider(new SpamProvider("spam"));
pva::ServerContext::shared_pointer server(pva::ServerContext::create(pva::ServerContext::Config()
.provider(provider)
.config(pva::ConfigurationBuilder()
.push_env()
.build())));
std::cout<<"Server use_count="<<server.use_count()<<" provider use_count="<<provider.use_count()<<"\n";
#ifdef USE_SIGNAL
signal(SIGINT, alldone);
signal(SIGTERM, alldone);
signal(SIGQUIT, alldone);
#endif
server->printInfo();
std::cout<<"Waiting\n";
done.wait();
std::cout<<"Done\n";
std::cout<<"Server use_count="<<server.use_count()<<"\n"
<<show_referrers(server, false);
server.reset();
std::cout<<"threads\n";
std::cout.flush();
epicsThreadShowAll(0);
std::cout<<"provider use_count="<<provider.use_count()<<"\n"
<<show_referrers(provider, false);
if(!provider.unique())
return 2;
} catch(std::exception& e){
std::cerr<<"Error: "<<e.what()<<"\n";
return 1;
}
return 0;
}

92
notes.md.txt Normal file
View File

@@ -0,0 +1,92 @@
changes
must keep ChannelProvider refs
Channel doesn't have strong ref to provider.
All channels closed when provider destory'd
default ChannelProviderFactory uses weak_ptr for sharedInstance()
copy+paste from eget.cpp very inefficient...
TODO
ca provider changes
channel has weak_ref to provider
create operations on disconnected channel
monitor queue
untangle responseHandlers.cpp
ref. loops galore
destroy which queued to send???
maps which grow w/o bound
named lock pattern
m_beaconHandlers
locking
no internal locks held when calling *Requester methods, including dtor
any external locks may be held when channel/operation methods called
provider->createChannel()
* returns unique() reference.
* Provider must maintain a cache of existing Channels are prefer this to creating new Channels
calls to channel->createChannel*
* returns unique() reference
* Channel retains internal references to Operation until destroy() is called (or all external references released)
* request on dis-connected channel queues until connected
Add channelStateChanged() to all operation Requesters
* called only with DISCONNECTED (channel connected -> disconnected) or DESTROYED (channel->destroy() called).
* Any in-progress action is implicitly cancel()'d
After some considerable thought, I like to re-define some of the semantics of ChannelProvider, Channel, and operations (eg. ChannelGet).
1. User/caller code takes ownership of returned Channel or operation.
For a Channel to remain open, or an operation to remain active, the caller must hold a reference. This will prevent accumulation of channels and operations which user code isn't accounting for.
The suggested way to accomplish this is for a ChannelProvider to return a shared_ptr<> with a custom cleanup function which calls destroy().
1.1 returned shared_ptr<Channel> must not include an internal references. That is, the first time a Channel is returned it is unique(). Subsequent calls to createChannel() with the same arguments may return unique()==false provided that this count includes only external references.
1.2 returned operation shared_ptr<> must be unique().
2. User/caller code need not cache Channel instances.
Every non-trivial client worries about minimizing the number of Channels.
2.1 ChannelProvider is required to maintain a cache of in-use Channels and prefer to return a cached entry before creating a new Channel.
3 Notify operations about Channel state changes
channelStateChanged() by itself isn't so useful. Clients always proxy this through to some action of each in-progress operation. So have the Channel do this.
3.1 Add a new method channelStateChanged() to all operation Requester classes. Default implementation is a no-op. Only DISCONNECTED and DESTROYED shall be used (CONNECT is delivered as a separate callback *Connect() ).
3.2 When DISCONNECTED is delivered the operation remains "valid" and its *Connect() will be called (again) if/when the Channel again becomes connected
3.3 When DESTROYED is delivered, the underlying Channel has been forcibly closed.
3.3 Delivery of DISCONNECTED or DESTROYED implicitly cancels any in-progress action.
4 Operation life-time
4.1 Channel must support starting operations while in disconnected state
4.2 Operations persist when a Channel becomes DISCONNECTED. On re-connect, the operation *Connect() method is called again, potentially with new Structure definition.

View File

@@ -2,26 +2,26 @@ TOP=..
include $(TOP)/configure/CONFIG
USR_CPPFLAGS += -I$(TOP)/src/remote
PROD_HOST += pvget
pvget_SRCS += pvget.cpp
pvget_LIBS += pvAccess pvData $(MBLIB) ca Com
PROD_HOST += pvput
pvput_SRCS += pvput.cpp
pvput_LIBS += pvAccess pvData $(MBLIB) ca Com
PROD_HOST += pvinfo
pvinfo_SRCS += pvinfo.cpp
pvinfo_LIBS += pvAccess pvData $(MBLIB) ca Com
PROD_HOST += pvlist
pvlist_SRCS += pvlist.cpp
pvlist_LIBS += pvAccess pvData $(MBLIB) Com
pvlist_SYS_LIBS_WIN32 += ws2_32
PROD_HOST += eget
eget_SRCS += eget.cpp
eget_LIBS += pvAccess pvData $(MBLIB) ca Com
PROD_LIBS += pvAccessCA pvAccess pvData $(MBLIB) ca Com
PROD_SYS_LIBS_WIN32 += ws2_32
include $(TOP)/configure/RULES
#----------------------------------------

View File

@@ -3,7 +3,6 @@
#endif
#include <iostream>
#include <pv/clientFactory.h>
#include <pv/pvAccess.h>
#include <pv/caProvider.h>
@@ -1096,11 +1095,6 @@ public:
return "ChannelGetRequesterImpl";
}
virtual void message(std::string const & message, MessageType messageType)
{
std::cerr << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")" << std::endl;
}
virtual void channelGetConnect(const epics::pvData::Status& status,
ChannelGet::shared_pointer const & channelGet,
epics::pvData::Structure::const_shared_pointer const & /*structure*/)
@@ -1204,11 +1198,6 @@ public:
return "ChannelRPCRequesterImpl";
}
virtual void message(std::string const & message, MessageType messageType)
{
std::cerr << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")" << std::endl;
}
virtual void channelRPCConnect(const epics::pvData::Status& status, ChannelRPC::shared_pointer const & /*channelRPC*/)
{
if (status.isSuccess())
@@ -1323,11 +1312,6 @@ public:
virtual string getRequesterName()
{
return "MonitorRequesterImpl";
};
virtual void message(std::string const & message,MessageType messageType)
{
std::cerr << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")" << std::endl;
}
virtual void monitorConnect(const epics::pvData::Status& status, Monitor::shared_pointer const & monitor, StructureConstPtr const & /*structure*/)
@@ -1355,6 +1339,13 @@ public:
}
}
virtual void channelDisconnect(bool destroy)
{
if(!destroy)
std::cerr << std::setw(30) << std::left << m_channelName
<< ' ' << "*** disconnected" << std::endl;
}
virtual void monitorEvent(Monitor::shared_pointer const & monitor)
{
@@ -1464,7 +1455,6 @@ int main (int argc, char *argv[])
//string urlEncodedRequest;
vector< pair<string,string> > parameters;
bool monitor = false;
bool quiet = false;
string defaultProvider = DEFAULT_PROVIDER;
setvbuf(stdout,NULL,_IOLBF,BUFSIZ); /* Set stdout to line buffering */
@@ -1578,7 +1568,6 @@ int main (int argc, char *argv[])
break;
case 'q': /* Quiet mode */
quiet = true;
break;
case 'd': /* Debug log level */
debug = true;
@@ -1664,8 +1653,6 @@ int main (int argc, char *argv[])
bool allOK = true;
Requester::shared_pointer requester(new RequesterImpl("eget"));
// parse URI
// try to parse as URI if only one nPvs
URI uri;
@@ -1682,14 +1669,16 @@ int main (int argc, char *argv[])
serviceRequest = true;
}
static string noAddress;
// register "ca" provider
epics::pvAccess::ca::CAClientFactory::start();
// PVs mode
if (!serviceRequest)
{
vector<string> pvs;
vector<string> pvsAddress;
vector<string> providerNames;
vector<ChannelProvider::shared_pointer> providers;
vector<epics::pvAccess::Destroyable::shared_pointer> operations;
if (validURI)
{
@@ -1713,26 +1702,38 @@ int main (int argc, char *argv[])
// skip trailing '/'
pvs.push_back(uri.path.substr(1));
pvsAddress.push_back(uri.host);
providerNames.push_back(uri.protocol);
providers.push_back(ChannelProviderRegistry::clients()->getProvider(uri.protocol));
if(!providers.back()) {
std::cerr<<"Unknown provider \""<<uri.protocol<<"\" for \""<<pvs.back()<<"\n";
allOK = false;
}
}
else
{
for (int n = 0; optind < argc; n++, optind++)
std::vector<std::string> uris;
if(!fromStream) {
for (int n = 0; optind < argc; n++, optind++)
{
uris.push_back(argv[optind]);
}
} else {
string cn;
while (true)
{
*inputStream >> cn;
if (!(*inputStream))
break;
uris.push_back(cn);
}
}
for (size_t n = 0; n<uris.size(); n++)
{
URI uri;
bool validURI = URI::parse(argv[optind], uri);
bool validURI = URI::parse(uris[n], uri);
if (validURI)
{
// TODO this is copy&pase code from above, clean it up
// for now no only pva/ca schema is supported, without authority
// TODO
if (uri.protocol != "pva" && uri.protocol != "ca")
{
std::cerr << "invalid URI scheme '" << uri.protocol << "', only 'pva' and 'ca' are supported" << std::endl;
// TODO
return 1;
}
if (uri.path.length() <= 1)
{
std::cerr << "invalid URI, empty path" << std::endl;
@@ -1743,17 +1744,25 @@ int main (int argc, char *argv[])
// skip trailing '/'
pvs.push_back(uri.path.substr(1));
pvsAddress.push_back(uri.host);
providerNames.push_back(uri.protocol);
providers.push_back(ChannelProviderRegistry::clients()->getProvider(uri.protocol));
}
else
{
pvs.push_back(argv[optind]);
pvsAddress.push_back(noAddress);
providerNames.push_back(defaultProvider);
uri.protocol = defaultProvider;
pvs.push_back(uris[n]);
pvsAddress.push_back(std::string());
providers.push_back(ChannelProviderRegistry::clients()->getProvider(defaultProvider));
}
if(!providers.back()) {
std::cerr<<"Unknown provider \""<<uri.protocol<<"\" for \""<<pvs.back()<<"\"\n";
allOK = false;
}
}
}
nPvs = pvs.size();
PVStructure::shared_pointer pvRequest =
CreateRequest::create()->createRequest(request);
if(pvRequest.get()==0) {
@@ -1761,20 +1770,17 @@ int main (int argc, char *argv[])
return 1;
}
// register "pva" and "ca" providers
ClientFactory::start();
epics::pvAccess::ca::CAClientFactory::start();
// first connect to all, this allows resource (e.g. TCP connection) sharing
vector<Channel::shared_pointer> channels(nPvs);
for (int n = 0; n < nPvs; n++)
{
TR1::shared_ptr<ChannelRequesterImpl> channelRequesterImpl(new ChannelRequesterImpl(quiet));
if (pvsAddress[n].empty())
channels[n] = getChannelProviderRegistry()->getProvider(providerNames[n])->createChannel(pvs[n], channelRequesterImpl);
else
channels[n] = getChannelProviderRegistry()->getProvider(providerNames[n])->createChannel(pvs[n], channelRequesterImpl,
ChannelProvider::PRIORITY_DEFAULT, pvsAddress[n]);
if(!providers[n]) continue;
channels[n] = providers[n]->createChannel(pvs[n], DefaultChannelRequester::build(),
ChannelProvider::PRIORITY_DEFAULT, pvsAddress[n]);
if(!channels[n]) {
std::cerr<<"No such channel '"<<pvs[n]<<"'\n";
}
}
// TODO maybe unify for nPvs == 1?!
@@ -1790,160 +1796,74 @@ int main (int argc, char *argv[])
}
// for now a simple iterating sync implementation, guarantees order
int n = -1;
while (true)
for (int n = 0; n < nPvs; n++)
{
Channel::shared_pointer channel;
Channel::shared_pointer channel(channels[n]);
if (!fromStream)
{
if (++n >= nPvs)
break;
channel = channels[n];
}
else
{
string cn;
string ca;
string cp;
// read next channel name from stream
*inputStream >> cn;
if (!(*inputStream))
break;
URI uri;
bool validURI = URI::parse(cn.c_str(), uri);
if (validURI)
{
// TODO this is copy&pase code from above, clean it up
// for now no only pva/ca schema is supported, without authority
// TODO
if (uri.protocol != "pva" && uri.protocol != "ca")
{
std::cerr << "invalid URI scheme '" << uri.protocol << "', only 'pva' and 'ca' are supported" << std::endl;
// TODO
return 1;
}
if (uri.path.length() <= 1)
{
std::cerr << "invalid URI, empty path" << std::endl;
// TODO
return 1;
}
// skip trailing '/'
cn = uri.path.substr(1);
ca = uri.host;
cp = uri.protocol;
}
else
{
// leave cn as it is, use default provider
ca = noAddress;
cp = defaultProvider;
}
TR1::shared_ptr<ChannelRequesterImpl> channelRequesterImpl(new ChannelRequesterImpl(quiet));
if (ca.empty())
channel = getChannelProviderRegistry()->getProvider(cp)->createChannel(cn, channelRequesterImpl);
else
channel = getChannelProviderRegistry()->getProvider(cp)->createChannel(cn, channelRequesterImpl,
ChannelProvider::PRIORITY_DEFAULT, ca);
if(!channel) {
allOK = false;
continue;
}
if (monitor)
{
TR1::shared_ptr<ChannelRequesterImpl> channelRequesterImpl = TR1::dynamic_pointer_cast<ChannelRequesterImpl>(channel->getChannelRequester());
channelRequesterImpl->showDisconnectMessage();
// TODO remove this line, when CA provider will allow creation of monitors
// when channels is yet not connected
if (channelRequesterImpl->waitUntilConnected(timeOut))
{
TR1::shared_ptr<MonitorRequesterImpl> monitorRequesterImpl(new MonitorRequesterImpl(channel->getChannelName()));
channel->createMonitor(monitorRequesterImpl, pvRequest);
}
else
{
allOK = false;
channel->destroy();
std::cerr << "[" << channel->getChannelName() << "] connection timeout" << std::endl;
}
TR1::shared_ptr<MonitorRequesterImpl> monitorRequesterImpl(new MonitorRequesterImpl(channel->getChannelName()));
operations.push_back(channel->createMonitor(monitorRequesterImpl, pvRequest));
}
else
{
/*
TR1::shared_ptr<ChannelRequesterImpl> channelRequesterImpl(new ChannelRequesterImpl());
Channel::shared_pointer channel = provider->createChannel(pvs[n], channelRequesterImpl);
*/
TR1::shared_ptr<GetFieldRequesterImpl> getFieldRequesterImpl;
TR1::shared_ptr<ChannelRequesterImpl> channelRequesterImpl = TR1::dynamic_pointer_cast<ChannelRequesterImpl>(channel->getChannelRequester());
if (channelRequesterImpl->waitUntilConnected(timeOut))
// probe for value field
// but only if there is only one PV request (otherwise mode change makes a mess)
if (mode == ValueOnlyMode && nPvs == 1)
{
TR1::shared_ptr<GetFieldRequesterImpl> getFieldRequesterImpl;
getFieldRequesterImpl.reset(new GetFieldRequesterImpl(channel));
// get all to be immune to bad clients not supporting selective getField request
channel->getField(getFieldRequesterImpl, "");
}
// probe for value field
// but only if there is only one PV request (otherwise mode change makes a mess)
if (mode == ValueOnlyMode && nPvs == 1)
if (getFieldRequesterImpl.get() == 0 ||
getFieldRequesterImpl->waitUntilFieldGet(timeOut))
{
// check probe
if (getFieldRequesterImpl.get())
{
getFieldRequesterImpl.reset(new GetFieldRequesterImpl(channel));
// get all to be immune to bad clients not supporting selective getField request
channel->getField(getFieldRequesterImpl, "");
}
if (getFieldRequesterImpl.get() == 0 ||
getFieldRequesterImpl->waitUntilFieldGet(timeOut))
{
// check probe
if (getFieldRequesterImpl.get())
Structure::const_shared_pointer structure =
TR1::dynamic_pointer_cast<const Structure>(getFieldRequesterImpl->getField());
if (structure.get() == 0 || structure->getField("value").get() == 0)
{
Structure::const_shared_pointer structure =
TR1::dynamic_pointer_cast<const Structure>(getFieldRequesterImpl->getField());
if (structure.get() == 0 || structure->getField("value").get() == 0)
{
// fallback to structure
mode = StructureMode;
pvRequest = CreateRequest::create()->createRequest("field()");
}
}
TR1::shared_ptr<ChannelGetRequesterImpl> getRequesterImpl(
new ChannelGetRequesterImpl(channel->getChannelName(), false)
);
ChannelGet::shared_pointer channelGet = channel->createChannelGet(getRequesterImpl, pvRequest);
bool ok = getRequesterImpl->waitUntilGet(timeOut);
allOK &= ok;
if (ok)
{
if (collectValues)
{
collectedValues.push_back(getRequesterImpl->getPVStructure());
collectedNames.push_back(channel->getChannelName());
}
else
{
// print immediately
printValue(channel->getChannelName(), getRequesterImpl->getPVStructure(), fromStream);
}
// fallback to structure
mode = StructureMode;
pvRequest = CreateRequest::create()->createRequest("field()");
}
}
else
TR1::shared_ptr<ChannelGetRequesterImpl> getRequesterImpl(
new ChannelGetRequesterImpl(channel->getChannelName(), false)
);
ChannelGet::shared_pointer channelGet = channel->createChannelGet(getRequesterImpl, pvRequest);
bool ok = getRequesterImpl->waitUntilGet(timeOut);
allOK &= ok;
if (ok)
{
allOK = false;
channel->destroy();
std::cerr << "[" << channel->getChannelName() << "] failed to get channel introspection data" << std::endl;
if (collectValues)
{
collectedValues.push_back(getRequesterImpl->getPVStructure());
collectedNames.push_back(channel->getChannelName());
}
else
{
// print immediately
printValue(channel->getChannelName(), getRequesterImpl->getPVStructure(), fromStream);
}
}
}
else
{
allOK = false;
channel->destroy();
std::cerr << "[" << channel->getChannelName() << "] connection timeout" << std::endl;
std::cerr << "[" << channel->getChannelName() << "] failed to get channel introspection data" << std::endl;
}
}
}
@@ -1956,9 +1876,6 @@ int main (int argc, char *argv[])
while (true)
epicsThreadSleep(timeOut);
}
epics::pvAccess::ca::CAClientFactory::stop();
ClientFactory::stop();
}
// service RPC mode
else
@@ -2098,58 +2015,45 @@ int main (int argc, char *argv[])
}
ClientFactory::start();
ChannelProvider::shared_pointer provider = getChannelProviderRegistry()->getProvider("pva");
ChannelProvider::shared_pointer provider = ChannelProviderRegistry::clients()->getProvider("pva");
assert(provider);
TR1::shared_ptr<ChannelRequesterImpl> channelRequesterImpl(new ChannelRequesterImpl(quiet));
Channel::shared_pointer channel =
authority.empty() ?
provider->createChannel(service, channelRequesterImpl) :
provider->createChannel(service, channelRequesterImpl,
provider->createChannel(service, DefaultChannelRequester::build(),
ChannelProvider::PRIORITY_DEFAULT, authority);
if (channelRequesterImpl->waitUntilConnected(timeOut))
{
TR1::shared_ptr<ChannelRPCRequesterImpl> rpcRequesterImpl(new ChannelRPCRequesterImpl(channel->getChannelName()));
ChannelRPC::shared_pointer channelRPC = channel->createChannelRPC(rpcRequesterImpl, pvRequest);
TR1::shared_ptr<ChannelRPCRequesterImpl> rpcRequesterImpl(new ChannelRPCRequesterImpl(channel->getChannelName()));
ChannelRPC::shared_pointer channelRPC = channel->createChannelRPC(rpcRequesterImpl, pvRequest);
if (rpcRequesterImpl->waitUntilConnected(timeOut))
if (rpcRequesterImpl->waitUntilConnected(timeOut))
{
channelRPC->lastRequest();
channelRPC->request(arg);
allOK &= rpcRequesterImpl->waitUntilRPC(timeOut);
if (allOK)
{
channelRPC->lastRequest();
channelRPC->request(arg);
allOK &= rpcRequesterImpl->waitUntilRPC(timeOut);
if (allOK)
if (dumpStructure)
{
if (dumpStructure)
{
if (rpcRequesterImpl->getLastResponse().get() == 0)
std::cout << "(null)" << std::endl;
else
{
//std::cout << *(rpcRequesterImpl->getLastResponse().get()) << std::endl;
pvutil_ostream myos(std::cout.rdbuf());
myos << *(rpcRequesterImpl->getLastResponse().get()) << std::endl;
}
}
if (rpcRequesterImpl->getLastResponse().get() == 0)
std::cout << "(null)" << std::endl;
else
formatNT(std::cout, rpcRequesterImpl->getLastResponse());
std::cout << std::endl;
{
//std::cout << *(rpcRequesterImpl->getLastResponse().get()) << std::endl;
pvutil_ostream myos(std::cout.rdbuf());
myos << *(rpcRequesterImpl->getLastResponse().get()) << std::endl;
}
}
}
else
{
allOK = false;
else
formatNT(std::cout, rpcRequesterImpl->getLastResponse());
std::cout << std::endl;
}
}
else
{
allOK = false;
std::cerr << "[" << channel->getChannelName() << "] connection timeout" << std::endl;
}
channel->destroy();
ClientFactory::stop();
}
if (cleanupAndReport)

View File

@@ -1,26 +1,31 @@
#include <iostream>
#include <pv/clientFactory.h>
#include <pv/pvAccess.h>
#include <stdio.h>
#include <epicsStdlib.h>
#include <epicsGetopt.h>
#include <epicsThread.h>
#include <pv/logger.h>
#include <pv/lock.h>
#include <vector>
#include <set>
#include <string>
#include <istream>
#include <fstream>
#include <sstream>
#include <pv/event.h>
#include <epicsExit.h>
#include <stdio.h>
#include "pvutils.cpp"
#if !defined(_WIN32)
#include <signal.h>
#define USE_SIGNAL
#endif
#include <epicsStdlib.h>
#include <epicsGetopt.h>
#include <epicsExit.h>
#include <epicsGuard.h>
#include <pv/caProvider.h>
#include <pv/pvAccess.h>
#include <epicsThread.h>
#include <pv/logger.h>
#include <pv/lock.h>
#include <pv/event.h>
#include "pvutils.cpp"
using namespace std;
namespace TR1 = std::tr1;
@@ -29,17 +34,15 @@ using namespace epics::pvAccess;
typedef epicsGuard<epicsMutex> Guard;
typedef epicsGuardRelease<epicsMutex> UnGuard;
namespace {
bool debugFlag = false;
#define DEFAULT_TIMEOUT 3.0
#define DEFAULT_REQUEST "field(value)"
#define DEFAULT_PROVIDER "pva"
double timeOut = DEFAULT_TIMEOUT;
string request(DEFAULT_REQUEST);
string defaultProvider(DEFAULT_PROVIDER);
const string noAddress;
string request("field(value)");
string defaultProvider("pva");
enum PrintMode { ValueOnlyMode, StructureMode, TerseMode };
PrintMode mode = ValueOnlyMode;
@@ -53,7 +56,7 @@ void usage (void)
" -v: Print version and exit\n"
"\noptions:\n"
" -r <pv request>: Request, specifies what fields to return and options, default is '%s'\n"
" -w <sec>: Wait time, specifies timeout, default is %f second(s)\n"
" -w <sec>: Wait time, specifies timeout, default is 3 seconds for get, inf. for monitor\n"
" -t: Terse mode - print only value, without names\n"
" -i: Do not format standard types (enum_t, time_t, ...)\n"
" -m: Monitor mode\n"
@@ -62,13 +65,12 @@ void usage (void)
" -d: Enable debug output\n"
" -F <ofs>: Use <ofs> as an alternate output field separator\n"
" -f <input file>: Use <input file> as an input that provides a list PV name(s) to be read, use '-' for stdin\n"
" -c: Wait for clean shutdown and report used instance count (for expert users)\n"
" enum format:\n"
" -n: Force enum interpretation of values as numbers (default is enum string)\n"
// " time format:\n"
// " -u: print userTag\n"
"\nexample: pvget double01\n\n"
, DEFAULT_REQUEST, DEFAULT_TIMEOUT, DEFAULT_PROVIDER);
, request.c_str(), defaultProvider.c_str());
}
void printValue(std::string const & channelName, PVStructure::shared_pointer const & pv)
@@ -78,10 +80,9 @@ void printValue(std::string const & channelName, PVStructure::shared_pointer con
PVField::shared_pointer value = pv->getSubField("value");
if (value.get() == 0)
{
std::cerr << "no 'value' field" << std::endl;
//std::cout << channelName << std::endl << *(pv.get()) << std::endl << std::endl;
std::cerr << "no 'value' field\n";
pvutil_ostream myos(std::cout.rdbuf());
myos << channelName << std::endl << *(pv.get()) << std::endl << std::endl;
myos << channelName << "\n" << *(pv.get()) << "\n\n";
}
else
{
@@ -94,13 +95,12 @@ void printValue(std::string const & channelName, PVStructure::shared_pointer con
std::cout << std::setw(30) << std::left << channelName;
std::cout << fieldSeparator;
formatTType(std::cout, TR1::static_pointer_cast<PVStructure>(value));
std::cout << std::endl;
std::cout << '\n';
}
else
{
//std::cout << channelName << std::endl << *(pv.get()) << std::endl << std::endl;
pvutil_ostream myos(std::cout.rdbuf());
myos << channelName << std::endl << *(pv.get()) << std::endl << std::endl;
myos << channelName << '\n' << *(pv.get()) << "\n\n";
}
}
else
@@ -112,55 +112,79 @@ void printValue(std::string const & channelName, PVStructure::shared_pointer con
std::cout << fieldSeparator;
terse(std::cout, value) << std::endl;
terse(std::cout, value) << '\n';
}
}
}
else if (mode == TerseMode)
terseStructure(std::cout, pv) << std::endl;
terseStructure(std::cout, pv) << '\n';
else
{
//std::cout << channelName << std::endl << *(pv.get()) << std::endl << std::endl;
pvutil_ostream myos(std::cout.rdbuf());
myos << channelName << std::endl << *(pv.get()) << std::endl << std::endl;
myos << channelName << '\n' << *(pv.get()) << "\n\n";
}
}
// tracking get and monitor operations in progress
class ChannelGetRequesterImpl : public ChannelGetRequester
struct Tracker {
static epicsMutex doneLock;
static epicsEvent doneEvt;
typedef std::set<Tracker*> inprog_t;
static inprog_t inprog;
static bool abort;
Tracker()
{
Guard G(doneLock);
inprog.insert(this);
}
~Tracker()
{
done();
}
void done()
{
{
Guard G(doneLock);
inprog.erase(this);
}
doneEvt.signal();
}
};
epicsMutex Tracker::doneLock;
epicsEvent Tracker::doneEvt;
Tracker::inprog_t Tracker::inprog;
bool Tracker::abort = false;
#ifdef USE_SIGNAL
void alldone(int num)
{
private:
PVStructure::shared_pointer m_pvStructure;
BitSet::shared_pointer m_bitSet;
Mutex m_pointerMutex;
Event m_event;
string m_channelName;
(void)num;
Tracker::abort = true;
Tracker::doneEvt.signal();
}
#endif
bool m_done;
struct ChannelGetRequesterImpl : public ChannelGetRequester, public Tracker
{
const string m_channelName;
operation_type::shared_pointer op;
public:
ChannelGetRequesterImpl(std::string channelName) : m_channelName(channelName) {}
virtual ~ChannelGetRequesterImpl() {}
ChannelGetRequesterImpl(std::string channelName) : m_channelName(channelName), m_done(false) {}
virtual string getRequesterName()
{
return "ChannelGetRequesterImpl";
}
virtual void message(std::string const & message, MessageType messageType)
{
std::cerr << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")" << std::endl;
}
virtual string getRequesterName() { return "ChannelGetRequesterImpl"; }
virtual void channelGetConnect(const epics::pvData::Status& status, ChannelGet::shared_pointer const & channelGet,
epics::pvData::Structure::const_shared_pointer const & /*structure*/)
{
if (status.isSuccess())
{
// show warning
if (!status.isOK())
if (!status.isOK() || debugFlag)
{
std::cerr << "[" << m_channelName << "] channel get create: " << status << std::endl;
std::cerr << "[" << m_channelName << "] channel get create: " << status << '\n';
}
channelGet->lastRequest();
@@ -168,8 +192,8 @@ public:
}
else
{
std::cerr << "[" << m_channelName << "] failed to create channel get: " << status << std::endl;
m_event.signal();
std::cerr << "[" << m_channelName << "] failed to create channel get: " << status << '\n';
done();
}
}
@@ -180,112 +204,82 @@ public:
{
if (status.isSuccess())
{
// show warning
if (!status.isOK())
if (!status.isOK() || debugFlag)
{
std::cerr << "[" << m_channelName << "] channel get: " << status << std::endl;
std::cerr << "[" << m_channelName << "] channel get: " << status << '\n';
}
// access smart pointers
{
Lock lock(m_pointerMutex);
printValue(m_channelName, pvStructure);
m_pvStructure = pvStructure;
m_bitSet = bitSet;
m_done = true;
}
}
else
{
std::cerr << "[" << m_channelName << "] failed to get: " << status << std::endl;
std::cerr << "[" << m_channelName << "] failed to get: " << status << '\n';
}
m_event.signal();
done();
}
PVStructure::shared_pointer getPVStructure()
{
Lock lock(m_pointerMutex);
return m_pvStructure;
}
bool waitUntilGet(double timeOut)
{
bool signaled = m_event.wait(timeOut);
if (!signaled)
{
std::cerr << "[" << m_channelName << "] get timeout" << std::endl;
return false;
}
Lock lock(m_pointerMutex);
return m_done;
}
};
class MonitorRequesterImpl : public MonitorRequester
struct MonitorRequesterImpl : public MonitorRequester, public Tracker
{
private:
string m_channelName;
const string m_channelName;
operation_type::shared_pointer op;
public:
MonitorRequesterImpl(std::string channelName) : m_channelName(channelName) {};
MonitorRequesterImpl(std::string channelName) : m_channelName(channelName) {}
virtual ~MonitorRequesterImpl() {}
virtual string getRequesterName()
{
return "MonitorRequesterImpl";
};
virtual void message(std::string const & message,MessageType messageType)
{
std::cerr << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")" << std::endl;
}
virtual void monitorConnect(const epics::pvData::Status& status, Monitor::shared_pointer const & monitor, StructureConstPtr const & /*structure*/)
{
if (status.isSuccess())
{
/*
string str;
structure->toString(&str);
std::cout << str << std::endl;
*/
Status startStatus = monitor->start();
// show error
// TODO and exit
if (!startStatus.isSuccess())
if (!startStatus.isSuccess() || debugFlag)
{
std::cerr << "[" << m_channelName << "] channel monitor start: " << startStatus << std::endl;
std::cerr << "[" << m_channelName << "] channel monitor start: " << startStatus << '\n';
}
}
else
{
std::cerr << "monitorConnect(" << status << ")" << std::endl;
std::cerr << "monitorConnect(" << status << ")\n";
done();
}
}
virtual void channelDisconnect(bool destroy) {
if(!destroy) {
std::cerr << m_channelName<<" Disconnected\n";
}
}
virtual void monitorEvent(Monitor::shared_pointer const & monitor)
{
if(debugFlag)
std::cerr << "[" << m_channelName << "] channel monitor event: \n";
MonitorElement::shared_pointer element;
while ((element = monitor->poll()))
for(MonitorElement::Ref it(monitor); it; ++it)
{
MonitorElement* element(it.get());
if (mode == ValueOnlyMode)
{
PVField::shared_pointer value = element->pvStructurePtr->getSubField("value");
if (value.get() == 0)
{
std::cerr << "no 'value' field" << std::endl;
std::cout << m_channelName << std::endl;
//std::cout << *(element->pvStructurePtr.get()) << std::endl << std::endl;
std::cerr << "no 'value' field" << '\n';
std::cout << m_channelName << '\n';
pvutil_ostream myos(std::cout.rdbuf());
myos << *(element->pvStructurePtr.get()) << std::endl << std::endl;
myos << *(element->pvStructurePtr.get()) << "\n\n";
}
else
{
@@ -298,14 +292,13 @@ public:
std::cout << std::setw(30) << std::left << m_channelName;
std::cout << fieldSeparator;
formatTType(std::cout, TR1::static_pointer_cast<PVStructure>(value));
std::cout << std::endl;
std::cout << '\n';
}
else
{
std::cout << m_channelName << std::endl;
//std::cout << *(element->pvStructurePtr.get()) << std::endl << std::endl;
std::cout << m_channelName << '\n';
pvutil_ostream myos(std::cout.rdbuf());
myos << *(element->pvStructurePtr.get()) << std::endl << std::endl;
myos << *(element->pvStructurePtr.get()) << "\n\n";
}
}
else
@@ -317,7 +310,7 @@ public:
std::cout << fieldSeparator;
terse(std::cout, value) << std::endl;
terse(std::cout, value) << '\n';
}
}
}
@@ -330,59 +323,46 @@ public:
std::cout << fieldSeparator;
terseStructure(std::cout, element->pvStructurePtr) << std::endl;
terseStructure(std::cout, element->pvStructurePtr) << '\n';
}
else
{
std::cout << m_channelName << std::endl;
//std::cout << *(element->pvStructurePtr.get()) << std::endl << std::endl;
std::cout << m_channelName << '\n';
pvutil_ostream myos(std::cout.rdbuf());
myos << *(element->pvStructurePtr.get()) << std::endl << std::endl;
myos << *(element->pvStructurePtr.get()) << "\n\n";
}
monitor->release(element);
}
}
virtual void unlisten(Monitor::shared_pointer const & /*monitor*/)
{
std::cerr << "unlisten" << std::endl;
if(debugFlag)
std::cerr << "unlisten" << m_channelName << '\n';
done();
}
};
} // namespace
/*+**************************************************************************
*
* Function: main
*
* Description: pvget main()
* Evaluate command line options, set up PVA, connect the
* channels, print the data as requested
*
* Arg(s) In: [options] <pv-name>...
*
* Arg(s) Out: none
*
* Return(s): Standard return code (0=success, 1=error)
*
**************************************************************************-*/
int main (int argc, char *argv[])
{
int opt; /* getopt() current option */
bool debug = false;
bool cleanupAndReport = false;
bool monitor = false;
bool quiet = false;
istream* inputStream = 0;
ifstream ifs;
bool fromStream = false;
double timeOut = -1.0;
bool explicit_timeout = false;
setvbuf(stdout,NULL,_IOLBF,BUFSIZ); /* Set stdout to line buffering */
// ================ Parse Arguments
while ((opt = getopt(argc, argv, ":hvr:w:tmp:qdcF:f:ni")) != -1) {
switch (opt) {
case 'h': /* Print usage */
@@ -403,7 +383,8 @@ int main (int argc, char *argv[])
{
fprintf(stderr, "'%s' is not a valid timeout value "
"- ignored. ('pvget -h' for help.)\n", optarg);
timeOut = DEFAULT_TIMEOUT;
} else {
explicit_timeout = true;
}
break;
case 'r': /* Set PVA timeout value */
@@ -424,13 +405,11 @@ int main (int argc, char *argv[])
defaultProvider = optarg;
break;
case 'q': /* Quiet mode */
quiet = true;
break;
case 'd': /* Debug log level */
debug = true;
debugFlag = true;
break;
case 'c': /* Clean-up and report used instance count */
cleanupAndReport = true;
break;
case 'F': /* Store this for output formatting */
fieldSeparator = (char) *optarg;
@@ -476,6 +455,13 @@ int main (int argc, char *argv[])
}
}
if(!explicit_timeout) {
if(monitor)
timeOut = -1.0; // forever
else
timeOut = 3.0;
}
int nPvs = argc - optind; /* Remaining arg list are PV names */
if (nPvs > 0)
{
@@ -511,166 +497,121 @@ int main (int argc, char *argv[])
}
SET_LOG_LEVEL(debug ? logLevelDebug : logLevelError);
SET_LOG_LEVEL(debugFlag ? logLevelDebug : logLevelError);
std::cout << std::boolalpha;
terseSeparator(fieldSeparator);
// ================ Connect channels and start operations
epics::pvAccess::ca::CAClientFactory::start();
bool allOK = true;
{
Requester::shared_pointer requester(new RequesterImpl("pvget"));
std::set<ChannelProvider::shared_pointer> providers;
typedef std::map<std::string, Channel::shared_pointer> chan_cache_t;
chan_cache_t chan_cache;
PVStructure::shared_pointer pvRequest = CreateRequest::create()->createRequest(request);
if(pvRequest.get()==NULL) {
fprintf(stderr, "failed to parse request string\n");
PVStructure::shared_pointer pvRequest;
try {
pvRequest = createRequest(request);
} catch(std::exception& e){
fprintf(stderr, "failed to parse request string: %s\n", e.what());
return 1;
}
// keep the operations, and associated channels, alive
std::vector<std::tr1::shared_ptr<Tracker> > ops;
for(size_t n=0; n<pvs.size(); n++)
{
URI uri;
bool validURI = URI::parse(pvs[n], uri);
if (validURI) {
if (uri.path.length() <= 1)
{
std::cerr << "invalid URI '" << pvs[n] << "', empty path\n";
return 1;
}
pvs[n] = uri.path.substr(1);;
} else {
uri.protocol = defaultProvider;
uri.host.clear();
}
ChannelProvider::shared_pointer provider(ChannelProviderRegistry::clients()->getProvider(uri.protocol));
if(!provider) {
std::cerr<<"Unknown provider \""<<uri.protocol<<"\" for channel "<<pvs[n]<<"\n";
return 1;
}
std::vector<std::string> pvNames;
std::vector<std::string> pvAddresses;
std::vector<std::string> providerNames;
pvNames.reserve(nPvs);
pvAddresses.reserve(nPvs);
providerNames.reserve(nPvs);
for (int n = 0; n < nPvs; n++)
{
URI uri;
bool validURI = URI::parse(pvs[n], uri);
std::string providerName(defaultProvider);
std::string pvName(pvs[n]);
std::string address(noAddress);
bool usingDefaultProvider = true;
if (validURI)
{
if (uri.path.length() <= 1)
{
std::cerr << "invalid URI '" << pvs[n] << "', empty path" << std::endl;
return 1;
}
providerName = uri.protocol;
pvName = uri.path.substr(1);
address = uri.host;
usingDefaultProvider = false;
}
if ((providerName != "pva") && (providerName != "ca"))
{
std::cerr << "invalid "
<< (usingDefaultProvider ? "default provider" : "URI scheme")
<< " '" << providerName
<< "', only 'pva' and 'ca' are supported" << std::endl;
Channel::shared_pointer channel;
chan_cache_t::const_iterator it = chan_cache.find(pvs[n]);
if(it==chan_cache.end()) {
try {
channel = provider->createChannel(pvs[n], DefaultChannelRequester::build(),
ChannelProvider::PRIORITY_DEFAULT, uri.host);
} catch(std::exception& e) {
std::cerr<<"Provider "<<uri.protocol<<" can't create channel \""<<pvs[n]<<"\"\n";
return 1;
}
pvNames.push_back(pvName);
pvAddresses.push_back(address);
providerNames.push_back(providerName);
chan_cache[pvs[n]] = channel;
} else {
channel = it->second;
}
ClientFactory::start();
epics::pvAccess::ca::CAClientFactory::start();
if(monitor) {
std::tr1::shared_ptr<MonitorRequesterImpl> req(new MonitorRequesterImpl(pvs[n]));
// first connect to all, this allows resource (e.g. TCP connection) sharing
vector<Channel::shared_pointer> channels(nPvs);
for (int n = 0; n < nPvs; n++)
{
TR1::shared_ptr<ChannelRequesterImpl> channelRequesterImpl(new ChannelRequesterImpl(quiet));
if (pvAddresses[n].empty())
channels[n] = getChannelProviderRegistry()->getProvider(
providerNames[n])->createChannel(pvNames[n], channelRequesterImpl);
else
channels[n] = getChannelProviderRegistry()->getProvider(
providerNames[n])->createChannel(pvNames[n], channelRequesterImpl,
ChannelProvider::PRIORITY_DEFAULT, pvAddresses[n]);
req->op = channel->createMonitor(req, pvRequest);
ops.push_back(req);
} else {
std::tr1::shared_ptr<ChannelGetRequesterImpl> req(new ChannelGetRequesterImpl(pvs[n]));
req->op = channel->createChannelGet(req, pvRequest);
ops.push_back(req);
}
// for now a simple iterating sync implementation, guarantees order
for (int n = 0; n < nPvs; n++)
{
Channel::shared_pointer channel = channels[n];
TR1::shared_ptr<ChannelRequesterImpl> channelRequesterImpl = TR1::dynamic_pointer_cast<ChannelRequesterImpl>(channel->getChannelRequester());
if (channelRequesterImpl->waitUntilConnected(timeOut))
{
TR1::shared_ptr<GetFieldRequesterImpl> getFieldRequesterImpl;
// probe for value field
if (mode == ValueOnlyMode)
{
getFieldRequesterImpl.reset(new GetFieldRequesterImpl(channel));
// get all to be immune to bad clients not supporting selective getField request
channel->getField(getFieldRequesterImpl, "");
}
if (getFieldRequesterImpl.get() == 0 ||
getFieldRequesterImpl->waitUntilFieldGet(timeOut))
{
// check probe
if (getFieldRequesterImpl.get())
{
Structure::const_shared_pointer structure =
TR1::dynamic_pointer_cast<const Structure>(getFieldRequesterImpl->getField());
if (structure.get() == 0 || structure->getField("value").get() == 0)
{
// fallback to structure
mode = StructureMode;
pvRequest = CreateRequest::create()->createRequest("field()");
}
}
if (!monitor)
{
TR1::shared_ptr<ChannelGetRequesterImpl> getRequesterImpl(new ChannelGetRequesterImpl(channel->getChannelName()));
ChannelGet::shared_pointer channelGet = channel->createChannelGet(getRequesterImpl, pvRequest);
allOK &= getRequesterImpl->waitUntilGet(timeOut);
if (allOK)
printValue(channel->getChannelName(), getRequesterImpl->getPVStructure());
}
else
{
TR1::shared_ptr<ChannelRequesterImpl> channelRequesterImpl = TR1::dynamic_pointer_cast<ChannelRequesterImpl>(channel->getChannelRequester());
channelRequesterImpl->showDisconnectMessage();
TR1::shared_ptr<MonitorRequesterImpl> monitorRequesterImpl(new MonitorRequesterImpl(channel->getChannelName()));
Monitor::shared_pointer monitorGet = channel->createMonitor(monitorRequesterImpl, pvRequest);
allOK &= true;
}
}
else
{
allOK = false;
channel->destroy();
std::cerr << "[" << channel->getChannelName() << "] failed to get channel introspection data" << std::endl;
}
}
else
{
allOK = false;
channel->destroy();
std::cerr << "[" << channel->getChannelName() << "] connection timeout" << std::endl;
}
}
if (allOK && monitor)
{
while (true)
epicsThreadSleep(timeOut);
}
epics::pvAccess::ca::CAClientFactory::stop();
ClientFactory::stop();
// make sure to keep the provider alive as Channels will be automatically closed
providers.insert(provider);
}
if (cleanupAndReport)
// Active channels continue to be referenced by get/monitor stored in 'ops'
chan_cache.clear();
// ========================== Wait for operations to complete, or timeout
#ifdef USE_SIGNAL
signal(SIGINT, alldone);
signal(SIGTERM, alldone);
signal(SIGQUIT, alldone);
#endif
if(debugFlag)
std::cerr<<"Waiting...\n";
{
// TODO implement wait on context
epicsThreadSleep ( 3.0 );
//std::cout << "-----------------------------------------------------------------------" << std::endl;
//epicsExitCallAtExits();
Guard G(Tracker::doneLock);
while(Tracker::inprog.size() && !Tracker::abort) {
UnGuard U(G);
if(timeOut<=0)
Tracker::doneEvt.wait();
else if(!Tracker::doneEvt.wait(timeOut)) {
allOK = false;
if(debugFlag)
std::cerr<<"Timeout\n";
break;
}
}
}
// ========================== All done now
if(debugFlag)
std::cerr<<"Done\n";
return allOK ? 0 : 1;
}

View File

@@ -1,5 +1,4 @@
#include <iostream>
#include <pv/clientFactory.h>
#include <pv/pvAccess.h>
#include <pv/caProvider.h>
@@ -137,26 +136,24 @@ int main (int argc, char *argv[])
bool allOK = true;
epics::pvAccess::ca::CAClientFactory::start();
{
std::vector<std::string> pvNames;
std::vector<std::string> pvAddresses;
std::vector<std::string> providerNames;
std::vector<ChannelProvider::shared_pointer> providers;
pvNames.reserve(nPvs);
pvAddresses.reserve(nPvs);
providerNames.reserve(nPvs);
for (int n = 0; n < nPvs; n++)
{
URI uri;
bool validURI = URI::parse(pvs[n], uri);
TR1::shared_ptr<ChannelRequesterImpl> channelRequesterImpl(new ChannelRequesterImpl());
std::string providerName(defaultProvider);
std::string pvName(pvs[n]);
std::string address(noAddress);
bool usingDefaultProvider = true;
if (validURI)
{
if (uri.path.length() <= 1)
@@ -167,82 +164,58 @@ int main (int argc, char *argv[])
providerName = uri.protocol;
pvName = uri.path.substr(1);
address = uri.host;
usingDefaultProvider = false;
}
if ((providerName != "pva") && (providerName != "ca"))
{
std::cerr << "invalid "
<< (usingDefaultProvider ? "default provider" : "URI scheme")
<< " '" << providerName
<< "', only 'pva' and 'ca' are supported" << std::endl;
return 1;
}
pvNames.push_back(pvName);
pvAddresses.push_back(address);
providerNames.push_back(providerName);
providers.push_back(ChannelProviderRegistry::clients()->getProvider(providerName));
if(!providers.back())
{
std::cerr << "unknown provider name '" << providerName
<< "', only 'pva' and 'ca' are supported" << std::endl;
allOK = false;
}
}
ClientFactory::start();
epics::pvAccess::ca::CAClientFactory::start();
// first connect to all, this allows resource (e.g. TCP connection) sharing
vector<Channel::shared_pointer> channels(nPvs);
for (int n = 0; n < nPvs; n++)
{
TR1::shared_ptr<ChannelRequesterImpl> channelRequesterImpl(new ChannelRequesterImpl());
if (pvAddresses[n].empty())
channels[n] = getChannelProviderRegistry()->getProvider(
providerNames[n])->createChannel(pvNames[n], channelRequesterImpl);
else
channels[n] = getChannelProviderRegistry()->getProvider(
providerNames[n])->createChannel(pvNames[n], channelRequesterImpl,
ChannelProvider::PRIORITY_DEFAULT, pvAddresses[n]);
if(!providers[n]) continue;
channels[n] = providers[n]->createChannel(pvNames[n], DefaultChannelRequester::build(),
ChannelProvider::PRIORITY_DEFAULT, pvAddresses[n]);
}
// for now a simple iterating sync implementation, guarantees order
for (int n = 0; n < nPvs; n++)
{
Channel::shared_pointer channel = channels[n];
TR1::shared_ptr<ChannelRequesterImpl> channelRequesterImpl = TR1::dynamic_pointer_cast<ChannelRequesterImpl>(channel->getChannelRequester());
if (channelRequesterImpl->waitUntilConnected(timeOut))
TR1::shared_ptr<GetFieldRequesterImpl> getFieldRequesterImpl(new GetFieldRequesterImpl(channel));
channel->getField(getFieldRequesterImpl, "");
if (getFieldRequesterImpl->waitUntilFieldGet(timeOut))
{
TR1::shared_ptr<GetFieldRequesterImpl> getFieldRequesterImpl(new GetFieldRequesterImpl(channel));
channel->getField(getFieldRequesterImpl, "");
Structure::const_shared_pointer structure =
TR1::dynamic_pointer_cast<const Structure>(getFieldRequesterImpl->getField());
if (getFieldRequesterImpl->waitUntilFieldGet(timeOut))
channel->printInfo();
if (structure)
{
Structure::const_shared_pointer structure =
TR1::dynamic_pointer_cast<const Structure>(getFieldRequesterImpl->getField());
channel->printInfo();
if (structure)
{
std::cout << *structure << std::endl << std::endl;
}
else
{
std::cout << "(null introspection data)" << std::endl << std::endl;
}
std::cout << *structure << std::endl << std::endl;
}
else
{
allOK = false;
channel->destroy();
std::cerr << "[" << channel->getChannelName() << "] failed to get channel introspection data" << std::endl;
std::cout << "(null introspection data)" << std::endl << std::endl;
}
}
else
{
allOK = false;
channel->destroy();
std::cerr << "[" << channel->getChannelName() << "] connection timeout" << std::endl;
std::cerr << "[" << channel->getChannelName() << "] failed to get channel introspection data" << std::endl;
}
}
epics::pvAccess::ca::CAClientFactory::stop();
ClientFactory::stop();
}
if (cleanupAndReport)

View File

@@ -3,6 +3,7 @@
#include <iostream>
#include <map>
#include <iterator>
#include <vector>
#include <string>
#include <istream>
@@ -39,6 +40,8 @@ using namespace std;
using namespace epics::pvData;
using namespace epics::pvAccess;
namespace {
/// Byte to hexchar mapping.
static const char lookup[] = {
'0', '1', '2', '3', '4', '5', '6', '7',
@@ -138,7 +141,7 @@ bool processSearchResponse(osiSockAddr const & responseFrom, ByteBuffer & receiv
return false;
epics::pvAccess::GUID guid;
epics::pvAccess::ServerGUID guid;
receiveBuffer.get(guid.value, 0, sizeof(guid.value));
/*int32 searchSequenceId = */receiveBuffer.getInt();
@@ -461,6 +464,7 @@ void usage (void)
, DEFAULT_TIMEOUT);
}
}//namespace
/*+**************************************************************************
*
@@ -481,7 +485,6 @@ int main (int argc, char *argv[])
{
int opt; /* getopt() current option */
bool debug = false;
bool quiet = false;
double timeOut = DEFAULT_TIMEOUT;
// char fieldSeparator = ' ';
bool printInfo = false;
@@ -517,7 +520,6 @@ int main (int argc, char *argv[])
}
break;
case 'q': /* Quiet mode */
quiet = true;
break;
case 'd': /* Debug log level */
debug = true;
@@ -588,15 +590,9 @@ int main (int argc, char *argv[])
bool allOK = true;
//if (!quiet)
// fprintf(stderr, "Searching...\n");
if (noArgs || byGUIDSearch)
discoverServers(timeOut);
//if (!quiet)
// fprintf(stderr, "done.\n");
// just list all the discovered servers
if (noArgs)
{
@@ -656,32 +652,53 @@ int main (int argc, char *argv[])
}
}
// TODO for now we call eget utility
// TODO timeOut
string cmd = "eget -";
if (debug)
cmd += 'd';
if (quiet)
cmd += 'q';
if (printInfo)
cmd += 'N';
cmd += "s pva://" + serverAddress + "/server?op=";
if (printInfo)
cmd += "info";
else
cmd += "channels";
StructureConstPtr argstype(getFieldCreate()->createFieldBuilder()
->setId("epics:nt/NTURI:1.0")
->add("scheme", pvString)
->add("path", pvString)
->addNestedStructure("query")
->add("op", pvString)
->endNested()
->createStructure());
FILE* egetpipe = popen (cmd.c_str(), "w");
if (!egetpipe)
{
char errStr[64];
epicsSocketConvertErrnoToString(errStr, sizeof(errStr));
fprintf(stderr, "Failed to exec 'eget': %s\n", errStr);
allOK = false;
PVStructure::shared_pointer args(getPVDataCreate()->createPVStructure(argstype));
args->getSubFieldT<PVString>("scheme")->put("pva");
args->getSubFieldT<PVString>("path")->put("server");
args->getSubFieldT<PVString>("query.op")->put(printInfo ? "info" : "channels");
if(debug) {
std::cerr<<"Query to "<<serverAddress<<"\n"<<args<<"\n";
}
pclose(egetpipe);
PVStructure::shared_pointer ret;
try {
RPCClient rpc("server",
createRequest("field()"),
ChannelProvider::shared_pointer(),
serverAddress);
if(debug)
std::cerr<<"Execute\n";
ret = rpc.request(args, timeOut, true);
} catch(std::exception& e) {
std::cerr<<"Error: "<<e.what()<<"\n";
return 1;
}
if(!printInfo) {
PVStringArray::shared_pointer pvs(ret->getSubField<PVStringArray>("value"));
PVStringArray::const_svector val(pvs->view());
std::copy(val.begin(),
val.end(),
std::ostream_iterator<std::string>(std::cout, "\n"));
return allOK ? 0 : 1;
}
std::cout<<ret<<"\n";
}
}

View File

@@ -1,5 +1,4 @@
#include <iostream>
#include <pv/clientFactory.h>
#include <pv/pvAccess.h>
#include <stdio.h>
@@ -16,6 +15,7 @@
#include <fstream>
#include <sstream>
#include <pv/pvaDefs.h>
#include <pv/event.h>
#include <epicsExit.h>
@@ -28,7 +28,7 @@ namespace TR1 = std::tr1;
using namespace epics::pvData;
using namespace epics::pvAccess;
//EnumMode enumMode = AutoEnum;
namespace {
size_t fromString(PVFieldPtr const & pv, StringArray const & from, size_t fromStartIndex);
@@ -357,38 +357,6 @@ void printValue(std::string const & channelName, PVStructure::shared_pointer con
std::cout << std::endl << *(pv.get()) << std::endl << std::endl;
}
struct AtomicBoolean_null_deleter
{
void operator()(void const *) const {}
};
// standard performance on set/clear, use of TR1::shared_ptr lock-free counter for get
// alternative is to use boost::atomic
class AtomicBoolean
{
public:
AtomicBoolean() : counter(static_cast<void*>(0), AtomicBoolean_null_deleter()) {};
void set() {
mutex.lock();
setp = counter;
mutex.unlock();
}
void clear() {
mutex.lock();
setp.reset();
mutex.unlock();
}
bool get() const {
return counter.use_count() == 2;
}
private:
TR1::shared_ptr<void> counter;
TR1::shared_ptr<void> setp;
epics::pvData::Mutex mutex;
};
class ChannelPutRequesterImpl : public ChannelPutRequester
{
private:
@@ -412,11 +380,6 @@ public:
return "ChannelPutRequesterImpl";
}
virtual void message(std::string const & message, MessageType messageType)
{
std::cerr << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")" << std::endl;
}
virtual void channelPutConnect(const epics::pvData::Status& status,
ChannelPut::shared_pointer const & channelPut,
epics::pvData::Structure::const_shared_pointer const & /*structure*/)
@@ -528,21 +491,7 @@ public:
};
/*+**************************************************************************
*
* Function: main
*
* Description: pvput main()
* Evaluate command line options, set up PVA, connect the
* channels, print the data as requested
*
* Arg(s) In: [options] <pv-name> <values>...
*
* Arg(s) Out: none
*
* Return(s): Standard return code (0=success, 1=error)
*
**************************************************************************-*/
} // namespace
int main (int argc, char *argv[])
{
@@ -654,12 +603,9 @@ int main (int argc, char *argv[])
URI uri;
bool validURI = URI::parse(pv, uri);
TR1::shared_ptr<ChannelRequesterImpl> channelRequesterImpl(new ChannelRequesterImpl(quiet));
string providerName(defaultProvider);
string pvName(pv);
string address(noAddress);
bool usingDefaultProvider = true;
if (validURI)
{
if (uri.path.length() <= 1)
@@ -670,15 +616,13 @@ int main (int argc, char *argv[])
providerName = uri.protocol;
pvName = uri.path.substr(1);
address = uri.host;
usingDefaultProvider = false;
}
if ((providerName != "pva") && (providerName != "ca"))
{
std::cerr << "invalid "
<< (usingDefaultProvider ? "default provider" : "URI scheme")
<< " '" << providerName
<< "', only 'pva' and 'ca' are supported" << std::endl;
epics::pvAccess::ca::CAClientFactory::start();
ChannelProvider::shared_pointer provider(ChannelProviderRegistry::clients()->getProvider(providerName));
if(!provider) {
std::cerr << "Unknown provider '"<<providerName<<"'\n";
return 1;
}
@@ -713,11 +657,11 @@ int main (int argc, char *argv[])
values.push_back(argv[optind]);
}
Requester::shared_pointer requester(new RequesterImpl("pvput"));
PVStructure::shared_pointer pvRequest = CreateRequest::create()->createRequest(request);
if(pvRequest.get()==NULL) {
fprintf(stderr, "failed to parse request string\n");
PVStructure::shared_pointer pvRequest;
try {
pvRequest = createRequest(request);
} catch(std::exception& e){
fprintf(stderr, "failed to parse request string: %s\n", e.what());
return 1;
}
@@ -727,72 +671,55 @@ int main (int argc, char *argv[])
terseSeparator(fieldSeparator);
setEnumPrintMode(enumMode);
ClientFactory::start();
epics::pvAccess::ca::CAClientFactory::start();
bool allOK = true;
try
{
do
{
// first connect
TR1::shared_ptr<ChannelRequesterImpl> channelRequesterImpl(new ChannelRequesterImpl(quiet));
// first connect
Channel::shared_pointer channel;
if (address.empty())
channel = getChannelProviderRegistry()->getProvider(
providerName)->createChannel(pvName, channelRequesterImpl);
else
channel = getChannelProviderRegistry()->getProvider(
providerName)->createChannel(pvName, channelRequesterImpl,
ChannelProvider::PRIORITY_DEFAULT, address);
if (channelRequesterImpl->waitUntilConnected(timeOut))
{
TR1::shared_ptr<ChannelPutRequesterImpl> putRequesterImpl(new ChannelPutRequesterImpl(channel->getChannelName()));
if (mode != TerseMode && !quiet)
std::cout << "Old : ";
ChannelPut::shared_pointer channelPut = channel->createChannelPut(putRequesterImpl, pvRequest);
allOK &= putRequesterImpl->waitUntilDone(timeOut);
if (allOK)
{
if (mode != TerseMode && !quiet)
printValue(pvName, putRequesterImpl->getStructure());
// convert value from string
// since we access structure from another thread, we need to lock
{
ScopedLock lock(channelPut);
fromString(putRequesterImpl->getStructure(), values);
}
// we do a put
putRequesterImpl->resetEvent();
// note on bitSet: we get all, we set all
channelPut->put(putRequesterImpl->getStructure(), putRequesterImpl->getBitSet());
allOK &= putRequesterImpl->waitUntilDone(timeOut);
if (allOK)
{
// and than a get again to verify put
if (mode != TerseMode && !quiet) std::cout << "New : ";
putRequesterImpl->resetEvent();
channelPut->get();
allOK &= putRequesterImpl->waitUntilDone(timeOut);
if (allOK && !quiet)
printValue(pvName, putRequesterImpl->getStructure());
}
}
}
else
{
allOK = false;
std::cerr << "[" << channel->getChannelName() << "] connection timeout" << std::endl;
}
channel->destroy();
Channel::shared_pointer channel;
try {
channel = provider->createChannel(pvName, DefaultChannelRequester::build(),
ChannelProvider::PRIORITY_DEFAULT, address);
} catch(std::exception& e){
std::cerr<<"Provider " << providerName<< " Failed to create channel \""<<pvName<<"\"\n";
return 1;
}
TR1::shared_ptr<ChannelPutRequesterImpl> putRequesterImpl(new ChannelPutRequesterImpl(channel->getChannelName()));
if (mode != TerseMode && !quiet)
std::cout << "Old : ";
ChannelPut::shared_pointer channelPut = channel->createChannelPut(putRequesterImpl, pvRequest);
allOK &= putRequesterImpl->waitUntilDone(timeOut);
if (allOK)
{
if (mode != TerseMode && !quiet)
printValue(pvName, putRequesterImpl->getStructure());
// convert value from string
// since we access structure from another thread, we need to lock
{
ScopedLock lock(channelPut);
fromString(putRequesterImpl->getStructure(), values);
}
// we do a put
putRequesterImpl->resetEvent();
// note on bitSet: we get all, we set all
channelPut->put(putRequesterImpl->getStructure(), putRequesterImpl->getBitSet());
allOK &= putRequesterImpl->waitUntilDone(timeOut);
if (allOK)
{
// and than a get again to verify put
if (mode != TerseMode && !quiet) std::cout << "New : ";
putRequesterImpl->resetEvent();
channelPut->get();
allOK &= putRequesterImpl->waitUntilDone(timeOut);
if (allOK && !quiet)
printValue(pvName, putRequesterImpl->getStructure());
}
}
while (false);
} catch (std::out_of_range& oor) {
allOK = false;
std::cerr << "parse error: not enough values" << std::endl;
@@ -804,8 +731,5 @@ int main (int argc, char *argv[])
std::cerr << "unknown exception caught" << std::endl;
}
epics::pvAccess::ca::CAClientFactory::stop();
ClientFactory::stop();
return allOK ? 0 : 1;
}

View File

@@ -18,21 +18,6 @@ namespace TR1 = std::tr1;
using namespace epics::pvData;
using namespace epics::pvAccess;
RequesterImpl::RequesterImpl(std::string const & requesterName) :
m_requesterName(requesterName)
{
}
string RequesterImpl::getRequesterName()
{
return "RequesterImpl";
}
void RequesterImpl::message(std::string const & message, MessageType messageType)
{
std::cerr << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")" << std::endl;
}
std::ostream& operator<<(std::ostream& o, const dump_stack_only_on_debug& d)
{
const Status &s = d.status;
@@ -442,70 +427,6 @@ char *url_encode(const char *str) {
return buf;
}
ChannelRequesterImpl::ChannelRequesterImpl(bool _printOnlyErrors) :
printOnlyErrors(_printOnlyErrors), showDisconnectMsg(false)
{
}
string ChannelRequesterImpl::getRequesterName()
{
return "ChannelRequesterImpl";
}
void ChannelRequesterImpl::message(std::string const & message, MessageType messageType)
{
if (!printOnlyErrors || messageType > warningMessage)
std::cerr << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")" << std::endl;
}
void ChannelRequesterImpl::channelCreated(const epics::pvData::Status& status, Channel::shared_pointer const & channel)
{
if (status.isSuccess())
{
// show warning
if (!status.isOK())
{
std::cerr << "[" << channel->getChannelName() << "] channel create: " << dump_stack_only_on_debug(status) << std::endl;
}
}
else
{
std::cerr << "[" << channel->getChannelName() << "] failed to create a channel: " << dump_stack_only_on_debug(status) << std::endl;
}
}
void ChannelRequesterImpl::channelStateChange(Channel::shared_pointer const & channel, Channel::ConnectionState connectionState)
{
if (connectionState == Channel::CONNECTED)
{
m_event.signal();
}
else if (showDisconnectMsg && connectionState == Channel::DISCONNECTED)
{
std::cerr << std::setw(30) << std::left << channel->getChannelName()
<< ' ' << "*** disconnected" << std::endl;
}
/*
else if (connectionState != Channel::DESTROYED)
{
std::cerr << "[" << channel->getChannelName() << "] channel state change: " << Channel::ConnectionStateNames[connectionState] << std::endl;
}
*/
}
bool ChannelRequesterImpl::waitUntilConnected(double timeOut)
{
return m_event.wait(timeOut);
}
void ChannelRequesterImpl::showDisconnectMessage(bool show)
{
showDisconnectMsg = show;
}
GetFieldRequesterImpl::GetFieldRequesterImpl(epics::pvAccess::Channel::shared_pointer channel) :
m_channel(channel)
{
@@ -517,11 +438,6 @@ string GetFieldRequesterImpl::getRequesterName()
return "GetFieldRequesterImpl";
}
void GetFieldRequesterImpl::message(std::string const & message, MessageType messageType)
{
std::cerr << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")" << std::endl;
}
void GetFieldRequesterImpl::getDone(const epics::pvData::Status& status, epics::pvData::FieldConstPtr const & field)
{
if (status.isSuccess())
@@ -540,8 +456,7 @@ void GetFieldRequesterImpl::getDone(const epics::pvData::Status& status, epics::
}
else
{
// do not complain about missing field
//std::cerr << "[" << m_channel->getChannelName() << "] failed to get channel introspection data: " << dump_stack_only_on_debug(status) << std::endl;
std::cerr << "[" << m_channel->getChannelName() << "] failed to get channel introspection data: " << dump_stack_only_on_debug(status) << std::endl;
}
m_event.signal();

View File

@@ -51,40 +51,6 @@ public:
bool query_indicated;
};
class RequesterImpl :
public epics::pvData::Requester
{
public:
RequesterImpl(std::string const & requesterName);
virtual std::string getRequesterName();
virtual void message(std::string const & message, epics::pvData::MessageType messageType);
private:
std::string m_requesterName;
};
class ChannelRequesterImpl :
public epics::pvAccess::ChannelRequester
{
private:
epics::pvData::Event m_event;
bool printOnlyErrors;
bool showDisconnectMsg;
public:
ChannelRequesterImpl(bool printOnlyErrors = false);
virtual std::string getRequesterName();
virtual void message(std::string const & message, epics::pvData::MessageType messageType);
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);
bool waitUntilConnected(double timeOut);
void showDisconnectMessage(bool show = true);
};
class GetFieldRequesterImpl :
public epics::pvAccess::GetFieldRequester
{
@@ -99,7 +65,6 @@ public:
GetFieldRequesterImpl(epics::pvAccess::Channel::shared_pointer channel);
virtual std::string getRequesterName();
virtual void message(std::string const & message, epics::pvData::MessageType messageType);
virtual void getDone(const epics::pvData::Status& status, epics::pvData::FieldConstPtr const & field);

View File

@@ -3,11 +3,6 @@
TOP = ..
include $(TOP)/configure/CONFIG
EPICS_PVA_MAJOR_VERSION = 5
EPICS_PVA_MINOR_VERSION = 0
EPICS_PVA_MAINTENANCE_VERSION = 1
EPICS_PVA_DEVELOPMENT_FLAG = 1
EXPANDVARS += EPICS_PVA_MAJOR_VERSION
EXPANDVARS += EPICS_PVA_MINOR_VERSION
EXPANDVARS += EPICS_PVA_MAINTENANCE_VERSION
@@ -27,23 +22,18 @@ include $(PVACCESS_SRC)/server/Makefile
include $(PVACCESS_SRC)/rpcService/Makefile
include $(PVACCESS_SRC)/rpcClient/Makefile
include $(PVACCESS_SRC)/pipelineService/Makefile
include $(PVACCESS_SRC)/ca/Makefile
include $(PVACCESS_SRC)/mb/Makefile
include $(PVACCESS_SRC)/ioc/Makefile
LIBRARY = pvAccess
SHRLIB_VERSION = $(EPICS_PVA_MAJOR_VERSION).$(EPICS_PVA_MINOR_VERSION).$(EPICS_PVA_MAINTENANCE_VERSION)
LIBRARY += pvAccess
pvAccess_LIBS += pvData
ifdef WITH_MICROBENCH
pvAccess_LIBS += pvMB
LIB_LIBS += pvMB
endif
pvAccess_LIBS += ca
pvAccess_LIBS += Com
LIB_LIBS += Com
# needed for Windows
pvAccess_SYS_LIBS_WIN32 += ws2_32
LIB_SYS_LIBS_WIN32 += ws2_32
include $(TOP)/configure/RULES

View File

@@ -1,11 +1,14 @@
# This is a Makefile fragment, see ../Makefile
TOP = ../..
include $(TOP)/configure/CONFIG
SRC_DIRS += $(PVACCESS_SRC)/ca
LIBRARY += pvAccessCA
pvAccessCA_LIBS += ca pvAccess pvData Com
INC += pv/caProvider.h
INC += pv/caChannel.h
INC += pv/caStatus.h
LIBSRCS += caProvider.cpp
LIBSRCS += caChannel.cpp
LIBSRCS += caStatus.cpp
pvAccessCA_SRCS += caProvider.cpp
pvAccessCA_SRCS += caChannel.cpp
pvAccessCA_SRCS += caStatus.cpp
include $(TOP)/configure/RULES

View File

@@ -7,9 +7,10 @@
#include <epicsVersion.h>
#include <pv/standardField.h>
#include <pv/logger.h>
#include <pv/pvAccess.h>
#define epicsExportSharedSymbols
#include <pv/logger.h>
#include <pv/caChannel.h>
#include <pv/caStatus.h>
@@ -284,12 +285,12 @@ void CAChannel::activate(short priority)
channelProvider->registerChannel(shared_from_this());
// TODO be sure that ca_connection_handler is not called before this call
EXCEPTION_GUARD(channelRequester->channelCreated(Status::Ok, shared_from_this()));
channelRequester->channelCreated(Status::Ok, shared_from_this());
}
else
{
Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(result)));
EXCEPTION_GUARD(channelRequester->channelCreated(errorStatus, shared_from_this()));
channelRequester->channelCreated(errorStatus, shared_from_this());
}
}
@@ -398,16 +399,6 @@ AccessRights CAChannel::getAccessRights(epics::pvData::PVField::shared_pointer c
}
ChannelProcess::shared_pointer CAChannel::createChannelProcess(
ChannelProcessRequester::shared_pointer const & channelProcessRequester,
epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/)
{
Status errorStatus(Status::STATUSTYPE_ERROR, "not supported");
ChannelProcess::shared_pointer nullChannelProcess;
EXCEPTION_GUARD(channelProcessRequester->channelProcessConnect(errorStatus, nullChannelProcess));
return nullChannelProcess;
}
ChannelGet::shared_pointer CAChannel::createChannelGet(
ChannelGetRequester::shared_pointer const & channelGetRequester,
epics::pvData::PVStructure::shared_pointer const & pvRequest)
@@ -423,28 +414,6 @@ ChannelPut::shared_pointer CAChannel::createChannelPut(
return CAChannelPut::create(shared_from_this(), channelPutRequester, pvRequest);
}
ChannelPutGet::shared_pointer CAChannel::createChannelPutGet(
ChannelPutGetRequester::shared_pointer const & channelPutGetRequester,
epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/)
{
Status errorStatus(Status::STATUSTYPE_ERROR, "not supported");
ChannelPutGet::shared_pointer nullChannelPutGet;
EXCEPTION_GUARD(channelPutGetRequester->channelPutGetConnect(errorStatus, nullChannelPutGet,
Structure::const_shared_pointer(), Structure::const_shared_pointer()));
return nullChannelPutGet;
}
ChannelRPC::shared_pointer CAChannel::createChannelRPC(
ChannelRPCRequester::shared_pointer const & channelRPCRequester,
epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/)
{
Status errorStatus(Status::STATUSTYPE_ERROR, "not supported");
ChannelRPC::shared_pointer nullChannelRPC;
EXCEPTION_GUARD(channelRPCRequester->channelRPCConnect(errorStatus, nullChannelRPC));
return nullChannelRPC;
}
Monitor::shared_pointer CAChannel::createMonitor(
MonitorRequester::shared_pointer const & monitorRequester,
@@ -454,18 +423,6 @@ Monitor::shared_pointer CAChannel::createMonitor(
}
ChannelArray::shared_pointer CAChannel::createChannelArray(
ChannelArrayRequester::shared_pointer const & channelArrayRequester,
epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/)
{
Status errorStatus(Status::STATUSTYPE_ERROR, "not supported");
ChannelArray::shared_pointer nullChannelArray;
EXCEPTION_GUARD(channelArrayRequester->channelArrayConnect(errorStatus, nullChannelArray,
Array::const_shared_pointer()));
return nullChannelArray;
}
void CAChannel::printInfo(std::ostream& out)
{
out << "CHANNEL : " << getChannelName() << std::endl;
@@ -480,7 +437,7 @@ void CAChannel::printInfo(std::ostream& out)
}
/* --------------- epics::pvData::Destroyable --------------- */
/* --------------- Destroyable --------------- */
void CAChannel::destroy()
@@ -614,6 +571,7 @@ void CAChannelGet::activate()
/* --------------- epics::pvAccess::ChannelGet --------------- */
namespace {
static void ca_get_handler(struct event_handler_args args)
{
@@ -978,6 +936,8 @@ static copyDBRtoPVStructure copyFuncTable[] =
copy_DBR_CTRL<dbr_ctrl_double, dbr_double_t, PVDouble, PVDoubleArray> // DBR_CTRL_DOUBLE
};
} // namespace
void CAChannelGet::getDone(struct event_handler_args &args)
{
if (args.status == ECA_NORMAL)
@@ -1053,7 +1013,7 @@ void CAChannelGet::lastRequest()
lastRequestFlag = true;
}
/* --------------- epics::pvData::Destroyable --------------- */
/* --------------- Destroyable --------------- */
void CAChannelGet::destroy()
@@ -1062,23 +1022,6 @@ void CAChannelGet::destroy()
}
/* --------------- epics::pvData::Lockable --------------- */
void CAChannelGet::lock()
{
// TODO
}
void CAChannelGet::unlock()
{
// TODO
}
@@ -1131,6 +1074,7 @@ void CAChannelPut::activate()
/* --------------- epics::pvAccess::ChannelPut --------------- */
namespace {
static void ca_put_handler(struct event_handler_args args)
{
@@ -1291,6 +1235,8 @@ static doPut doPutFuncTable[] =
doPut_pvStructure<dbr_double_t, pvDouble, PVDouble, PVDoubleArray>, // DBR_DOUBLE
};
} // namespace
void CAChannelPut::putDone(struct event_handler_args &args)
{
if (args.status == ECA_NORMAL)
@@ -1304,7 +1250,6 @@ void CAChannelPut::putDone(struct event_handler_args &args)
}
}
void CAChannelPut::put(PVStructure::shared_pointer const & pvPutStructure,
BitSet::shared_pointer const & /*putBitSet*/)
{
@@ -1399,7 +1344,7 @@ void CAChannelPut::lastRequest()
lastRequestFlag = true;
}
/* --------------- epics::pvData::Destroyable --------------- */
/* --------------- Destroyable --------------- */
void CAChannelPut::destroy()
@@ -1408,22 +1353,6 @@ void CAChannelPut::destroy()
}
/* --------------- epics::pvData::Lockable --------------- */
void CAChannelPut::lock()
{
// TODO
}
void CAChannelPut::unlock()
{
// TODO
}
@@ -1460,17 +1389,11 @@ CAChannelMonitor::CAChannelMonitor(CAChannel::shared_pointer const & _channel,
monitorRequester(_monitorRequester),
getType(getDBRType(pvRequest, _channel->getNativeType())),
pvStructure(createPVStructure(_channel, getType, pvRequest)),
changedBitSet(new BitSet(static_cast<uint32>(pvStructure->getStructure()->getNumberFields()))),
overrunBitSet(new BitSet(static_cast<uint32>(pvStructure->getStructure()->getNumberFields()))),
count(0),
element(new MonitorElement())
element(new MonitorElement(pvStructure))
{
// TODO
changedBitSet->set(0);
element->pvStructurePtr = pvStructure;
element->changedBitSet = changedBitSet;
element->overrunBitSet = overrunBitSet;
element->changedBitSet->set(0);
}
void CAChannelMonitor::activate()
@@ -1599,7 +1522,7 @@ void CAChannelMonitor::cancel()
// noop
}
/* --------------- epics::pvData::Destroyable --------------- */
/* --------------- Destroyable --------------- */
void CAChannelMonitor::destroy()

View File

@@ -6,33 +6,40 @@
#include <algorithm>
/* for CA */
#include <cadef.h>
#include <epicsSignal.h>
#include <epicsThread.h>
#include <epicsExit.h>
#include <pv/logger.h>
#include <pv/configuration.h>
#include <pv/pvAccess.h>
#define epicsExportSharedSymbols
#include <pv/logger.h>
#include <pv/caProvider.h>
#include <pv/caChannel.h>
namespace epics {
namespace pvAccess {
namespace ca {
using namespace epics::pvData;
using namespace epics::pvAccess;
using namespace epics::pvAccess::ca;
#define EXCEPTION_GUARD(code) try { code; } \
catch (std::exception &e) { LOG(logLevelError, "Unhandled exception caught from client code at %s:%d: %s", __FILE__, __LINE__, e.what()); } \
catch (...) { LOG(logLevelError, "Unhandled exception caught from client code at %s:%d.", __FILE__, __LINE__); }
std::string CAChannelProvider::PROVIDER_NAME = "ca";
CAChannelProvider::CAChannelProvider() : current_context(0), destroyed(false)
{
initialize();
}
CAChannelProvider::CAChannelProvider(const std::tr1::shared_ptr<Configuration>&)
: current_context(0)
, destroyed(false)
{
// Ignoring Configuration as CA only allows config via. environment,
// and we don't want to change this here.
initialize();
}
CAChannelProvider::~CAChannelProvider()
{
// call destroy() to destroy CA context
@@ -41,7 +48,7 @@ CAChannelProvider::~CAChannelProvider()
std::string CAChannelProvider::getProviderName()
{
return PROVIDER_NAME;
return "ca";
}
ChannelFind::shared_pointer CAChannelProvider::channelFind(
@@ -168,108 +175,38 @@ void CAChannelProvider::initialize()
}
class CAChannelProviderFactory : public ChannelProviderFactory
static
void ca_factory_cleanup(void*)
{
private:
Mutex m_mutex;
CAChannelProvider::shared_pointer sharedProvider;
public:
POINTER_DEFINITIONS(CAChannelProviderFactory);
virtual ~CAChannelProviderFactory()
{
Lock guard(m_mutex);
if (sharedProvider)
{
CAChannelProvider::shared_pointer provider;
sharedProvider.swap(provider);
// factroy cleans up also shared provider
provider->destroy();
}
try {
ChannelProviderRegistry::clients()->remove("ca");
} catch(std::exception& e) {
LOG(logLevelWarn, "Error when unregister \"ca\" factory");
}
virtual std::string getFactoryName()
{
return CAChannelProvider::PROVIDER_NAME;
}
virtual ChannelProvider::shared_pointer sharedInstance()
{
Lock guard(m_mutex);
if (!sharedProvider)
{
try {
// TODO use std::make_shared
std::tr1::shared_ptr<CAChannelProvider> tp(new CAChannelProvider());
sharedProvider = tp;
} catch (std::exception &e) {
LOG(logLevelError, "Unhandled exception caught at %s:%d: %s", __FILE__, __LINE__, e.what());
} catch (...) {
LOG(logLevelError, "Unhandled exception caught at %s:%d.", __FILE__, __LINE__);
}
}
return sharedProvider;
}
virtual ChannelProvider::shared_pointer newInstance(const std::tr1::shared_ptr<Configuration>& conf)
{
// Ignoring configuration as CA only allows config via. environment,
// and we don't want to change this here.
try {
// TODO use std::make_shared
std::tr1::shared_ptr<CAChannelProvider> tp(new CAChannelProvider());
ChannelProvider::shared_pointer ni = tp;
return ni;
} catch (std::exception &e) {
LOG(logLevelError, "Unhandled exception caught at %s:%d: %s", __FILE__, __LINE__, e.what());
return ChannelProvider::shared_pointer();
} catch (...) {
LOG(logLevelError, "Unhandled exception caught at %s:%d.", __FILE__, __LINE__);
return ChannelProvider::shared_pointer();
}
}
};
static Mutex startStopMutex;
ChannelProviderRegistry::shared_pointer CAClientFactory::channelRegistry;
ChannelProviderFactory::shared_pointer CAClientFactory::channelProvider;
int CAClientFactory::numStart = 0;
}
void CAClientFactory::start()
{
Lock guard(startStopMutex);
std::cout << "CAClientFactory::start() numStart " << numStart << std::endl;
++numStart;
if(numStart>1) return;
epicsSignalInstallSigAlarmIgnore();
epicsSignalInstallSigPipeIgnore();
channelProvider.reset(new CAChannelProviderFactory());
channelRegistry = ChannelProviderRegistry::getChannelProviderRegistry();
std::cout << "channelRegistry::use_count " << channelRegistry.use_count() << std::endl;
channelRegistry->add(channelProvider);
if(ChannelProviderRegistry::clients()->add<CAChannelProvider>("ca", false))
epicsAtExit(&ca_factory_cleanup, NULL);
}
void CAClientFactory::stop()
{
std::cout << "ClientFactory::stop() numStart " << numStart << std::endl;
std::cout << "channelRegistry::use_count " << channelRegistry.use_count() << std::endl;
Lock guard(startStopMutex);
if(numStart==0) return;
--numStart;
if(numStart>=1) return;
if (channelProvider)
{
channelRegistry->remove(CAChannelProvider::PROVIDER_NAME);
if(!channelProvider.unique()) {
LOG(logLevelWarn, "ClientFactory::stop() finds shared client context with %u remaining users",
(unsigned)channelProvider.use_count());
}
channelProvider.reset();
channelRegistry.reset();
}
// unregister now done with exit hook
}
}}}
// perhaps useful during dynamic loading?
extern "C" {
void registerClientProvider_ca()
{
try {
CAClientFactory::start();
} catch(std::exception& e){
std::cerr<<"Error loading ca: "<<e.what()<<"\n";
}
}
} // extern "C"

View File

@@ -52,10 +52,6 @@ public:
virtual AccessRights getAccessRights(epics::pvData::PVField::shared_pointer const & pvField);
virtual ChannelProcess::shared_pointer createChannelProcess(
ChannelProcessRequester::shared_pointer const & channelProcessRequester,
epics::pvData::PVStructure::shared_pointer const & pvRequest);
virtual ChannelGet::shared_pointer createChannelGet(
ChannelGetRequester::shared_pointer const & channelGetRequester,
epics::pvData::PVStructure::shared_pointer const & pvRequest);
@@ -64,25 +60,13 @@ public:
ChannelPutRequester::shared_pointer const & channelPutRequester,
epics::pvData::PVStructure::shared_pointer const & pvRequest);
virtual ChannelPutGet::shared_pointer createChannelPutGet(
ChannelPutGetRequester::shared_pointer const & channelPutGetRequester,
epics::pvData::PVStructure::shared_pointer const & pvRequest);
virtual ChannelRPC::shared_pointer createChannelRPC(
ChannelRPCRequester::shared_pointer const & channelRPCRequester,
epics::pvData::PVStructure::shared_pointer const & pvRequest);
virtual Monitor::shared_pointer createMonitor(
MonitorRequester::shared_pointer const & monitorRequester,
epics::pvData::PVStructure::shared_pointer const & pvRequest);
virtual ChannelArray::shared_pointer createChannelArray(
ChannelArrayRequester::shared_pointer const & channelArrayRequester,
epics::pvData::PVStructure::shared_pointer const & pvRequest);
virtual void printInfo(std::ostream& out);
/* --------------- epics::pvData::Destroyable --------------- */
/* --------------- Destroyable --------------- */
virtual void destroy();
@@ -148,15 +132,10 @@ public:
virtual void cancel();
virtual void lastRequest();
/* --------------- epics::pvData::Destroyable --------------- */
/* --------------- Destroyable --------------- */
virtual void destroy();
/* --------------- epics::pvData::Lockable --------------- */
virtual void lock();
virtual void unlock();
private:
CAChannelGet(CAChannel::shared_pointer const & _channel,
@@ -208,15 +187,10 @@ public:
virtual void cancel();
virtual void lastRequest();
/* --------------- epics::pvData::Destroyable --------------- */
/* --------------- Destroyable --------------- */
virtual void destroy();
/* --------------- epics::pvData::Lockable --------------- */
virtual void lock();
virtual void unlock();
private:
CAChannelPut(CAChannel::shared_pointer const & _channel,
@@ -263,7 +237,7 @@ public:
virtual void cancel();
/* --------------- epics::pvData::Destroyable --------------- */
/* --------------- Destroyable --------------- */
virtual void destroy();

View File

@@ -16,6 +16,7 @@
namespace epics {
namespace pvAccess {
class Configuration;
namespace ca {
class epicsShareClass CAChannelProvider :
@@ -25,9 +26,8 @@ class epicsShareClass CAChannelProvider :
public:
POINTER_DEFINITIONS(CAChannelProvider);
static std::string PROVIDER_NAME;
CAChannelProvider();
CAChannelProvider(const std::tr1::shared_ptr<Configuration>&);
virtual ~CAChannelProvider();
/* --------------- epics::pvAccess::ChannelProvider --------------- */

View File

@@ -2,6 +2,13 @@
SRC_DIRS += $(PVACCESS_SRC)/client
INC += pv/monitor.h
INC += pv/pvAccess.h
INC += pva/client.h
LIBSRCS += pvAccess.cpp
pvAccess_SRCS += pvAccess.cpp
pvAccess_SRCS += client.cpp
pvAccess_SRCS += clientSync.cpp
pvAccess_SRCS += clientGet.cpp
pvAccess_SRCS += clientRPC.cpp
pvAccess_SRCS += clientMonitor.cpp

234
src/client/client.cpp Normal file
View File

@@ -0,0 +1,234 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <epicsMutex.h>
#include <epicsGuard.h>
#include <pv/pvData.h>
#include <pv/bitSet.h>
#define epicsExportSharedSymbols
#include "pv/logger.h"
#include "pva/client.h"
#include "pv/pvAccess.h"
#include "pv/configuration.h"
namespace pvd = epics::pvData;
namespace pva = epics::pvAccess;
typedef epicsGuard<epicsMutex> Guard;
namespace pvac {
Timeout::Timeout()
:std::runtime_error("Timeout")
{}
struct ClientChannel::Impl : public pva::ChannelRequester
{
epicsMutex mutex;
pva::Channel::shared_pointer channel;
// assume few listeners per channel, store in vector
typedef std::vector<ClientChannel::ConnectCallback*> listeners_t;
listeners_t listeners;
virtual ~Impl() {}
virtual std::string getRequesterName() OVERRIDE FINAL { return "ClientChannel::Impl"; }
virtual void channelCreated(const pvd::Status& status, pva::Channel::shared_pointer const & channel) OVERRIDE FINAL {}
virtual void channelStateChange(pva::Channel::shared_pointer const & channel, pva::Channel::ConnectionState connectionState) OVERRIDE FINAL
{
listeners_t notify;
{
Guard G(mutex);
notify = listeners;
}
ConnectEvent evt;
evt.connected = connectionState==pva::Channel::CONNECTED;
for(listeners_t::const_iterator it=notify.begin(), end=notify.end(); it!=end; ++it)
{
try {
(*it)->connectEvent(evt);
}catch(std::exception& e){
LOG(pva::logLevelError, "Unhandled exception in connection state listener: %s\n", e.what());
Guard G(mutex);
for(listeners_t::iterator it2=listeners.begin(), end2=listeners.end(); it2!=end2; ++it2) {
if(*it==*it2) {
listeners.erase(it2);
break;
}
}
}
}
}
};
ClientChannel::Options::Options()
:priority(0)
,address()
{}
bool ClientChannel::Options::operator<(const Options& O) const
{
return priority<O.priority || (priority==O.priority && address<O.address);
}
Operation::Operation(const std::tr1::shared_ptr<Impl>& i)
:impl(i)
{}
Operation::~Operation() {}
std::string Operation::name() const
{
return impl ? impl->name() : "<NULL>";
}
void Operation::cancel()
{
if(impl) impl->cancel();
}
ClientChannel::ClientChannel(const std::tr1::shared_ptr<pva::ChannelProvider>& provider,
const std::string& name,
const Options& opt)
:impl(new Impl)
{
if(name.empty())
THROW_EXCEPTION2(std::logic_error, "empty channel name not allowed");
if(!provider)
THROW_EXCEPTION2(std::logic_error, "NULL ChannelProvider");
impl->channel = provider->createChannel(name, impl, opt.priority, opt.address);
if(!impl->channel)
throw std::runtime_error("ChannelProvider failed to create Channel");
}
ClientChannel::~ClientChannel() {}
std::string ClientChannel::name() const
{
return impl ? impl->channel->getChannelName() : std::string();
}
void ClientChannel::addConnectListener(ConnectCallback* cb)
{
if(!impl) throw std::logic_error("Dead Channel");
ConnectEvent evt;
{
Guard G(impl->mutex);
for(Impl::listeners_t::const_iterator it=impl->listeners.begin(), end=impl->listeners.end(); it!=end; ++it)
{
if(cb==*it) return; // no duplicates
}
impl->listeners.push_back(cb);
evt.connected = impl->channel->isConnected();
}
try{
cb->connectEvent(evt);
}catch(...){
removeConnectListener(cb);
throw;
}
}
void ClientChannel::removeConnectListener(ConnectCallback* cb)
{
if(!impl) throw std::logic_error("Dead Channel");
Guard G(impl->mutex);
for(Impl::listeners_t::iterator it=impl->listeners.begin(), end=impl->listeners.end(); it!=end; ++it)
{
if(cb==*it) {
impl->listeners.erase(it);
return;
}
}
}
std::tr1::shared_ptr<epics::pvAccess::Channel>
ClientChannel::getChannel()
{ return impl->channel; }
struct ClientProvider::Impl
{
pva::ChannelProvider::shared_pointer provider;
epicsMutex mutex;
typedef std::map<std::pair<std::string, ClientChannel::Options>, std::tr1::weak_ptr<ClientChannel::Impl> > channels_t;
channels_t channels;
};
ClientProvider::ClientProvider(const std::string& providerName,
const std::tr1::shared_ptr<epics::pvAccess::Configuration>& conf)
:impl(new Impl)
{
std::string name;
pva::ChannelProviderRegistry::shared_pointer reg;
if(strncmp("server:", providerName.c_str(), 7)==0) {
name = providerName.substr(7);
reg = pva::ChannelProviderRegistry::servers();
} else if(strncmp("client:", providerName.c_str(), 7)==0) {
name = providerName.substr(7);
reg = pva::ChannelProviderRegistry::clients();
} else {
name = providerName;
reg = pva::ChannelProviderRegistry::clients();
}
impl->provider = reg->createProvider(name,
conf ? conf : pva::ConfigurationBuilder()
.push_env()
.build());
if(!impl->provider)
THROW_EXCEPTION2(std::invalid_argument, providerName);
}
ClientProvider::~ClientProvider() {}
ClientChannel
ClientProvider::connect(const std::string& name,
const ClientChannel::Options& conf)
{
Guard G(impl->mutex);
Impl::channels_t::key_type K(name, conf);
Impl::channels_t::iterator it(impl->channels.find(K));
if(it!=impl->channels.end()) {
// cache hit
std::tr1::shared_ptr<ClientChannel::Impl> chan(it->second.lock());
if(chan)
return ClientChannel(chan);
else
impl->channels.erase(it); // remove stale
}
// cache miss
ClientChannel ret(impl->provider, name, conf);
impl->channels[K] = ret.impl;
return ret;
}
bool ClientProvider::disconnect(const std::string& name,
const ClientChannel::Options& conf)
{
Guard G(impl->mutex);
Impl::channels_t::iterator it(impl->channels.find(std::make_pair(name, conf)));
bool found = it!=impl->channels.end();
if(found)
impl->channels.erase(it);
return found;
}
void ClientProvider::disconnect()
{
Guard G(impl->mutex);
impl->channels.clear();
}
} //namespace pvac

213
src/client/clientGet.cpp Normal file
View File

@@ -0,0 +1,213 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <epicsMutex.h>
#include <epicsGuard.h>
#include <epicsEvent.h>
#include <pv/current_function.h>
#include <pv/pvData.h>
#include <pv/bitSet.h>
#define epicsExportSharedSymbols
#include "pv/logger.h"
#include "pva/client.h"
#include "pv/pvAccess.h"
namespace pvd = epics::pvData;
namespace pva = epics::pvAccess;
typedef epicsGuard<epicsMutex> Guard;
typedef epicsGuardRelease<epicsMutex> UnGuard;
namespace {
struct GetPutter : public pva::ChannelPutRequester,
public pvac::Operation::Impl
{
mutable epicsMutex mutex;
bool started;
operation_type::shared_pointer op;
pvac::ClientChannel::GetCallback *getcb;
pvac::ClientChannel::PutCallback *putcb;
pvac::GetEvent event;
GetPutter(pvac::ClientChannel::GetCallback* cb) :started(false), getcb(cb), putcb(0) {}
GetPutter(pvac::ClientChannel::PutCallback* cb) :started(false), getcb(0), putcb(cb) {}
virtual ~GetPutter() {cancel();}
void callEvent(Guard& G, pvac::GetEvent::event_t evt = pvac::GetEvent::Fail)
{
if(!putcb && !getcb) return;
event.event = evt;
if(putcb) {
pvac::ClientChannel::PutCallback *cb=putcb;
putcb = 0;
UnGuard U(G);
cb->putDone(event);
}
if(getcb) {
pvac::ClientChannel::GetCallback *cb=getcb;
getcb = 0;
UnGuard U(G);
cb->getDone(event);
}
}
virtual std::string name() const OVERRIDE FINAL
{
Guard G(mutex);
return op ? op->getChannel()->getChannelName() : "<dead>";
}
virtual void cancel() OVERRIDE FINAL
{
Guard G(mutex);
if(started && op) op->cancel();
callEvent(G, pvac::GetEvent::Cancel);
}
virtual std::string getRequesterName() OVERRIDE FINAL
{ return "GetPutter"; }
virtual void channelPutConnect(
const epics::pvData::Status& status,
pva::ChannelPut::shared_pointer const & channelPut,
epics::pvData::Structure::const_shared_pointer const & structure) OVERRIDE FINAL
{
Guard G(mutex);
if(started) return;
if(!putcb && !getcb) return;
if(!status.isOK()) {
event.message = status.getMessage();
} else {
event.message.clear();
}
if(!status.isSuccess()) {
callEvent(G);
} else if(getcb){
channelPut->lastRequest();
channelPut->get();
started = true;
} else if(putcb){
pvac::ClientChannel::PutCallback *cb(putcb);
pvd::BitSet::shared_pointer tosend(new pvd::BitSet);
pvac::ClientChannel::PutCallback::Args args(*tosend);
try {
UnGuard U(G);
cb->putBuild(structure, args);
if(!args.root)
throw std::logic_error("No put value provided");
else if(args.root->getStructure().get()!=structure.get())
throw std::logic_error("Provided put value with wrong type");
}catch(std::exception& e){
if(putcb) {
event.message = e.what();
callEvent(G);
} else {
LOG(pva::logLevelInfo, "Lost exception in %s: %s", CURRENT_FUNCTION, e.what());
}
}
// check putcb again after UnGuard
if(putcb) {
channelPut->lastRequest();
channelPut->put(std::tr1::const_pointer_cast<pvd::PVStructure>(args.root), tosend);
started = true;
}
}
}
virtual void channelDisconnect(bool destroy) OVERRIDE FINAL
{
Guard G(mutex);
event.message = "Disconnect";
callEvent(G);
}
virtual void putDone(
const epics::pvData::Status& status,
pva::ChannelPut::shared_pointer const & channelPut) OVERRIDE FINAL
{
Guard G(mutex);
if(!putcb) return;
if(!status.isOK()) {
event.message = status.getMessage();
} else {
event.message.clear();
}
callEvent(G, status.isSuccess()? pvac::GetEvent::Success : pvac::GetEvent::Fail);
}
virtual void getDone(
const epics::pvData::Status& status,
pva::ChannelPut::shared_pointer const & channelPut,
epics::pvData::PVStructure::shared_pointer const & pvStructure,
epics::pvData::BitSet::shared_pointer const & bitSet) OVERRIDE FINAL
{
Guard G(mutex);
if(!getcb) return;
if(!status.isOK()) {
event.message = status.getMessage();
} else {
event.message.clear();
}
event.value = pvStructure;
// assume bitSet->get(0)==true as we only make one request
callEvent(G, status.isSuccess()? pvac::GetEvent::Success : pvac::GetEvent::Fail);
}
};
} //namespace
namespace pvac {
Operation
ClientChannel::get(ClientChannel::GetCallback* cb,
epics::pvData::PVStructure::const_shared_pointer pvRequest)
{
if(!impl) throw std::logic_error("Dead Channel");
if(!pvRequest)
pvRequest = pvd::createRequest("field()");
std::tr1::shared_ptr<GetPutter> ret(new GetPutter(cb));
{
Guard G(ret->mutex);
ret->op = getChannel()->createChannelPut(ret, std::tr1::const_pointer_cast<pvd::PVStructure>(pvRequest));
}
return Operation(ret);
}
Operation
ClientChannel::put(PutCallback* cb,
epics::pvData::PVStructure::const_shared_pointer pvRequest)
{
if(!impl) throw std::logic_error("Dead Channel");
if(!pvRequest)
pvRequest = pvd::createRequest("field()");
std::tr1::shared_ptr<GetPutter> ret(new GetPutter(cb));
{
Guard G(ret->mutex);
ret->op = getChannel()->createChannelPut(ret, std::tr1::const_pointer_cast<pvd::PVStructure>(pvRequest));
}
return Operation(ret);
}
}//namespace pvac

View File

@@ -0,0 +1,217 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <epicsMutex.h>
#include <epicsGuard.h>
#include <pv/current_function.h>
#include <pv/pvData.h>
#include <pv/bitSet.h>
#define epicsExportSharedSymbols
#include "pv/logger.h"
#include "pva/client.h"
#include "pv/pvAccess.h"
namespace pvd = epics::pvData;
namespace pva = epics::pvAccess;
typedef epicsGuard<epicsMutex> Guard;
typedef epicsGuardRelease<epicsMutex> UnGuard;
namespace pvac {
struct Monitor::Impl : public pva::MonitorRequester
{
mutable epicsMutex mutex;
pva::Channel::shared_pointer chan;
operation_type::shared_pointer op;
bool started, done, seenEmpty;
ClientChannel::MonitorCallback *cb;
MonitorEvent event;
pva::MonitorElement::Ref last;
Impl(ClientChannel::MonitorCallback* cb)
:started(false)
,done(false)
,seenEmpty(false)
,cb(cb)
{}
virtual ~Impl() {cancel();}
void callEvent(Guard& G, MonitorEvent::event_t evt = MonitorEvent::Fail)
{
ClientChannel::MonitorCallback *cb=this->cb;
if(!cb) return;
event.event = evt;
if(evt==MonitorEvent::Fail || evt==MonitorEvent::Cancel)
this->cb = 0; // last event
try {
UnGuard U(G);
cb->monitorEvent(event);
return;
}catch(std::exception& e){
if(!this->cb || evt==MonitorEvent::Fail) {
LOG(pva::logLevelError, "Unhandled exception in ClientChannel::MonitorCallback::monitorEvent(): %s", e.what());
} else {
event.event = MonitorEvent::Fail;
event.message = e.what();
}
}
// continues error handling
try {
UnGuard U(G);
cb->monitorEvent(event);
return;
}catch(std::exception& e){
LOG(pva::logLevelError, "Unhandled exception following exception in ClientChannel::MonitorCallback::monitorEvent(): %s", e.what());
}
}
void cancel()
{
Guard G(mutex);
last.reset();
if(started) {
op->stop();
started = false;
}
op->destroy();
callEvent(G, MonitorEvent::Cancel);
}
virtual std::string getRequesterName() OVERRIDE FINAL
{ return "RPCer"; }
virtual void monitorConnect(pvd::Status const & status,
pva::MonitorPtr const & operation,
pvd::StructureConstPtr const & structure) OVERRIDE FINAL
{
Guard G(mutex);
if(!cb || started || done) return;
if(!status.isOK()) {
event.message = status.getMessage();
} else {
event.message.clear();
}
if(!status.isSuccess()) {
callEvent(G);
} else {
pvd::Status sts(operation->start());
if(sts.isSuccess()) {
started = true;
last.attach(operation);
} else {
event.message = sts.getMessage();
callEvent(G);
}
}
}
virtual void channelDisconnect(bool destroy) OVERRIDE FINAL
{
Guard G(mutex);
if(!cb || done) return;
event.message = "Disconnect";
started = false;
callEvent(G, MonitorEvent::Disconnect);
}
virtual void monitorEvent(pva::MonitorPtr const & monitor) OVERRIDE FINAL
{
Guard G(mutex);
if(!cb || done) return;
event.message.clear();
callEvent(G, MonitorEvent::Data);
}
virtual void unlisten(pva::MonitorPtr const & monitor) OVERRIDE FINAL
{
Guard G(mutex);
if(!cb || done) return;
done = true;
if(seenEmpty)
callEvent(G, MonitorEvent::Data);
// else // wait until final poll()
}
};
Monitor::Monitor(const std::tr1::shared_ptr<Impl>& impl)
:impl(impl)
{}
Monitor::~Monitor() {}
std::string Monitor::name() const
{
return impl ? impl->chan->getChannelName() : "<NULL>";
}
void Monitor::cancel()
{
changed.clear();
overrun.clear();
root.reset();
if(impl) impl->cancel();
}
bool Monitor::poll()
{
if(!impl) return false;
Guard G(impl->mutex);
if(!impl->done && impl->last.next()) {
root = impl->last->pvStructurePtr;
changed = *impl->last->changedBitSet;
overrun = *impl->last->overrunBitSet;
} else {
root.reset();
changed.clear();
overrun.clear();
}
return impl->seenEmpty = !!root;
}
bool Monitor::complete() const
{
if(!impl) return true;
Guard G(impl->mutex);
return impl->done && impl->seenEmpty;
}
Monitor
ClientChannel::monitor(MonitorCallback *cb,
epics::pvData::PVStructure::const_shared_pointer pvRequest)
{
if(!impl) throw std::logic_error("Dead Channel");
if(!pvRequest)
pvRequest = pvd::createRequest("field()");
std::tr1::shared_ptr<Monitor::Impl> ret(new Monitor::Impl(cb));
ret->chan = getChannel();
{
Guard G(ret->mutex);
ret->op = ret->chan->createMonitor(ret, std::tr1::const_pointer_cast<pvd::PVStructure>(pvRequest));
}
return Monitor(ret);
}
}//namespace pvac

159
src/client/clientRPC.cpp Normal file
View File

@@ -0,0 +1,159 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <epicsMutex.h>
#include <epicsGuard.h>
#include <pv/pvData.h>
#include <pv/bitSet.h>
#define epicsExportSharedSymbols
#include "pv/logger.h"
#include "pva/client.h"
#include "pv/pvAccess.h"
namespace pvd = epics::pvData;
namespace pva = epics::pvAccess;
typedef epicsGuard<epicsMutex> Guard;
typedef epicsGuardRelease<epicsMutex> UnGuard;
namespace {
struct RPCer : public pva::ChannelRPCRequester,
public pvac::Operation::Impl
{
mutable epicsMutex mutex;
bool started;
operation_type::shared_pointer op;
pvac::ClientChannel::GetCallback *cb;
pvac::GetEvent event;
pvd::PVStructure::const_shared_pointer args;
RPCer(pvac::ClientChannel::GetCallback* cb,
const pvd::PVStructure::const_shared_pointer& args) :started(false), cb(cb), args(args) {}
virtual ~RPCer() {cancel();}
void callEvent(Guard& G, pvac::GetEvent::event_t evt = pvac::GetEvent::Fail)
{
pvac::ClientChannel::GetCallback *cb=this->cb;
if(!cb) return;
event.event = evt;
this->cb = 0;
try {
UnGuard U(G);
cb->getDone(event);
return;
}catch(std::exception& e){
if(!this->cb || evt==pvac::GetEvent::Fail) {
LOG(pva::logLevelError, "Unhandled exception in ClientChannel::GetCallback::getDone(): %s", e.what());
} else {
event.event = pvac::GetEvent::Fail;
event.message = e.what();
}
}
// continues error handling
try {
UnGuard U(G);
cb->getDone(event);
return;
}catch(std::exception& e){
LOG(pva::logLevelError, "Unhandled exception following exception in ClientChannel::GetCallback::monitorEvent(): %s", e.what());
}
}
virtual std::string name() const OVERRIDE FINAL
{
Guard G(mutex);
return op ? op->getChannel()->getChannelName() : "<dead>";
}
virtual void cancel()
{
Guard G(mutex);
if(started && op) op->cancel();
callEvent(G, pvac::GetEvent::Cancel);
}
virtual std::string getRequesterName() OVERRIDE FINAL
{ return "RPCer"; }
virtual void channelRPCConnect(
const epics::pvData::Status& status,
pva::ChannelRPC::shared_pointer const & operation)
{
Guard G(mutex);
if(!cb || started) return;
if(!status.isOK()) {
event.message = status.getMessage();
} else {
event.message.clear();
}
if(!status.isSuccess()) {
callEvent(G);
} else {
operation->request(std::tr1::const_pointer_cast<pvd::PVStructure>(args));
started = true;
}
}
virtual void channelDisconnect(bool destroy) OVERRIDE FINAL
{
Guard G(mutex);
event.message = "Disconnect";
callEvent(G);
}
virtual void requestDone(
const epics::pvData::Status& status,
pva::ChannelRPC::shared_pointer const & operation,
epics::pvData::PVStructure::shared_pointer const & pvResponse)
{
Guard G(mutex);
if(!cb) return;
if(!status.isOK()) {
event.message = status.getMessage();
} else {
event.message.clear();
}
event.value = pvResponse;
callEvent(G, status.isSuccess()? pvac::GetEvent::Success : pvac::GetEvent::Fail);
}
};
}//namespace
namespace pvac {
Operation
ClientChannel::rpc(GetCallback* cb,
const epics::pvData::PVStructure::const_shared_pointer& arguments,
epics::pvData::PVStructure::const_shared_pointer pvRequest)
{
if(!impl) throw std::logic_error("Dead Channel");
if(!pvRequest)
pvRequest = pvd::createRequest("field()");
std::tr1::shared_ptr<RPCer> ret(new RPCer(cb, arguments));
{
Guard G(ret->mutex);
ret->op = getChannel()->createChannelRPC(ret, std::tr1::const_pointer_cast<pvd::PVStructure>(pvRequest));
}
return Operation(ret);
}
}//namespace pvac

303
src/client/clientSync.cpp Normal file
View File

@@ -0,0 +1,303 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <epicsMutex.h>
#include <epicsGuard.h>
#include <epicsEvent.h>
#include <pv/current_function.h>
#include <pv/pvData.h>
#include <pv/bitSet.h>
#define epicsExportSharedSymbols
#include "pv/logger.h"
#include "pva/client.h"
#include "pv/pvAccess.h"
namespace pvd = epics::pvData;
namespace pva = epics::pvAccess;
typedef epicsGuard<epicsMutex> Guard;
typedef epicsGuardRelease<epicsMutex> UnGuard;
namespace {
struct WaitCommon
{
epicsMutex mutex;
epicsEvent event;
bool done;
WaitCommon() :done(false) {}
void wait(double timeout)
{
Guard G(mutex);
while(!done) {
UnGuard U(G);
if(!event.wait(timeout)) {
throw pvac::Timeout();
}
}
}
};
struct GetWait : public pvac::ClientChannel::GetCallback,
public WaitCommon
{
pvac::GetEvent result;
GetWait() {}
virtual ~GetWait() {}
virtual void getDone(const pvac::GetEvent& evt) OVERRIDE FINAL
{
{
Guard G(mutex);
if(done) {
LOG(pva::logLevelWarn, "oops, double event to GetCallback");
} else {
result = evt;
done = true;
}
}
event.signal();
}
};
} //namespace
namespace pvac {
pvd::PVStructure::const_shared_pointer
pvac::ClientChannel::get(double timeout,
pvd::PVStructure::const_shared_pointer pvRequest)
{
GetWait waiter;
Operation op(get(&waiter, pvRequest));
waiter.wait(timeout);
if(waiter.result.event==pvac::GetEvent::Success)
return waiter.result.value;
else
throw std::runtime_error(waiter.result.message);
}
pvd::PVStructure::const_shared_pointer
pvac::ClientChannel::rpc(double timeout,
const epics::pvData::PVStructure::const_shared_pointer& arguments,
epics::pvData::PVStructure::const_shared_pointer pvRequest)
{
GetWait waiter;
Operation op(rpc(&waiter, arguments, pvRequest));
{
Guard G(waiter.mutex);
while(!waiter.done) {
UnGuard U(G);
if(!waiter.event.wait(timeout)) {
op.cancel();
throw Timeout();
}
}
}
if(waiter.result.event==pvac::GetEvent::Success)
return waiter.result.value;
else
throw std::runtime_error(waiter.result.message);
}
namespace {
struct PutValCommon : public pvac::ClientChannel::PutCallback,
public WaitCommon
{
pvac::PutEvent result;
PutValCommon() {}
virtual ~PutValCommon() {}
virtual void putDone(const PutEvent& evt) OVERRIDE FINAL
{
{
Guard G(mutex);
if(done) {
LOG(pva::logLevelWarn, "oops, double event to PutCallback");
} else {
result = evt;
done = true;
}
}
event.signal();
}
};
struct PutValScalar : public PutValCommon
{
const void* value;
pvd::ScalarType vtype;
PutValScalar(const void* value, pvd::ScalarType vtype) :value(value), vtype(vtype) {}
virtual ~PutValScalar() {}
virtual void putBuild(const epics::pvData::StructureConstPtr& build, Args& args) OVERRIDE FINAL
{
pvd::PVStructurePtr root(pvd::getPVDataCreate()->createPVStructure(build));
pvd::PVScalarPtr value(root->getSubField<pvd::PVScalar>("value"));
if(value) {
value->putFrom(this->value, vtype);
args.tosend.set(value->getFieldOffset());
} else {
// TODO: handle enum
throw std::runtime_error("PV has no scalar 'value' sub-field");
}
args.root = root;
}
};
struct PutValArray : public PutValCommon
{
pvd::shared_vector<const void> arr;
PutValArray(const pvd::shared_vector<const void>& arr) :arr(arr) {}
virtual ~PutValArray() {}
virtual void putBuild(const epics::pvData::StructureConstPtr& build, Args& args) OVERRIDE FINAL
{
pvd::PVStructurePtr root(pvd::getPVDataCreate()->createPVStructure(build));
pvd::PVScalarArrayPtr value(root->getSubField<pvd::PVScalarArray>("value"));
if(value) {
value->putFrom(arr);
args.tosend.set(value->getFieldOffset());
} else {
throw std::runtime_error("PV has no scalar array 'value' sub-field");
}
args.root = root;
}
};
} //namespace
void
ClientChannel::putValue(const void* value,
pvd::ScalarType vtype,
double timeout,
const epics::pvData::PVStructure::const_shared_pointer &pvRequest)
{
PutValScalar waiter(value, vtype);
Operation op(put(&waiter, pvRequest));
waiter.wait(timeout);
if(waiter.result.event==PutEvent::Success)
return;
else
throw std::runtime_error(waiter.result.message);
}
void
ClientChannel::putValue(const epics::pvData::shared_vector<const void>& value,
double timeout,
const epics::pvData::PVStructure::const_shared_pointer &pvRequest)
{
PutValArray waiter(value);
Operation op(put(&waiter, pvRequest));
waiter.wait(timeout);
if(waiter.result.event==PutEvent::Success)
return;
else
throw std::runtime_error(waiter.result.message);
}
struct MonitorSync::SImpl : public ClientChannel::MonitorCallback
{
const bool ourevent;
epicsEvent * const event;
epicsMutex mutex;
bool hadevent;
MonitorEvent last;
// maintained to ensure we (MonitorCallback) outlive the subscription
Monitor sub;
SImpl(epicsEvent *event)
:ourevent(!event)
,event(ourevent ? new epicsEvent : event)
{}
virtual ~SImpl()
{
sub.cancel();
if(ourevent)
delete event;
}
virtual void monitorEvent(const MonitorEvent& evt) OVERRIDE FINAL
{
{
Guard G(mutex);
last = evt;
hadevent = true;
}
event->signal();
}
};
MonitorSync::MonitorSync(const Monitor& mon, const std::tr1::shared_ptr<SImpl>& simpl)
:Monitor(mon.impl)
,simpl(simpl)
{
simpl->sub = mon;
event.event = MonitorEvent::Fail;
}
MonitorSync::~MonitorSync() {
std::cout<<"SYNC use_count="<<simpl.use_count()<<"\n";
}
bool MonitorSync::poll()
{
if(!simpl) throw std::logic_error("No subscription");
Guard G(simpl->mutex);
event = simpl->last;
simpl->last.event = MonitorEvent::Fail;
bool ret = simpl->hadevent;
simpl->hadevent = false;
return ret;
}
bool MonitorSync::wait()
{
if(!simpl) throw std::logic_error("No subscription");
simpl->event->wait();
Guard G(simpl->mutex);
event = simpl->last;
simpl->last.event = MonitorEvent::Fail;
bool ret = simpl->hadevent;
simpl->hadevent = false;
return ret;
}
bool MonitorSync::wait(double timeout)
{
if(!simpl) throw std::logic_error("No subscription");
bool ret = simpl->event->wait(timeout);
if(ret) {
Guard G(simpl->mutex);
event = simpl->last;
simpl->last.event = MonitorEvent::Fail;
ret = simpl->hadevent;
simpl->hadevent = false;
}
return ret;
}
void MonitorSync::wake() {
if(simpl) simpl->event->signal();
}
MonitorSync
ClientChannel::monitor(const epics::pvData::PVStructure::const_shared_pointer &pvRequest,
epicsEvent *event)
{
std::tr1::shared_ptr<MonitorSync::SImpl> simpl(new MonitorSync::SImpl(event));
Monitor mon(monitor(simpl.get(), pvRequest));
return MonitorSync(mon, simpl);
}
}//namespace pvac

211
src/client/pv/monitor.h Normal file
View File

@@ -0,0 +1,211 @@
/* monitor.h */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mrk
*/
#ifndef MONITOR_H
#define MONITOR_H
#ifdef epicsExportSharedSymbols
# define monitorEpicsExportSharedSymbols
# undef epicsExportSharedSymbols
#endif
#include <pv/status.h>
#include <pv/pvData.h>
#include <pv/sharedPtr.h>
#include <pv/bitSet.h>
#ifdef monitorEpicsExportSharedSymbols
# define epicsExportSharedSymbols
# undef monitorEpicsExportSharedSymbols
#endif
#include <pv/requester.h>
#include <pv/destroyable.h>
#include <shareLib.h>
namespace epics { namespace pvAccess {
class MonitorRequester;
class MonitorElement;
typedef std::tr1::shared_ptr<MonitorElement> MonitorElementPtr;
typedef std::vector<MonitorElementPtr> MonitorElementPtrArray;
class Monitor;
typedef std::tr1::shared_ptr<Monitor> MonitorPtr;
/**
* @brief An element for a monitorQueue.
*
* Class instance representing monitor element.
* @author mrk
*/
class epicsShareClass MonitorElement {
public:
POINTER_DEFINITIONS(MonitorElement);
MonitorElement(){}
MonitorElement(epics::pvData::PVStructurePtr const & pvStructurePtr);
const epics::pvData::PVStructurePtr pvStructurePtr;
const epics::pvData::BitSet::shared_pointer changedBitSet;
const epics::pvData::BitSet::shared_pointer overrunBitSet;
// info to assist monitor debugging
enum state_t {
Free, //!< data invalid. eg. on internal free list
Queued, //!< data valid. Owned by Monitor. Waiting for Monitor::poll()
InUse //!< data valid. Owned by MonitorRequester. Waiting for Monitor::release()
} state;
class Ref;
};
/** Access to Monitor subscription and queue
*
* Downstream interface to access a monitor queue (via poll() and release() )
*/
class epicsShareClass Monitor : public virtual Destroyable{
public:
POINTER_DEFINITIONS(Monitor);
typedef MonitorRequester requester_type;
virtual ~Monitor(){}
/**
* Start monitoring.
* @return completion status.
*/
virtual epics::pvData::Status start() = 0;
/**
* Stop Monitoring.
* @return completion status.
*/
virtual epics::pvData::Status stop() = 0;
/**
* If monitor has occurred return data.
* @return monitorElement for modified data.
* Must call get to determine if data is available.
*
* May recursively call MonitorRequester::unlisten()
*/
virtual MonitorElementPtr poll() = 0;
/**
* Release a MonitorElement that was returned by poll.
* A poll() must be called after the release() to check the presence of any modified data.
* @param monitorElement
*/
virtual void release(MonitorElementPtr const & monitorElement) = 0;
struct Stats {
size_t nfilled; //!< # of elements ready to be poll()d
size_t noutstanding; //!< # of elements poll()d but not released()d
size_t nempty; //!< # of elements available for new remote data
};
virtual void getStats(Stats& s) const {
s.nfilled = s.noutstanding = s.nempty = 0;
}
/**
* Report remote queue status.
* @param freeElements number of free elements.
*/
virtual void reportRemoteQueueStatus(epics::pvData::int32 freeElements) {}
};
/** A smart pointer to extract a MonitorElement from a Monitor queue
*
* To fetch a single element
@code
epics::pvAccess::Monitor::shared_pointer mon(....);
epics::pvAccess::MonitorElement::Ref elem(mon);
if(elem) {
// do something with element
assert(elem->pvStructurePtr->getSubField("foo"));
} else {
// queue was empty
}
@endcode
* To fetch all available elements (c++11)
@code
epics::pvAccess::Monitor::shared_pointer mon(....);
for(auto& elem : *mon) {
assert(elem.pvStructurePtr->getSubField("foo"));
}
@endcode
* To fetch all available elements (c++98)
@code
epics::pvAccess::Monitor::shared_pointer mon(....);
for(epics::pvAccess::MonitorElement::Ref it(mon); it; ++it) {
MonitorElement& elem(*it);
assert(elem.pvStructurePtr->getSubField("foo"));
}
@endcode
*/
class MonitorElement::Ref
{
Monitor* mon;
MonitorElementPtr elem;
public:
Ref() :mon(0), elem() {}
Ref(Monitor& M) :mon(&M), elem(mon->poll()) {}
Ref(const Monitor::shared_pointer& M) :mon(M.get()), elem(mon->poll()) {}
~Ref() { reset(); }
void attach(Monitor& M) {
reset();
mon = &M;
}
void attach(const Monitor::shared_pointer& M) {
reset();
mon = M.get();
}
bool next() {
if(elem) mon->release(elem);
elem = mon->poll();
return !!elem;
}
void reset() {
if(elem && mon) mon->release(elem);
elem.reset();
}
Ref& operator++() {// prefix increment. aka "++(*this)"
next();
return *this;
}
#if __cplusplus>=201103L
inline explicit operator bool() const { return elem.get(); }
#else
private:
typedef const Monitor* const * hidden_bool_type;
public:
operator hidden_bool_type() const { return elem.get() ? &mon : 0; }
#endif
inline MonitorElement* operator->() { return elem.get(); }
inline MonitorElement& operator*() { return *elem; }
inline MonitorElement* get() { return elem.get(); }
inline bool operator==(const Ref& o) const { return elem==o.elem; }
inline bool operator!=(const Ref& o) const { return !(*this==o); }
};
#if __cplusplus>=201103L
// used by c++11 for-range
inline MonitorElement::Ref begin(Monitor& mon) { return MonitorElement::Ref(mon); }
inline MonitorElement::Ref end(Monitor& mon) { return MonitorElement::Ref(); }
#endif // __cplusplus<201103L
}}
namespace epics { namespace pvData {
using epics::pvAccess::MonitorElement;
using epics::pvAccess::MonitorElementPtr;
using epics::pvAccess::MonitorElementPtrArray;
using epics::pvAccess::Monitor;
using epics::pvAccess::MonitorPtr;
}}
#endif /* MONITOR_H */

File diff suppressed because it is too large Load Diff

View File

@@ -14,6 +14,9 @@ namespace pvAccess {
const char* Channel::ConnectionStateNames[] = { "NEVER_CONNECTED", "CONNECTED", "DISCONNECTED", "DESTROYED" };
Channel::Channel() {}
Channel::~Channel() {}
std::string Channel::getRequesterName()
{
std::tr1::shared_ptr<ChannelRequester> req(getChannelRequester());
@@ -32,6 +35,21 @@ void Channel::message(std::string const & message, epics::pvData::MessageType me
}
}
Channel::ConnectionState Channel::getConnectionState() { return CONNECTED; }
bool Channel::isConnected() { return getConnectionState()==CONNECTED; }
void Channel::getField(GetFieldRequester::shared_pointer const & requester,std::string const & subField)
{
requester->getDone(pvd::Status(pvd::Status::STATUSTYPE_FATAL, "Not Implemented")
,pvd::FieldConstPtr());
}
AccessRights Channel::getAccessRights(epics::pvData::PVField::shared_pointer const & pvField)
{
return readWrite;
}
ChannelProcess::shared_pointer Channel::createChannelProcess(
ChannelProcessRequester::shared_pointer const & requester,
epics::pvData::PVStructure::shared_pointer const & pvRequest)
@@ -81,7 +99,7 @@ ChannelRPC::shared_pointer Channel::createChannelRPC(
}
pvd::Monitor::shared_pointer Channel::createMonitor(
epics::pvData::MonitorRequester::shared_pointer const & requester,
epics::pvAccess::MonitorRequester::shared_pointer const & requester,
epics::pvData::PVStructure::shared_pointer const & pvRequest)
{
pvd::Monitor::shared_pointer ret;
@@ -100,5 +118,32 @@ ChannelArray::shared_pointer Channel::createChannelArray(
return ret;
}
std::string DefaultChannelRequester::getRequesterName() { return "DefaultChannelRequester"; }
void DefaultChannelRequester::channelCreated(const epics::pvData::Status& status, Channel::shared_pointer const & channel)
{
if(!status.isSuccess()) {
std::ostringstream strm;
status.dump(strm);
throw std::runtime_error(strm.str());
}
}
void DefaultChannelRequester::channelStateChange(Channel::shared_pointer const & channel, Channel::ConnectionState connectionState)
{ /* no-op */ }
ChannelRequester::shared_pointer DefaultChannelRequester::build()
{
ChannelRequester::shared_pointer ret(new DefaultChannelRequester);
return ret;
}
MonitorElement::MonitorElement(epics::pvData::PVStructurePtr const & pvStructurePtr)
: pvStructurePtr(pvStructurePtr)
,changedBitSet(epics::pvData::BitSet::create(static_cast<epics::pvData::uint32>(pvStructurePtr->getNumberFields())))
,overrunBitSet(epics::pvData::BitSet::create(static_cast<epics::pvData::uint32>(pvStructurePtr->getNumberFields())))
,state(Free)
{}
}} // namespace epics::pvAccess

391
src/client/pva/client.h Normal file
View File

@@ -0,0 +1,391 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#ifndef PVATESTCLIENT_H
#define PVATESTCLIENT_H
#include <stdexcept>
#include <epicsMutex.h>
#include <pv/pvData.h>
#include <pv/bitSet.h>
class epicsEvent;
namespace epics {namespace pvAccess {
class ChannelProvider;
class Channel;
class Monitor;
class Configuration;
}}//namespace epics::pvAccess
//! See @ref pvac API
namespace pvac {
/** @defgroup pvac Client API
*
* PVAccess network client (or other epics::pvAccess::ChannelProvider)
*
* Usage:
*
* 1. Construct a ClientProvider
* 2. Use the ClientProvider to obtain a ClientChannel
* 3. Use the ClientChannel to begin an get, put, rpc, or monitor operation
*
* Code examples
*
* - @ref examples_getme
* - @ref examples_putme
* - @ref examples_monitorme
* @{
*/
class ClientProvider;
//! Handle for in-progress get/put/rpc operation
struct epicsShareClass Operation
{
struct Impl
{
virtual ~Impl() {}
virtual std::string name() const =0;
virtual void cancel() =0;
};
Operation() {}
Operation(const std::tr1::shared_ptr<Impl>&);
~Operation();
//! Channel name
std::string name() const;
//! Immediate cancellation.
//! Does not wait for remote confirmation.
void cancel();
protected:
std::tr1::shared_ptr<Impl> impl;
};
//! Information on put completion
struct PutEvent
{
enum event_t {
Fail, //!< request ends in failure. Check message
Cancel, //!< request cancelled before completion
Success, //!< It worked!
} event;
std::string message;
void *priv;
};
//! Information on get/rpc completion
struct epicsShareClass GetEvent : public PutEvent
{
//! New data. NULL unless event==Success
epics::pvData::PVStructure::const_shared_pointer value;
};
struct MonitorSync;
//! Handle for monitor subscription
struct epicsShareClass Monitor
{
struct Impl;
Monitor() {}
Monitor(const std::tr1::shared_ptr<Impl>&);
~Monitor();
//! Channel name
std::string name() const;
//! Immediate cancellation.
//! Does not wait for remote confirmation.
void cancel();
/** updates root, changed, overrun
*
* @return true if root!=NULL
* @note MonitorEvent::Data will not be repeated until poll()==false.
*/
bool poll();
//! true if all events received.
//! Check after poll()==false
bool complete() const;
epics::pvData::PVStructure::const_shared_pointer root;
epics::pvData::BitSet changed,
overrun;
private:
std::tr1::shared_ptr<Impl> impl;
friend struct MonitorSync;
};
//! Information on monitor subscription/queue change
struct MonitorEvent
{
enum event_t {
Fail=1, //!< subscription ends in an error
Cancel=2, //!< subscription ends in cancellation
Disconnect=4,//!< subscription interrupted to do lose of communication
Data=8, //!< Data queue not empty. Call Monitor::poll()
} event;
std::string message; // set for event=Fail
};
/** Subscription usable w/o callbacks
*
* Basic usage is to call wait().
* If true is returned, then the 'event', 'root', 'changed', and 'overrun'
* members have been updated with a new event.
* Test 'event.event' first to find out which kind of event has occured.
*/
struct epicsShareClass MonitorSync : public Monitor
{
struct SImpl;
MonitorSync() {}
MonitorSync(const Monitor&, const std::tr1::shared_ptr<SImpl>&);
~MonitorSync();
//! wait for new event
bool wait();
//! wait for new event
//! @return false on timeout
bool wait(double timeout);
//! check if new event is available
bool poll();
//! Abort one call to wait()
//! wait() will return with MonitorEvent::Fail
void wake();
//! most recent event
//! updated only during wait() or poll()
MonitorEvent event;
private:
std::tr1::shared_ptr<SImpl> simpl;
};
//! information on connect/disconnect
struct ConnectEvent
{
bool connected;
};
//! Thrown by blocking methods of ClientChannel on operation timeout
struct Timeout : public std::runtime_error
{
Timeout();
};
/** Represents a single channel
*
* This class has two sets of methods, those which block for completion, and
* those which use callbacks to signal completion.
*
* Those which block accept a 'timeout' argument (in seconds).
*
* Those which use callbacks accept a 'cb' argument and return an Operation or Monitor handle object.
*/
class epicsShareClass ClientChannel
{
struct Impl;
std::tr1::shared_ptr<Impl> impl;
friend class ClientProvider;
ClientChannel(const std::tr1::shared_ptr<Impl>& i) :impl(i) {}
public:
//! Channel creation options
struct epicsShareClass Options {
short priority;
std::string address;
Options();
bool operator<(const Options&) const;
};
//! Construct a null channel. All methods throw. May later be assigned from a valid ClientChannel
ClientChannel() {}
/** Construct a ClientChannel using epics::pvAccess::ChannelProvider::createChannel()
*
* Does not block.
* @throw std::logic_error if the provider is NULL or name is an empty string
* @throw std::runtime_error if the ChannelProvider can't provide
*/
ClientChannel(const std::tr1::shared_ptr<epics::pvAccess::ChannelProvider>& provider,
const std::string& name,
const Options& opt = Options());
~ClientChannel();
//! Channel name or an empty string
std::string name() const;
//! callback for get() and rpc()
struct GetCallback {
virtual ~GetCallback() {}
//! get or rpc operation is complete
virtual void getDone(const GetEvent& evt)=0;
};
//! Issue request to retrieve current PV value
//! @param cb Completion notification callback. Must outlive Operation (call Operation::cancel() to force release)
//! @param pvRequest if NULL defaults to "field()".
Operation get(GetCallback* cb,
epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer());
//! Block and retrieve current PV value
//! @param timeout in seconds
//! @param pvRequest if NULL defaults to "field()".
//! @throws Timeout or std::runtime_error
epics::pvData::PVStructure::const_shared_pointer
get(double timeout = 3.0,
epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer());
//! Start an RPC call
//! @param cb Completion notification callback. Must outlive Operation (call Operation::cancel() to force release)
//! @param arguments encoded call arguments
//! @param pvRequest if NULL defaults to "field()".
Operation rpc(GetCallback* cb,
const epics::pvData::PVStructure::const_shared_pointer& arguments,
epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer());
//! Block and execute remote call
//! @param timeout in seconds
//! @param arguments encoded call arguments
//! @param pvRequest if NULL defaults to "field()".
epics::pvData::PVStructure::const_shared_pointer
rpc(double timeout,
const epics::pvData::PVStructure::const_shared_pointer& arguments,
epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer());
//! callbacks for put()
struct PutCallback {
virtual ~PutCallback() {}
struct Args {
Args(epics::pvData::BitSet& tosend) :tosend(tosend) {}
epics::pvData::PVStructure::const_shared_pointer root;
epics::pvData::BitSet& tosend;
};
/** Server provides expected structure.
*
* Implementation must instanciate (or re-use) a PVStructure into args.root,
* then initialize any necessary fields and set bits in args.tosend as approprate.
*
* If this method throws, then putDone() is called with PutEvent::Fail
*/
virtual void putBuild(const epics::pvData::StructureConstPtr& build, Args& args) =0;
//! Put operation is complete
virtual void putDone(const PutEvent& evt)=0;
};
//! Initiate request to change PV
//! @param cb Completion notification callback. Must outlive Operation (call Operation::cancel() to force release)
//! TODO: produce bitset to mask fields being set
Operation put(PutCallback* cb,
epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer());
//! Put to the 'value' field and block until complete.
//! Accepts a scalar value
template<epics::pvData::ScalarType ID>
inline void putValue(typename epics::pvData::meta::arg_type<typename epics::pvData::ScalarTypeTraits<ID>::type>::type value,
double timeout = 3.0,
epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer())
{
putValue(static_cast<const void*>(&value), ID, timeout, pvRequest);
}
//! Put to the 'value' field and block until complete.
//! Accepts untyped scalar value
void putValue(const void* value, epics::pvData::ScalarType vtype,
double timeout = 3.0,
const epics::pvData::PVStructure::const_shared_pointer& pvRequest = epics::pvData::PVStructure::const_shared_pointer());
//! Put to the 'value' field and block until complete.
//! Accepts scalar array
void putValue(const epics::pvData::shared_vector<const void>& value,
double timeout = 3.0,
const epics::pvData::PVStructure::const_shared_pointer& pvRequest = epics::pvData::PVStructure::const_shared_pointer());
//! Monitor event notification
struct MonitorCallback {
virtual ~MonitorCallback() {}
/** New monitor event
*
* - MonitorEvent::Fail - An Error occurred. Check evt.message
* - MonitorEvent::Cancel - Monitor::cancel() called
* - MonitorEvent::Disconnect - Underlying ClientChannel becomes disconnected
* - MonitorEvent::Data - FIFO becomes not empty.Call Monitor::poll()
*/
virtual void monitorEvent(const MonitorEvent& evt)=0;
};
//! Begin subscription
//! @param cb Completion notification callback. Must outlive Operation (call Operation::cancel() to force release)
Monitor monitor(MonitorCallback *cb,
epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer());
/** Begin subscription w/o callbacks
*
* @param event If not NULL, then subscription events are signaled to this epicsEvent. Test with poll().
* Otherwise an internal epicsEvent is allocated for use with wait()
*
* @note For simple usage with a single MonitorSync, pass event=NULL and call wait().
* If more than one MonitorSync is being created, then pass a custom epicsEvent and use poll() to test
* which subscriptions have events pending.
*/
MonitorSync monitor(const epics::pvData::PVStructure::const_shared_pointer& pvRequest = epics::pvData::PVStructure::const_shared_pointer(),
epicsEvent *event =0);
//! Connection state change CB
struct ConnectCallback {
virtual ~ConnectCallback() {}
virtual void connectEvent(const ConnectEvent& evt)=0;
};
//! Append to list of listeners
//! @param cb Channel dis/connect notification callback. Must outlive ClientChannel or call to removeConnectListener()
void addConnectListener(ConnectCallback*);
//! Remove from list of listeners
void removeConnectListener(ConnectCallback*);
private:
std::tr1::shared_ptr<epics::pvAccess::Channel> getChannel();
};
//! Central client context.
class epicsShareClass ClientProvider
{
struct Impl;
std::tr1::shared_ptr<Impl> impl;
public:
/** Use named provider.
*
* @param providerName ChannelProvider name, may be prefixed with "clients:" or "servers:" to query
* epics::pvAccess::ChannelProviderRegistry::clients() or
* epics::pvAccess::ChannelProviderRegistry::servers().
* No prefix implies "clients:".
*/
ClientProvider(const std::string& providerName,
const std::tr1::shared_ptr<epics::pvAccess::Configuration>& conf = std::tr1::shared_ptr<epics::pvAccess::Configuration>());
~ClientProvider();
/** Get a new Channel
*
* Does not block.
* Never returns NULL.
* Uses internal Channel cache.
*/
ClientChannel connect(const std::string& name,
const ClientChannel::Options& conf = ClientChannel::Options());
//! Remove from channel cache
bool disconnect(const std::string& name,
const ClientChannel::Options& conf = ClientChannel::Options());
//! Clear channel cache
void disconnect();
};
//! @}
}//namespace pvac
#endif // PVATESTCLIENT_H

View File

@@ -7,6 +7,8 @@
#include <map>
#include <vector>
#include <epicsThread.h>
#include <pv/lock.h>
#include <pv/noDefaultMethods.h>
#include <pv/pvData.h>
@@ -21,29 +23,11 @@ using std::string;
namespace epics {
namespace pvAccess {
ChannelProviderRegistry::ChannelProviderRegistry()
{
std::cout << "ChannelProviderRegistry\n";
ChannelProviderRegistry::shared_pointer ChannelProviderRegistry::build() {
ChannelProviderRegistry::shared_pointer ret(new ChannelProviderRegistry);
return ret;
}
ChannelProviderRegistry::~ChannelProviderRegistry()
{
std::cout << "~ChannelProviderRegistry\n";
}
ChannelProviderRegistry::shared_pointer ChannelProviderRegistry::getChannelProviderRegistry()
{
static Mutex mutex;
static ChannelProviderRegistry::shared_pointer global_reg;
Lock guard(mutex);
if(!global_reg) {
global_reg = ChannelProviderRegistry::build();
}
return global_reg;
}
ChannelProvider::shared_pointer ChannelProviderRegistry::getProvider(std::string const & providerName) {
ChannelProviderFactory::shared_pointer fact(getFactory(providerName));
if(fact)
@@ -52,14 +36,6 @@ ChannelProvider::shared_pointer ChannelProviderRegistry::getProvider(std::string
return ChannelProvider::shared_pointer();
}
ChannelProvider::shared_pointer ChannelProviderRegistry::createProvider(std::string const & providerName) {
ChannelProviderFactory::shared_pointer fact(getFactory(providerName));
if(fact)
return fact->newInstance();
else
return ChannelProvider::shared_pointer();
}
ChannelProvider::shared_pointer ChannelProviderRegistry::createProvider(std::string const & providerName,
const std::tr1::shared_ptr<Configuration>& conf) {
ChannelProviderFactory::shared_pointer fact(getFactory(providerName));
@@ -79,60 +55,144 @@ ChannelProviderFactory::shared_pointer ChannelProviderRegistry::getFactory(std::
return iter->second;
}
std::auto_ptr<ChannelProviderRegistry::stringVector_t> ChannelProviderRegistry::getProviderNames()
void ChannelProviderRegistry::getProviderNames(std::set<std::string>& names)
{
Lock G(mutex);
std::auto_ptr<stringVector_t> ret(new stringVector_t);
for (providers_t::const_iterator iter = providers.begin();
iter != providers.end(); iter++)
ret->push_back(iter->first);
return ret;
iter != providers.end(); iter++)
names.insert(iter->first);
}
bool ChannelProviderRegistry::add(const ChannelProviderFactory::shared_pointer& fact, bool replace)
{
assert(fact);
Lock G(mutex);
std::string name(fact->getFactoryName());
if(!replace && providers.find(name)!=providers.end())
throw false;
return false;
providers[name] = fact;
return true;
}
ChannelProviderFactory::shared_pointer ChannelProviderRegistry::remove(const std::string& name)
namespace {
struct FunctionFactory : public ChannelProviderFactory {
const std::string pname;
epics::pvData::Mutex sharedM;
ChannelProvider::weak_pointer shared;
const ChannelProviderRegistry::factoryfn_t fn;
{
ChannelProviderFactory::shared_pointer ret;
providers_t::iterator iter(providers.find(name));
if(iter!=providers.end()) {
ret = iter->second;
providers.erase(iter);
FunctionFactory(const std::string& name, ChannelProviderRegistry::factoryfn_t fn)
:pname(name), fn(fn)
{}
virtual ~FunctionFactory() {}
virtual std::string getFactoryName() { return pname; }
virtual ChannelProvider::shared_pointer sharedInstance()
{
epics::pvData::Lock L(sharedM);
ChannelProvider::shared_pointer ret(shared.lock());
if(!ret) {
ret = fn(std::tr1::shared_ptr<Configuration>());
shared = ret;
}
return ret;
}
virtual ChannelProvider::shared_pointer newInstance(const std::tr1::shared_ptr<Configuration>& conf)
{
return fn(conf);
}
};
}//namespace
ChannelProviderFactory::shared_pointer ChannelProviderRegistry::add(const std::string& name, factoryfn_t fn, bool replace)
{
ChannelProviderFactory::shared_pointer F(new FunctionFactory(name, fn));
return add(F, replace) ? F : ChannelProviderFactory::shared_pointer();
}
ChannelProviderFactory::shared_pointer ChannelProviderRegistry::remove(const std::string& name)
{
Lock G(mutex);
ChannelProviderFactory::shared_pointer fact(getFactory(name));
if(fact) {
remove(fact);
}
return fact;
}
bool ChannelProviderRegistry::remove(const ChannelProviderFactory::shared_pointer& fact)
{
assert(fact);
Lock G(mutex);
providers_t::iterator iter(providers.find(fact->getFactoryName()));
if(iter!=providers.end() && iter->second==fact) {
providers.erase(iter);
return true;
}
return false;
}
void ChannelProviderRegistry::clear()
{
Lock G(mutex);
providers.clear();
}
namespace {
struct providerRegGbl_t {
ChannelProviderRegistry::shared_pointer clients,
servers;
providerRegGbl_t()
:clients(ChannelProviderRegistry::build())
,servers(ChannelProviderRegistry::build())
{}
} *providerRegGbl;
epicsThreadOnceId providerRegOnce = EPICS_THREAD_ONCE_INIT;
void providerRegInit(void*)
{
providerRegGbl = new providerRegGbl_t;
}
} // namespace
ChannelProviderRegistry::shared_pointer ChannelProviderRegistry::clients()
{
epicsThreadOnce(&providerRegOnce, &providerRegInit, 0);
return providerRegGbl->clients;
}
ChannelProviderRegistry::shared_pointer ChannelProviderRegistry::servers()
{
epicsThreadOnce(&providerRegOnce, &providerRegInit, 0);
return providerRegGbl->servers;
}
ChannelFind::shared_pointer
ChannelProvider::channelList(ChannelListRequester::shared_pointer const & requester)
{
ChannelFind::shared_pointer ret;
requester->channelListResult(Status::error("not implemented"),
ret,
epics::pvData::PVStringArray::const_svector(),
false);
return ret;
}
ChannelProviderRegistry::shared_pointer getChannelProviderRegistry() {
std::cerr << "getChannelProviderRegistry should not be used\n";
return ChannelProviderRegistry::getChannelProviderRegistry();
}
void registerChannelProviderFactory(ChannelProviderFactory::shared_pointer const & channelProviderFactory) {
std::cerr << "registerChannelProviderFactory should not be used\n";
getChannelProviderRegistry()->add(channelProviderFactory);
}
void unregisterChannelProviderFactory(ChannelProviderFactory::shared_pointer const & channelProviderFactory) {
std::cerr << "unregisterChannelProviderFactory should not be used\n";
getChannelProviderRegistry()->remove(channelProviderFactory->getFactoryName());
}
epicsShareFunc void unregisterAllChannelProviderFactory()
Channel::shared_pointer
ChannelProvider::createChannel(std::string const & name,
ChannelRequester::shared_pointer const & requester,
short priority)
{
getChannelProviderRegistry()->clear();
return createChannel(name, requester, priority, "");
}
}
}

View File

@@ -2,4 +2,4 @@
SRC_DIRS += $(PVACCESS_SRC)/factory
LIBSRCS += ChannelAccessFactory.cpp
pvAccess_SRCS += ChannelAccessFactory.cpp

View File

@@ -1,11 +1,17 @@
# This is a Makefile fragment, see ../Makefile
TOP = ../..
include $(TOP)/configure/CONFIG
SRC_DIRS += $(PVACCESS_SRC)/ioc
LIBRARY += pvAccessIOC
pvAccessIOC_LIBS += pvAccess pvData
pvAccessIOC_LIBS += $(EPICS_BASE_IOC_LIBS)
INC += pv/syncChannelFind.h
DBD += PVAServerRegister.dbd
DBD += PVAClientRegister.dbd
LIBSRCS += PVAServerRegister.cpp
LIBSRCS += PVAClientRegister.cpp
pvAccessIOC_SRCS += PVAServerRegister.cpp
pvAccessIOC_SRCS += PVAClientRegister.cpp
include $(TOP)/configure/RULES

View File

@@ -22,34 +22,22 @@
#include <iocsh.h>
#include <epicsExit.h>
#include <epicsExport.h>
#include <pv/pvAccess.h>
#include <pv/clientFactory.h>
using std::cout;
using std::endl;
using namespace epics::pvData;
#include <epicsExport.h>
using namespace epics::pvAccess;
static const iocshFuncDef startPVAClientFuncDef = {
"startPVAClient", 0, 0
};
extern "C" void startPVAClient(const iocshArgBuf *args)
static void stopPVAClient(void*)
{
ClientFactory::start();
ClientFactory::stop();
}
static void registerStartPVAClient(void)
{
static int firstTime = 1;
if (firstTime) {
firstTime = 0;
iocshRegister(&startPVAClientFuncDef, startPVAClient);
}
ClientFactory::start();
epicsAtExit(stopPVAClient, 0);
}

View File

@@ -25,17 +25,33 @@
#include <epicsEvent.h>
#include <epicsThread.h>
#include <iocsh.h>
#include <initHooks.h>
#include <epicsExit.h>
#include <epicsExport.h>
#include <pv/pvAccess.h>
#include <pv/serverContext.h>
#include <epicsExport.h>
using std::cout;
using std::endl;
using namespace epics::pvData;
using namespace epics::pvAccess;
namespace pvd = epics::pvData;
namespace pva = epics::pvAccess;
static pvd::Mutex the_server_lock;
static pva::ServerContext::shared_pointer the_server;
static void startitup() {
the_server = pva::ServerContext::create(pva::ServerContext::Config()
.config(pva::ConfigurationBuilder()
// default to all providers instead of just "local"
.add("EPICS_PVAS_PROVIDER_NAMES", pva::PVACCESS_ALL_PROVIDERS)
.push_map()
// prefer to use EPICS_PVAS_PROVIDER_NAMES
// from environment
.push_env()
.build()));
}
static const iocshArg startPVAServerArg0 = { "providerNames", iocshArgString };
static const iocshArg *startPVAServerArgs[] = {
@@ -46,23 +62,80 @@ static const iocshFuncDef startPVAServerFuncDef = {
};
static void startPVAServer(const iocshArgBuf *args)
{
char *names = args[0].sval;
if(!names) {
startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
} else {
std::string providerNames(names);
startPVAServer(providerNames,0,true,true);
try {
char *names = args[0].sval;
if(names && names[0]!='\0') {
printf("Warning: startPVAServer() no longer accepts provider list as argument.\n"
" Instead place the following before calling startPVAServer() and iocInit()\n"
" epicsEnvSet(\"EPICS_PVAS_PROVIDER_NAMES\", \"%s\")\n",
names);
}
pvd::Lock G(the_server_lock);
if(the_server) {
std::cout<<"PVA server already running\n";
return;
}
startitup();
}catch(std::exception& e){
std::cout<<"Error: "<<e.what()<<"\n";
}
}
static const iocshArg *stopPVAServerArgs[1] = {};
static const iocshFuncDef stopPVAServerFuncDef = {
"stopPVAServer", 0, stopPVAServerArgs
};
static void stopPVAServer(const iocshArgBuf *args)
{
try {
pvd::Lock G(the_server_lock);
if(!the_server) {
std::cout<<"PVA server not running\n";
return;
}
the_server.reset();
}catch(std::exception& e){
std::cout<<"Error: "<<e.what()<<"\n";
}
}
static const iocshArg *statusPVAServerArgs[1] = {};
static const iocshFuncDef statusPVAServerFuncDef = {
"statusPVAServer", 0, statusPVAServerArgs
};
static void statusPVAServer(const iocshArgBuf *args)
{
try {
pvd::Lock G(the_server_lock);
if(!the_server) {
std::cout<<"PVA server not running\n";
return;
} else {
the_server->printInfo();
}
}catch(std::exception& e){
std::cout<<"Error: "<<e.what()<<"\n";
}
}
static void initStartPVAServer(initHookState state)
{
pvd::Lock G(the_server_lock);
if(state==initHookAfterIocRunning && !the_server) {
startitup();
} else if(state==initHookAtIocPause) {
the_server.reset();
}
}
static void registerStartPVAServer(void)
{
static int firstTime = 1;
if (firstTime) {
firstTime = 0;
iocshRegister(&startPVAServerFuncDef, startPVAServer);
}
iocshRegister(&startPVAServerFuncDef, startPVAServer);
iocshRegister(&stopPVAServerFuncDef, stopPVAServer);
iocshRegister(&statusPVAServerFuncDef, statusPVAServer);
initHookRegister(&initStartPVAServer);
}
extern "C" {

View File

@@ -4,4 +4,4 @@ SRC_DIRS += $(PVACCESS_SRC)/mb
INC += pv/pvAccessMB.h
LIBSRCS += pvAccessMB.cpp
pvAccess_SRCS += pvAccessMB.cpp

View File

@@ -5,5 +5,5 @@ SRC_DIRS += $(PVACCESS_SRC)/pipelineService
INC += pv/pipelineService.h
INC += pv/pipelineServer.h
LIBSRCS += pipelineService.cpp
LIBSRCS += pipelineServer.cpp
pvAccess_SRCS += pipelineService.cpp
pvAccess_SRCS += pipelineServer.cpp

View File

@@ -16,8 +16,8 @@
using namespace epics::pvData;
using namespace std;
namespace epics {
namespace pvAccess {
namespace {
using namespace epics::pvAccess;
class ChannelPipelineMonitorImpl :
public PipelineMonitor,
@@ -229,16 +229,6 @@ public:
m_pipelineSession->cancel();
}
virtual void lock()
{
// noop
}
virtual void unlock()
{
// noop
}
virtual size_t getFreeElementCount() {
Lock guard(m_freeQueueLock);
return m_freeQueue.size();
@@ -350,9 +340,9 @@ public:
virtual ConnectionState getConnectionState()
{
return isConnected() ?
Channel::CONNECTED :
Channel::DESTROYED;
return m_destroyed.get() ?
Channel::DESTROYED :
Channel::CONNECTED;
}
virtual std::string getChannelName()
@@ -365,71 +355,11 @@ public:
return m_channelRequester;
}
virtual bool isConnected()
{
return !m_destroyed.get();
}
virtual AccessRights getAccessRights(epics::pvData::PVField::shared_pointer const & /*pvField*/)
{
return none;
}
virtual void getField(GetFieldRequester::shared_pointer const & requester,std::string const & /*subField*/)
{
requester->getDone(notSupportedStatus, epics::pvData::Field::shared_pointer());
}
virtual ChannelProcess::shared_pointer createChannelProcess(
ChannelProcessRequester::shared_pointer const & channelProcessRequester,
epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/)
{
ChannelProcess::shared_pointer nullPtr;
channelProcessRequester->channelProcessConnect(notSupportedStatus, nullPtr);
return nullPtr;
}
virtual ChannelGet::shared_pointer createChannelGet(
ChannelGetRequester::shared_pointer const & channelGetRequester,
epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/)
{
ChannelGet::shared_pointer nullPtr;
channelGetRequester->channelGetConnect(notSupportedStatus, nullPtr,
epics::pvData::Structure::const_shared_pointer());
return nullPtr;
}
virtual ChannelPut::shared_pointer createChannelPut(
ChannelPutRequester::shared_pointer const & channelPutRequester,
epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/)
{
ChannelPut::shared_pointer nullPtr;
channelPutRequester->channelPutConnect(notSupportedStatus, nullPtr,
epics::pvData::Structure::const_shared_pointer());
return nullPtr;
}
virtual ChannelPutGet::shared_pointer createChannelPutGet(
ChannelPutGetRequester::shared_pointer const & channelPutGetRequester,
epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/)
{
ChannelPutGet::shared_pointer nullPtr;
epics::pvData::Structure::const_shared_pointer nullStructure;
channelPutGetRequester->channelPutGetConnect(notSupportedStatus, nullPtr, nullStructure, nullStructure);
return nullPtr;
}
virtual ChannelRPC::shared_pointer createChannelRPC(
ChannelRPCRequester::shared_pointer const & channelRPCRequester,
epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/)
{
ChannelRPC::shared_pointer nullPtr;
channelRPCRequester->channelRPCConnect(notSupportedStatus, nullPtr);
return nullPtr;
}
virtual Monitor::shared_pointer createMonitor(
MonitorRequester::shared_pointer const & monitorRequester,
epics::pvData::PVStructure::shared_pointer const & pvRequest)
@@ -476,11 +406,6 @@ public:
}
virtual void printInfo()
{
printInfo(std::cout);
}
virtual void printInfo(std::ostream& out)
{
out << "PipelineChannel: ";
@@ -495,12 +420,6 @@ public:
return getChannelName();
}
virtual void message(std::string const & message,MessageType messageType)
{
// just delegate
m_channelRequester->message(message, messageType);
}
virtual void destroy()
{
m_destroyed.set();
@@ -510,6 +429,10 @@ public:
Status PipelineChannel::notSupportedStatus(Status::STATUSTYPE_ERROR, "only monitor (aka pipeline) requests are supported by this channel");
Status PipelineChannel::destroyedStatus(Status::STATUSTYPE_ERROR, "channel destroyed");
} // namespace
namespace epics {
namespace pvAccess {
Channel::shared_pointer createPipelineChannel(ChannelProvider::shared_pointer const & provider,
std::string const & channelName,
ChannelRequester::shared_pointer const & channelRequester,
@@ -523,7 +446,6 @@ Channel::shared_pointer createPipelineChannel(ChannelProvider::shared_pointer co
return channel;
}
class PipelineChannelProvider :
public virtual ChannelProvider,
public virtual ChannelFind,
@@ -700,53 +622,11 @@ private:
string PipelineChannelProvider::PROVIDER_NAME("PipelineService");
Status PipelineChannelProvider::noSuchChannelStatus(Status::STATUSTYPE_ERROR, "no such channel");
class PipelineChannelProviderFactory : public ChannelProviderFactory
{
public:
POINTER_DEFINITIONS(PipelineChannelProviderFactory);
PipelineChannelProviderFactory() :
m_channelProviderImpl(new PipelineChannelProvider())
{
}
virtual std::string getFactoryName()
{
return PipelineChannelProvider::PROVIDER_NAME;
}
virtual ChannelProvider::shared_pointer sharedInstance()
{
return m_channelProviderImpl;
}
virtual ChannelProvider::shared_pointer newInstance()
{
// TODO use std::make_shared
std::tr1::shared_ptr<PipelineChannelProvider> tp(new PipelineChannelProvider());
ChannelProvider::shared_pointer channelProvider = tp;
return channelProvider;
}
private:
PipelineChannelProvider::shared_pointer m_channelProviderImpl;
};
PipelineServer::PipelineServer()
:m_channelProviderImpl(new PipelineChannelProvider)
{
// TODO factory is never deregistered, multiple PipelineServer instances create multiple factories, etc.
m_channelProviderFactory.reset(new PipelineChannelProviderFactory());
registerChannelProviderFactory(m_channelProviderFactory);
m_channelProviderImpl = m_channelProviderFactory->sharedInstance();
m_serverContext = ServerContextImpl::create();
m_serverContext->setChannelProviderName(m_channelProviderImpl->getProviderName());
m_serverContext->initialize(getChannelProviderRegistry());
m_serverContext = ServerContext::create(ServerContext::Config()
.provider(m_channelProviderImpl));
}
PipelineServer::~PipelineServer()
@@ -766,50 +646,27 @@ void PipelineServer::run(int seconds)
m_serverContext->run(seconds);
}
struct ThreadRunnerParam {
PipelineServer::shared_pointer server;
int timeToRun;
};
static void threadRunner(void* usr)
{
ThreadRunnerParam* pusr = static_cast<ThreadRunnerParam*>(usr);
ThreadRunnerParam param = *pusr;
delete pusr;
param.server->run(param.timeToRun);
}
/// Method requires usage of std::tr1::shared_ptr<PipelineServer>. This instance must be
/// owned by a shared_ptr instance.
void PipelineServer::runInNewThread(int seconds)
{
std::auto_ptr<ThreadRunnerParam> param(new ThreadRunnerParam());
param->server = shared_from_this();
param->timeToRun = seconds;
epicsThreadCreate("PipelineServer thread",
epicsThreadPriorityMedium,
epicsThreadGetStackSize(epicsThreadStackSmall),
threadRunner, param.get());
// let the thread delete 'param'
param.release();
if(seconds!=0)
std::cerr<<"PipelineServer::runInNewThread() only suppose seconds=0\n";
}
void PipelineServer::destroy()
{
m_serverContext->destroy();
m_serverContext->shutdown();
}
void PipelineServer::registerService(std::string const & serviceName, PipelineService::shared_pointer const & service)
{
std::tr1::dynamic_pointer_cast<PipelineChannelProvider>(m_channelProviderImpl)->registerService(serviceName, service);
m_channelProviderImpl->registerService(serviceName, service);
}
void PipelineServer::unregisterService(std::string const & serviceName)
{
std::tr1::dynamic_pointer_cast<PipelineChannelProvider>(m_channelProviderImpl)->unregisterService(serviceName);
m_channelProviderImpl->unregisterService(serviceName);
}
}

View File

@@ -28,14 +28,15 @@
namespace epics {
namespace pvAccess {
class PipelineChannelProvider;
class epicsShareClass PipelineServer :
public std::tr1::enable_shared_from_this<PipelineServer>
{
private:
ServerContextImpl::shared_pointer m_serverContext;
ChannelProviderFactory::shared_pointer m_channelProviderFactory;
ChannelProvider::shared_pointer m_channelProviderImpl;
ServerContext::shared_pointer m_serverContext;
std::tr1::shared_ptr<PipelineChannelProvider> m_channelProviderImpl;
// TODO no thread poll implementation

View File

@@ -3,9 +3,10 @@
SRC_DIRS += $(PVACCESS_SRC)/pva
INC += pv/pvaConstants.h
INC += pv/pvaDefs.h
INC += pv/pvaVersion.h
INC += pv/pvaVersionNum.h
INC += pv/clientFactory.h
LIBSRCS += pvaVersion.cpp
LIBSRCS += clientFactory.cpp
pvAccess_SRCS += pvaVersion.cpp
pvAccess_SRCS += clientFactory.cpp

View File

@@ -7,7 +7,7 @@
#include <string>
#include <epicsSignal.h>
#include <epicsExit.h>
#include <pv/lock.h>
#define epicsExportSharedSymbols
@@ -15,94 +15,51 @@
#include <pv/clientFactory.h>
#include <pv/clientContextImpl.h>
namespace epics {
namespace pvAccess {
using namespace epics::pvData;
using namespace epics::pvAccess;
class ChannelProviderFactoryImpl : public ChannelProviderFactory
static
void pva_factory_cleanup(void*)
{
private:
Mutex m_mutex;
ChannelProvider::shared_pointer m_shared_provider;
public:
POINTER_DEFINITIONS(ChannelProviderFactoryImpl);
virtual ~ChannelProviderFactoryImpl()
{
Lock guard(m_mutex);
if (m_shared_provider)
{
ChannelProvider::shared_pointer provider;
m_shared_provider.swap(provider);
// factroy cleans up also shared provider
provider->destroy();
}
try {
ChannelProviderRegistry::clients()->remove("pva");
} catch(std::exception& e) {
LOG(logLevelWarn, "Error when unregister \"pva\" factory");
}
virtual std::string getFactoryName()
{
return ClientContextImpl::PROVIDER_NAME;
}
virtual ChannelProvider::shared_pointer sharedInstance()
{
Lock guard(m_mutex);
if (!m_shared_provider)
{
epics::pvAccess::Configuration::shared_pointer def;
m_shared_provider = createClientProvider(def);
}
return m_shared_provider;
}
virtual ChannelProvider::shared_pointer newInstance(const std::tr1::shared_ptr<epics::pvAccess::Configuration>& conf)
{
Lock guard(m_mutex);
return createClientProvider(conf);
}
};
static Mutex startStopMutex;
ChannelProviderRegistryPtr ClientFactory::channelRegistry;
ChannelProviderFactoryPtr ClientFactory::channelProvider;
int ClientFactory::numStart = 0;
}
void ClientFactory::start()
{
Lock guard(startStopMutex);
std::cout << "ClientFactory::start() numStart " << numStart << std::endl;
++numStart;
if(numStart>1) return;
epicsSignalInstallSigAlarmIgnore();
epicsSignalInstallSigPipeIgnore();
channelProvider.reset(new ChannelProviderFactoryImpl());
channelRegistry = ChannelProviderRegistry::getChannelProviderRegistry();
std::cout << "channelRegistry::use_count " << channelRegistry.use_count() << std::endl;
channelRegistry->add(channelProvider);
if(ChannelProviderRegistry::clients()->add("pva", createClientProvider, false))
epicsAtExit(&pva_factory_cleanup, NULL);
}
void ClientFactory::stop()
{
std::cout << "ClientFactory::stop() numStart " << numStart << std::endl;
std::cout << "channelRegistry::use_count " << channelRegistry.use_count() << std::endl;
Lock guard(startStopMutex);
if(numStart==0) return;
--numStart;
if(numStart>=1) return;
if (channelProvider)
{
channelRegistry->remove(ClientContextImpl::PROVIDER_NAME);
if(!channelProvider.unique()) {
LOG(logLevelWarn, "ClientFactory::stop() finds shared client context with %u remaining users",
(unsigned)channelProvider.use_count());
}
channelProvider.reset();
channelRegistry.reset();
}
// unregister now done with exit hook
}
}}
// automatically register on load
namespace {
struct pvaloader
{
pvaloader() {
ClientFactory::start();
}
} pvaloaderinstance;
} // namespace
// perhaps useful during dynamic loading?
extern "C" {
void registerClientProvider_pva()
{
try {
ClientFactory::start();
} catch(std::exception& e){
std::cerr<<"Error loading pva: "<<e.what()<<"\n";
}
}
} // extern "C"

View File

@@ -40,13 +40,6 @@ const epics::pvData::int32 PVA_BROADCAST_PORT = 5076;
/** PVA protocol message header size. */
const epics::pvData::int16 PVA_MESSAGE_HEADER_SIZE = 8;
/**
* All messages must be aligned to 8-bytes (64-bit).
* MUST be 1. Code does not handle well alignment in some situations (e.g. direct deserialize).
* Alignment is not worth additional code complexity.
*/
const epics::pvData::int32 PVA_ALIGNMENT = 1;
/**
* UDP maximum send message size.
* MAX_UDP: 1500 (max of ethernet and 802.{2,3} MTU) - 20/40(IPv4/IPv6)

48
src/pva/pv/pvaDefs.h Normal file
View File

@@ -0,0 +1,48 @@
#ifndef PVADEFS_H
#define PVADEFS_H
#include <epicsTypes.h>
#include <epicsMutex.h>
#include <epicsGuard.h>
namespace epics {
namespace pvAccess {
/**
* Globally unique ID.
*/
struct ServerGUID {
char value[12];
};
// 'GUID' can be ambigious on windows/mingw
typedef ServerGUID GUID EPICS_DEPRECATED;
typedef epicsInt32 pvAccessID;
class AtomicBoolean
{
public:
AtomicBoolean() : val(false) {}
void set() {
epicsGuard<epicsMutex> G(mutex);
val = true;
}
void clear() {
epicsGuard<epicsMutex> G(mutex);
val = false;
}
bool get() const {
epicsGuard<epicsMutex> G(mutex);
return val;
}
private:
bool val;
mutable epicsMutex mutex;
};
}}
#endif // PVADEFS_H

View File

@@ -24,6 +24,12 @@
#include "pv/pvaVersionNum.h"
#ifndef VERSION_INT
# define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P))
#endif
#define PVACCESS_VERSION_INT VERSION_INT(EPICS_PVA_MAJOR_VERSION, EPICS_PVA_MINOR_VERSION, EPICS_PVA_MAINTENANCE_VERSION, 0)
namespace epics {
namespace pvAccess {

View File

@@ -2,25 +2,17 @@
SRC_DIRS += $(PVACCESS_SRC)/remote
INC += pv/remote.h
INC += pv/security.h
INC += pv/blockingUDP.h
INC += pv/beaconHandler.h
INC += pv/blockingTCP.h
INC += pv/channelSearchManager.h
INC += pv/simpleChannelSearchManagerImpl.h
INC += pv/transportRegistry.h
INC += pv/serializationHelper.h
INC += pv/codec.h
LIBSRCS += blockingUDPTransport.cpp
LIBSRCS += blockingUDPConnector.cpp
LIBSRCS += beaconHandler.cpp
LIBSRCS += blockingTCPConnector.cpp
LIBSRCS += simpleChannelSearchManagerImpl.cpp
LIBSRCS += abstractResponseHandler.cpp
LIBSRCS += blockingTCPAcceptor.cpp
LIBSRCS += transportRegistry.cpp
LIBSRCS += serializationHelper.cpp
LIBSRCS += codec.cpp
LIBSRCS += security.cpp
pvAccess_SRCS += blockingUDPTransport.cpp
pvAccess_SRCS += blockingUDPConnector.cpp
pvAccess_SRCS += beaconHandler.cpp
pvAccess_SRCS += blockingTCPConnector.cpp
pvAccess_SRCS += simpleChannelSearchManagerImpl.cpp
pvAccess_SRCS += abstractResponseHandler.cpp
pvAccess_SRCS += blockingTCPAcceptor.cpp
pvAccess_SRCS += transportRegistry.cpp
pvAccess_SRCS += serializationHelper.cpp
pvAccess_SRCS += codec.cpp
pvAccess_SRCS += security.cpp

View File

@@ -34,7 +34,7 @@ BeaconHandler::~BeaconHandler()
}
void BeaconHandler::beaconNotify(osiSockAddr* /*from*/, int8 remoteTransportRevision,
TimeStamp* timestamp, GUID const & guid, int16 sequentalID,
TimeStamp* timestamp, ServerGUID const & guid, int16 sequentalID,
int16 changeCount,
PVFieldPtr /*data*/)
{
@@ -44,7 +44,7 @@ void BeaconHandler::beaconNotify(osiSockAddr* /*from*/, int8 remoteTransportRevi
}
bool BeaconHandler::updateBeacon(int8 /*remoteTransportRevision*/, TimeStamp* /*timestamp*/,
GUID const & guid, int16 /*sequentalID*/, int16 changeCount)
ServerGUID const & guid, int16 /*sequentalID*/, int16 changeCount)
{
Lock guard(_mutex);
// first beacon notification check

View File

@@ -89,6 +89,9 @@ Transport::shared_pointer BlockingTCPConnector::connect(TransportClient::shared_
return transport;
}
/* TODO: bound map<> size
* Lazy creates a lock for each 'address' ever encountered.
*/
bool lockAcquired = _namedLocker.acquireSynchronizationObject(&address, LOCK_TIMEOUT);
if(lockAcquired) {
try {

View File

@@ -16,6 +16,22 @@
using namespace std;
using namespace epics::pvData;
namespace {
struct closer {
epics::pvAccess::Transport::shared_pointer P;
closer(const epics::pvAccess::Transport::shared_pointer& P) :P(P) {}
void operator()(epics::pvAccess::Transport*) {
try{
P->close();
}catch(...){
P.reset();
throw;
}
P.reset();
}
};
}
namespace epics {
namespace pvAccess {
@@ -72,7 +88,12 @@ Transport::shared_pointer BlockingUDPConnector::connect(TransportClient::shared_
BlockingUDPTransport::shared_pointer transport(new BlockingUDPTransport(_serverFlag, responseHandler,
socket, bindAddress, transportRevision));
return Transport::shared_pointer(transport);
// the worker thread holds a strong ref, which is released by transport->close()
// note: casting to Transport* to prevent iOS version of shared_ptr from trying (and failing)
// to setup shared_from_this() using the wrapped pointer
Transport::shared_pointer ret(static_cast<Transport*>(transport.get()), closer(transport));
return ret;
}
}

View File

@@ -58,8 +58,8 @@ BlockingUDPTransport::BlockingUDPTransport(bool serverFlag,
_tappedNIF(0),
_sendToEnabled(false),
_localMulticastAddressEnabled(false),
_receiveBuffer(new ByteBuffer(MAX_UDP_RECV+RECEIVE_BUFFER_PRE_RESERVE)),
_sendBuffer(new ByteBuffer(MAX_UDP_RECV)),
_receiveBuffer(MAX_UDP_RECV+RECEIVE_BUFFER_PRE_RESERVE),
_sendBuffer(MAX_UDP_RECV),
_lastMessageStartPosition(0),
_clientServerWithEndianFlag(
(serverFlag ? 0x40 : 0x00) | ((EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG) ? 0x80 : 0x00))
@@ -163,7 +163,7 @@ void BlockingUDPTransport::close(bool waitForThreadToComplete) {
// wait for send thread to exit cleanly
if (_thread.get() && waitForThreadToComplete)
{
if (!_shutdownEvent.wait(5.0))
if (!_thread->exitWait(5.0))
{
LOG(logLevelError,
"Receive thread for UDP socket %s has not exited.",
@@ -176,16 +176,16 @@ void BlockingUDPTransport::enqueueSendRequest(TransportSender::shared_pointer co
Lock lock(_sendMutex);
_sendToEnabled = false;
_sendBuffer->clear();
_sendBuffer.clear();
sender->lock();
try {
sender->send(_sendBuffer.get(), this);
sender->send(&_sendBuffer, this);
sender->unlock();
endMessage();
if(!_sendToEnabled)
send(_sendBuffer.get());
send(&_sendBuffer);
else
send(_sendBuffer.get(), _sendTo);
send(&_sendBuffer, _sendTo);
} catch(...) {
sender->unlock();
}
@@ -198,20 +198,18 @@ void BlockingUDPTransport::flushSendQueue()
}
void BlockingUDPTransport::startMessage(int8 command, size_t /*ensureCapacity*/, int32 payloadSize) {
_lastMessageStartPosition = _sendBuffer->getPosition();
_sendBuffer->putByte(PVA_MAGIC);
_sendBuffer->putByte(PVA_VERSION);
_sendBuffer->putByte(_clientServerWithEndianFlag);
_sendBuffer->putByte(command); // command
_sendBuffer->putInt(payloadSize);
_lastMessageStartPosition = _sendBuffer.getPosition();
_sendBuffer.putByte(PVA_MAGIC);
_sendBuffer.putByte(PVA_VERSION);
_sendBuffer.putByte(_clientServerWithEndianFlag);
_sendBuffer.putByte(command); // command
_sendBuffer.putInt(payloadSize);
}
void BlockingUDPTransport::endMessage() {
//we always (for now) send by packet, so no need for this here...
//alignBuffer(PVA_ALIGNMENT);
_sendBuffer->putInt(
_sendBuffer.putInt(
_lastMessageStartPosition+(sizeof(int16)+2),
_sendBuffer->getPosition()-_lastMessageStartPosition-PVA_MESSAGE_HEADER_SIZE);
_sendBuffer.getPosition()-_lastMessageStartPosition-PVA_MESSAGE_HEADER_SIZE);
}
void BlockingUDPTransport::run() {
@@ -224,8 +222,8 @@ void BlockingUDPTransport::run() {
try {
char* recvfrom_buffer_start = (char*)(_receiveBuffer->getArray()+RECEIVE_BUFFER_PRE_RESERVE);
size_t recvfrom_buffer_len =_receiveBuffer->getSize()-RECEIVE_BUFFER_PRE_RESERVE;
char* recvfrom_buffer_start = (char*)(_receiveBuffer.getArray()+RECEIVE_BUFFER_PRE_RESERVE);
size_t recvfrom_buffer_len =_receiveBuffer.getSize()-RECEIVE_BUFFER_PRE_RESERVE;
while(!_closed.get())
{
int bytesRead = recvfrom(_channel,
@@ -249,11 +247,11 @@ void BlockingUDPTransport::run() {
}
if(likely(!ignore)) {
_receiveBuffer->setPosition(RECEIVE_BUFFER_PRE_RESERVE);
_receiveBuffer->setLimit(RECEIVE_BUFFER_PRE_RESERVE+bytesRead);
_receiveBuffer.setPosition(RECEIVE_BUFFER_PRE_RESERVE);
_receiveBuffer.setLimit(RECEIVE_BUFFER_PRE_RESERVE+bytesRead);
try {
processBuffer(thisTransport, fromAddress, _receiveBuffer.get());
processBuffer(thisTransport, fromAddress, &_receiveBuffer);
} catch(std::exception& e) {
LOG(logLevelError,
"an exception caught while in UDP receiveThread at %s:%d: %s",
@@ -304,8 +302,6 @@ void BlockingUDPTransport::run() {
string threadName = "UDP-rx "+inetAddressToString(_bindAddress);
LOG(logLevelTrace, "Thread '%s' exiting.", threadName.c_str());
}
_shutdownEvent.signal();
}
bool BlockingUDPTransport::processBuffer(Transport::shared_pointer const & transport,
@@ -396,7 +392,7 @@ bool BlockingUDPTransport::processBuffer(Transport::shared_pointer const & trans
// handle
_responseHandler->handleResponse(&fromAddress, transport,
version, command, payloadSize,
_receiveBuffer.get());
&_receiveBuffer);
}
// set position (e.g. in case handler did not read all)

View File

@@ -47,12 +47,10 @@ namespace {
struct BreakTransport : TransportSender
{
virtual ~BreakTransport() {}
virtual void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control)
virtual void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control) OVERRIDE FINAL
{
throw epics::pvAccess::detail::connection_closed_exception("Break");
}
virtual void lock() {}
virtual void unlock() {}
};
} // namespace
@@ -67,10 +65,16 @@ const std::size_t AbstractCodec::MAX_ENSURE_DATA_SIZE = MAX_ENSURE_SIZE/2;
const std::size_t AbstractCodec::MAX_ENSURE_BUFFER_SIZE = MAX_ENSURE_SIZE;
const std::size_t AbstractCodec::MAX_ENSURE_DATA_BUFFER_SIZE = 1024;
static
size_t bufSizeSelect(size_t request)
{
return std::max(request, size_t(MAX_TCP_RECV + AbstractCodec::MAX_ENSURE_DATA_BUFFER_SIZE));
}
AbstractCodec::AbstractCodec(
bool serverFlag,
std::tr1::shared_ptr<epics::pvData::ByteBuffer> const & receiveBuffer,
std::tr1::shared_ptr<epics::pvData::ByteBuffer> const & sendBuffer,
size_t sendBufferSize,
size_t receiveBufferSize,
int32_t socketSendBufferSize,
bool blockingProcessQueue):
//PROTECTED
@@ -79,48 +83,30 @@ AbstractCodec::AbstractCodec(
_senderThread(0),
_writeMode(PROCESS_SEND_QUEUE),
_writeOpReady(false),_lowLatency(false),
_socketBuffer(receiveBuffer),
_sendBuffer(sendBuffer),
_socketBuffer(bufSizeSelect(receiveBufferSize)),
_sendBuffer(bufSizeSelect(sendBufferSize)),
//PRIVATE
_storedPayloadSize(0), _storedPosition(0), _startPosition(0),
_maxSendPayloadSize(0),
_maxSendPayloadSize(_sendBuffer.getSize() - 2*PVA_MESSAGE_HEADER_SIZE), // start msg + control
_lastMessageStartPosition(std::numeric_limits<size_t>::max()),_lastSegmentedMessageType(0),
_lastSegmentedMessageCommand(0), _nextMessagePayloadOffset(0),
_byteOrderFlag(EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG ? 0x80 : 0x00),
_clientServerFlag(serverFlag ? 0x40 : 0x00),
_socketSendBufferSize(0)
_socketSendBufferSize(socketSendBufferSize)
{
if (receiveBuffer->getSize() < 2*MAX_ENSURE_SIZE)
if (_socketBuffer.getSize() < 2*MAX_ENSURE_SIZE)
throw std::invalid_argument(
"receiveBuffer.capacity() < 2*MAX_ENSURE_SIZE");
// require aligned buffer size
//(not condition, but simplifies alignment code)
if (receiveBuffer->getSize() % PVA_ALIGNMENT != 0)
throw std::invalid_argument(
"receiveBuffer.capacity() % PVAConstants.PVA_ALIGNMENT != 0");
if (sendBuffer->getSize() < 2*MAX_ENSURE_SIZE)
if (_sendBuffer.getSize() < 2*MAX_ENSURE_SIZE)
throw std::invalid_argument("sendBuffer() < 2*MAX_ENSURE_SIZE");
// require aligned buffer size
//(not condition, but simplifies alignment code)
if (sendBuffer->getSize() % PVA_ALIGNMENT != 0)
throw std::invalid_argument(
"sendBuffer() % PVAConstants.PVA_ALIGNMENT != 0");
// initialize to be empty
_socketBuffer->setPosition(_socketBuffer->getLimit());
_startPosition = _socketBuffer->getPosition();
_socketBuffer.setPosition(_socketBuffer.getLimit());
_startPosition = _socketBuffer.getPosition();
// clear send
_sendBuffer->clear();
// start msg + control
_maxSendPayloadSize =
_sendBuffer->getSize() - 2*PVA_MESSAGE_HEADER_SIZE;
_socketSendBufferSize = socketSendBufferSize;
_sendBuffer.clear();
}
@@ -144,19 +130,19 @@ void AbstractCodec::processRead() {
void AbstractCodec::processHeader() {
// magic code
int8_t magicCode = _socketBuffer->getByte();
int8_t magicCode = _socketBuffer.getByte();
// version
_version = _socketBuffer->getByte();
_version = _socketBuffer.getByte();
// flags
_flags = _socketBuffer->getByte();
_flags = _socketBuffer.getByte();
// command
_command = _socketBuffer->getByte();
_command = _socketBuffer.getByte();
// read payload size
_payloadSize = _socketBuffer->getInt();
_payloadSize = _socketBuffer.getInt();
// check magic code
if (magicCode != PVA_MAGIC)
@@ -186,8 +172,8 @@ void AbstractCodec::processReadNormal() {
}
/*
hexDump("Header", (const int8*)_socketBuffer->getArray(),
_socketBuffer->getPosition(), PVA_MESSAGE_HEADER_SIZE);
hexDump("Header", (const int8*)_socketBuffer.getArray(),
_socketBuffer.getPosition(), PVA_MESSAGE_HEADER_SIZE);
*/
@@ -218,10 +204,9 @@ void AbstractCodec::processReadNormal() {
}
_storedPayloadSize = _payloadSize;
_storedPosition = _socketBuffer->getPosition();
_storedLimit = _socketBuffer->getLimit();
_socketBuffer->setLimit(std::min<std::size_t>
(_storedPosition + _storedPayloadSize, _storedLimit));
_storedPosition = _socketBuffer.getPosition();
_storedLimit = _socketBuffer.getLimit();
_socketBuffer.setLimit(std::min(_storedPosition + _storedPayloadSize, _storedLimit));
bool postProcess = true;
try
{
@@ -269,9 +254,7 @@ void AbstractCodec::postProcessApplicationMessage()
{
// set position as whole message was read
//(in case code haven't done so)
std::size_t newPosition =
alignedValue(
_storedPosition + _storedPayloadSize, PVA_ALIGNMENT);
std::size_t newPosition = _storedPosition + _storedPayloadSize;
// aligned buffer size ensures that there is enough space
//in buffer,
@@ -285,18 +268,13 @@ void AbstractCodec::postProcessApplicationMessage()
// we only handle unused alignment bytes
int bytesNotRead =
newPosition - _socketBuffer->getPosition();
newPosition - _socketBuffer.getPosition();
assert(bytesNotRead>=0);
if (bytesNotRead < PVA_ALIGNMENT)
if (bytesNotRead==0)
{
// make alignment bytes as real payload to enable SPLIT
// no end-of-socket or segmented scenario can happen
// due to aligned buffer size
_storedPayloadSize += bytesNotRead;
// reveal currently existing padding
_socketBuffer->setLimit(_storedLimit);
ensureData(bytesNotRead);
_storedPayloadSize -= bytesNotRead;
_socketBuffer.setLimit(_storedLimit);
continue;
}
@@ -309,8 +287,8 @@ void AbstractCodec::postProcessApplicationMessage()
throw invalid_data_stream_exception(
"unprocessed read buffer");
}
_socketBuffer->setLimit(_storedLimit);
_socketBuffer->setPosition(newPosition);
_socketBuffer.setLimit(_storedLimit);
_socketBuffer.setPosition(newPosition);
break;
}
}
@@ -363,13 +341,13 @@ bool AbstractCodec::readToBuffer(
bool persistent) {
// do we already have requiredBytes available?
std::size_t remainingBytes = _socketBuffer->getRemaining();
std::size_t remainingBytes = _socketBuffer.getRemaining();
if (remainingBytes >= requiredBytes) {
return true;
}
// assumption: remainingBytes < MAX_ENSURE_DATA_BUFFER_SIZE &&
// requiredBytes < (socketBuffer.capacity() - PVA_ALIGNMENT)
// requiredBytes < (socketBuffer.capacity() - 1)
//
// copy unread part to the beginning of the buffer
@@ -378,23 +356,22 @@ bool AbstractCodec::readToBuffer(
//
// a new start position, we are careful to preserve alignment
_startPosition =
MAX_ENSURE_SIZE + _socketBuffer->getPosition() % PVA_ALIGNMENT;
_startPosition = MAX_ENSURE_SIZE;
std::size_t endPosition = _startPosition + remainingBytes;
for (std::size_t i = _startPosition; i < endPosition; i++)
_socketBuffer->putByte(i, _socketBuffer->getByte());
_socketBuffer.putByte(i, _socketBuffer.getByte());
// update buffer to the new position
_socketBuffer->setLimit(_socketBuffer->getSize());
_socketBuffer->setPosition(endPosition);
_socketBuffer.setLimit(_socketBuffer.getSize());
_socketBuffer.setPosition(endPosition);
// read at least requiredBytes bytes
std::size_t requiredPosition = _startPosition + requiredBytes;
while (_socketBuffer->getPosition() < requiredPosition)
while (_socketBuffer.getPosition() < requiredPosition)
{
int bytesRead = read(_socketBuffer.get());
int bytesRead = read(&_socketBuffer);
if (bytesRead < 0)
{
@@ -409,8 +386,8 @@ bool AbstractCodec::readToBuffer(
else
{
// set pointers (aka flip)
_socketBuffer->setLimit(_socketBuffer->getPosition());
_socketBuffer->setPosition(_startPosition);
_socketBuffer.setLimit(_socketBuffer.getPosition());
_socketBuffer.setPosition(_startPosition);
return false;
}
@@ -418,8 +395,8 @@ bool AbstractCodec::readToBuffer(
}
// set pointers (aka flip)
_socketBuffer->setLimit(_socketBuffer->getPosition());
_socketBuffer->setPosition(_startPosition);
_socketBuffer.setLimit(_socketBuffer.getPosition());
_socketBuffer.setPosition(_startPosition);
return true;
}
@@ -428,7 +405,7 @@ bool AbstractCodec::readToBuffer(
void AbstractCodec::ensureData(std::size_t size) {
// enough of data?
if (_socketBuffer->getRemaining() >= size)
if (_socketBuffer.getRemaining() >= size)
return;
// to large for buffer...
@@ -438,15 +415,14 @@ void AbstractCodec::ensureData(std::size_t size) {
<< ", but maximum " << MAX_ENSURE_DATA_SIZE << " is allowed.";
LOG(logLevelWarn,
"%s at %s:%d.,", msg.str().c_str(), __FILE__, __LINE__);
std::string s = msg.str();
throw std::invalid_argument(s);
throw std::invalid_argument(msg.str());
}
try
{
// subtract what was already processed
std::size_t pos = _socketBuffer->getPosition();
std::size_t pos = _socketBuffer.getPosition();
_storedPayloadSize -= pos - _storedPosition;
// SPLIT message case
@@ -462,9 +438,9 @@ void AbstractCodec::ensureData(std::size_t size) {
_readMode = SPLIT;
readToBuffer(size, true);
_readMode = storedMode;
_storedPosition = _socketBuffer->getPosition();
_storedLimit = _socketBuffer->getLimit();
_socketBuffer->setLimit(
_storedPosition = _socketBuffer.getPosition();
_storedLimit = _socketBuffer.getLimit();
_socketBuffer.setLimit(
std::min<std::size_t>(
_storedPosition + _storedPayloadSize, _storedLimit));
@@ -485,26 +461,13 @@ void AbstractCodec::ensureData(std::size_t size) {
//[0 to MAX_ENSURE_DATA_BUFFER_SIZE/2), if any
// remaining is relative to payload since buffer is
//bounded from outside
std::size_t remainingBytes = _socketBuffer->getRemaining();
std::size_t remainingBytes = _socketBuffer.getRemaining();
for (std::size_t i = 0; i < remainingBytes; i++)
_socketBuffer->putByte(i, _socketBuffer->getByte());
_socketBuffer.putByte(i, _socketBuffer.getByte());
// restore limit (there might be some data already present
//and readToBuffer needs to know real limit)
_socketBuffer->setLimit(_storedLimit);
// remember alignment offset of end of the message (to be restored)
std::size_t storedAlignmentOffset =
_socketBuffer->getPosition() % PVA_ALIGNMENT;
// skip post-message alignment bytes
if (storedAlignmentOffset > 0)
{
std::size_t toSkip = PVA_ALIGNMENT - storedAlignmentOffset;
readToBuffer(toSkip, true);
std::size_t currentPos = _socketBuffer->getPosition();
_socketBuffer->setPosition(currentPos + toSkip);
}
_socketBuffer.setLimit(_storedLimit);
// we expect segmented message, we expect header
// that (and maybe some control packets) needs to be "removed"
@@ -515,27 +478,25 @@ void AbstractCodec::ensureData(std::size_t size) {
_readMode = storedMode;
// make sure we have all the data (maybe we run into SPLIT)
readToBuffer(size - remainingBytes + storedAlignmentOffset, true);
readToBuffer(size - remainingBytes, true);
// skip storedAlignmentOffset bytes (sender should padded start of
//segmented message)
// SPLIT cannot mess with this, since start of the message,
//i.e. current position, is always aligned
_socketBuffer->setPosition(
_socketBuffer->getPosition() + storedAlignmentOffset);
_socketBuffer.setPosition(
_socketBuffer.getPosition());
// copy before position (i.e. start of the payload)
for (int32_t i = remainingBytes - 1,
j = _socketBuffer->getPosition() - 1; i >= 0; i--, j--)
_socketBuffer->putByte(j, _socketBuffer->getByte(i));
j = _socketBuffer.getPosition() - 1; i >= 0; i--, j--)
_socketBuffer.putByte(j, _socketBuffer.getByte(i));
_startPosition = _socketBuffer->getPosition() - remainingBytes;
_socketBuffer->setPosition(_startPosition);
_startPosition = _socketBuffer.getPosition() - remainingBytes;
_socketBuffer.setPosition(_startPosition);
_storedPayloadSize += remainingBytes - storedAlignmentOffset;
_storedPayloadSize += remainingBytes;
_storedPosition = _startPosition;
_storedLimit = _socketBuffer->getLimit();
_socketBuffer->setLimit(
_storedLimit = _socketBuffer.getLimit();
_socketBuffer.setLimit(
std::min<std::size_t>(
_storedPosition + _storedPayloadSize, _storedLimit));
@@ -567,23 +528,23 @@ std::size_t AbstractCodec::alignedValue(
void AbstractCodec::alignData(std::size_t alignment) {
std::size_t k = (alignment - 1);
std::size_t pos = _socketBuffer->getPosition();
std::size_t pos = _socketBuffer.getPosition();
std::size_t newpos = (pos + k) & (~k);
if (pos == newpos)
return;
std::size_t diff = _socketBuffer->getLimit() - newpos;
std::size_t diff = _socketBuffer.getLimit() - newpos;
if (diff > 0)
{
_socketBuffer->setPosition(newpos);
_socketBuffer.setPosition(newpos);
return;
}
ensureData(diff);
// position has changed, recalculate
newpos = (_socketBuffer->getPosition() + k) & (~k);
_socketBuffer->setPosition(newpos);
newpos = (_socketBuffer.getPosition() + k) & (~k);
_socketBuffer.setPosition(newpos);
}
static const char PADDING_BYTES[] =
@@ -601,20 +562,14 @@ static const char PADDING_BYTES[] =
void AbstractCodec::alignBuffer(std::size_t alignment) {
std::size_t k = (alignment - 1);
std::size_t pos = _sendBuffer->getPosition();
std::size_t pos = _sendBuffer.getPosition();
std::size_t newpos = (pos + k) & (~k);
if (pos == newpos)
return;
/*
// there is always enough of space
// since sendBuffer capacity % PVA_ALIGNMENT == 0
_sendBuffer->setPosition(newpos);
*/
// for safety reasons we really pad (override previous message data)
std::size_t padCount = newpos - pos;
_sendBuffer->put(PADDING_BYTES, 0, padCount);
_sendBuffer.put(PADDING_BYTES, 0, padCount);
}
@@ -626,18 +581,18 @@ void AbstractCodec::startMessage(
std::numeric_limits<size_t>::max(); // TODO revise this
ensureBuffer(
PVA_MESSAGE_HEADER_SIZE + ensureCapacity + _nextMessagePayloadOffset);
_lastMessageStartPosition = _sendBuffer->getPosition();
_sendBuffer->putByte(PVA_MAGIC);
_sendBuffer->putByte(PVA_VERSION);
_sendBuffer->putByte(
_lastMessageStartPosition = _sendBuffer.getPosition();
_sendBuffer.putByte(PVA_MAGIC);
_sendBuffer.putByte(PVA_VERSION);
_sendBuffer.putByte(
(_lastSegmentedMessageType | _byteOrderFlag | _clientServerFlag)); // data message
_sendBuffer->putByte(command); // command
_sendBuffer->putInt(payloadSize);
_sendBuffer.putByte(command); // command
_sendBuffer.putInt(payloadSize);
// apply offset
if (_nextMessagePayloadOffset > 0)
_sendBuffer->setPosition(
_sendBuffer->getPosition() + _nextMessagePayloadOffset);
_sendBuffer.setPosition(
_sendBuffer.getPosition() + _nextMessagePayloadOffset);
}
@@ -648,11 +603,11 @@ void AbstractCodec::putControlMessage(
_lastMessageStartPosition =
std::numeric_limits<size_t>::max(); // TODO revise this
ensureBuffer(PVA_MESSAGE_HEADER_SIZE);
_sendBuffer->putByte(PVA_MAGIC);
_sendBuffer->putByte(PVA_VERSION);
_sendBuffer->putByte((0x01 | _byteOrderFlag | _clientServerFlag)); // control message
_sendBuffer->putByte(command); // command
_sendBuffer->putInt(data); // data
_sendBuffer.putByte(PVA_MAGIC);
_sendBuffer.putByte(PVA_VERSION);
_sendBuffer.putByte((0x01 | _byteOrderFlag | _clientServerFlag)); // control message
_sendBuffer.putByte(command); // command
_sendBuffer.putInt(data); // data
}
@@ -665,17 +620,14 @@ void AbstractCodec::endMessage(bool hasMoreSegments) {
if (_lastMessageStartPosition != std::numeric_limits<size_t>::max())
{
std::size_t lastPayloadBytePosition = _sendBuffer->getPosition();
// align
alignBuffer(PVA_ALIGNMENT);
std::size_t lastPayloadBytePosition = _sendBuffer.getPosition();
// set paylaod size (non-aligned)
std::size_t payloadSize =
lastPayloadBytePosition -
_lastMessageStartPosition - PVA_MESSAGE_HEADER_SIZE;
_sendBuffer->putInt(_lastMessageStartPosition + 4, payloadSize);
_sendBuffer.putInt(_lastMessageStartPosition + 4, payloadSize);
// set segmented bit
if (hasMoreSegments) {
@@ -683,15 +635,15 @@ void AbstractCodec::endMessage(bool hasMoreSegments) {
if (_lastSegmentedMessageType == 0)
{
std::size_t flagsPosition = _lastMessageStartPosition + 2;
epics::pvData::int8 type = _sendBuffer->getByte(flagsPosition);
epics::pvData::int8 type = _sendBuffer.getByte(flagsPosition);
// set first segment bit
_sendBuffer->putByte(flagsPosition, (type | 0x10));
_sendBuffer.putByte(flagsPosition, (type | 0x10));
// first + last segment bit == in-between segment
_lastSegmentedMessageType = type | 0x30;
_lastSegmentedMessageCommand =
_sendBuffer->getByte(flagsPosition + 1);
_sendBuffer.getByte(flagsPosition + 1);
}
_nextMessagePayloadOffset = lastPayloadBytePosition % PVA_ALIGNMENT;
_nextMessagePayloadOffset = 0;
}
else
{
@@ -700,7 +652,7 @@ void AbstractCodec::endMessage(bool hasMoreSegments) {
{
std::size_t flagsPosition = _lastMessageStartPosition + 2;
// set last segment bit (by clearing first segment bit)
_sendBuffer->putByte(flagsPosition,
_sendBuffer.putByte(flagsPosition,
(_lastSegmentedMessageType & 0xEF));
_lastSegmentedMessageType = 0;
}
@@ -730,7 +682,7 @@ void AbstractCodec::endMessage(bool hasMoreSegments) {
void AbstractCodec::ensureBuffer(std::size_t size) {
if (_sendBuffer->getRemaining() >= size)
if (_sendBuffer.getRemaining() >= size)
return;
// too large for buffer...
@@ -744,7 +696,7 @@ void AbstractCodec::ensureBuffer(std::size_t size) {
throw std::invalid_argument(s);
}
while (_sendBuffer->getRemaining() < size)
while (_sendBuffer.getRemaining() < size)
flush(false);
}
@@ -755,10 +707,10 @@ void AbstractCodec::flushSerializeBuffer() {
void AbstractCodec::flushSendBuffer() {
_sendBuffer->flip();
_sendBuffer.flip();
try {
send(_sendBuffer.get());
send(&_sendBuffer);
} catch (io_exception &) {
try {
if (isOpen())
@@ -769,7 +721,7 @@ void AbstractCodec::flushSendBuffer() {
throw connection_closed_exception("Failed to send buffer.");
}
_sendBuffer->clear();
_sendBuffer.clear();
_lastMessageStartPosition = std::numeric_limits<size_t>::max();
}
@@ -880,7 +832,7 @@ void AbstractCodec::processSendQueue()
if (sender.get() == 0)
{
// flush
if (_sendBuffer->getPosition() > 0)
if (_sendBuffer.getPosition() > 0)
flush(true);
sendCompleted(); // do not schedule sending
@@ -894,7 +846,7 @@ void AbstractCodec::processSendQueue()
try {
processSender(sender);
} catch(...) {
if (_sendBuffer->getPosition() > 0)
if (_sendBuffer.getPosition() > 0)
flush(true);
sendCompleted();
throw;
@@ -903,7 +855,7 @@ void AbstractCodec::processSendQueue()
}
// flush
if (_sendBuffer->getPosition() > 0)
if (_sendBuffer.getPosition() > 0)
flush(true);
}
@@ -928,9 +880,9 @@ void AbstractCodec::processSender(
ScopedLock lock(sender);
try {
_lastMessageStartPosition = _sendBuffer->getPosition();
_lastMessageStartPosition = _sendBuffer.getPosition();
sender->send(_sendBuffer.get(), this);
sender->send(&_sendBuffer, this);
// automatic end (to set payload size)
endMessage(false);
@@ -963,10 +915,10 @@ void AbstractCodec::enqueueSendRequest(
if (_senderThread == epicsThreadGetIdSelf() &&
_sendQueue.empty() &&
_sendBuffer->getRemaining() >= requiredBufferSize)
_sendBuffer.getRemaining() >= requiredBufferSize)
{
processSender(sender);
if (_sendBuffer->getPosition() > 0)
if (_sendBuffer.getPosition() > 0)
{
if (_lowLatency)
flush(true);
@@ -986,9 +938,9 @@ void AbstractCodec::setRecipient(osiSockAddr const & sendTo) {
void AbstractCodec::setByteOrder(int byteOrder)
{
_socketBuffer->setEndianess(byteOrder);
_socketBuffer.setEndianess(byteOrder);
// TODO sync
_sendBuffer->setEndianess(byteOrder);
_sendBuffer.setEndianess(byteOrder);
_byteOrderFlag = EPICS_ENDIAN_BIG == byteOrder ? 0x80 : 0x00;
}
@@ -1047,42 +999,22 @@ bool AbstractCodec::directDeserialize(ByteBuffer *existingBuffer, char* deserial
//
//
BlockingAbstractCodec::BlockingAbstractCodec(
bool serverFlag,
std::tr1::shared_ptr<epics::pvData::ByteBuffer> const & receiveBuffer,
std::tr1::shared_ptr<epics::pvData::ByteBuffer> const & sendBuffer,
int32_t socketSendBufferSize)
:AbstractCodec(serverFlag, receiveBuffer, sendBuffer, socketSendBufferSize, true)
,_readThread(epics::pvData::Thread::Config(this, &BlockingAbstractCodec::receiveThread)
.prio(epicsThreadPriorityCAServerLow)
.name("TCP-rx")
.autostart(false))
,_sendThread(epics::pvData::Thread::Config(this, &BlockingAbstractCodec::sendThread)
.prio(epicsThreadPriorityCAServerLow)
.name("TCP-tx")
.autostart(false))
BlockingTCPTransportCodec::~BlockingTCPTransportCodec()
{
_isOpen.getAndSet(true);
waitJoin();
}
BlockingAbstractCodec::~BlockingAbstractCodec()
{
assert(!_isOpen.get());
_sendThread.exitWait();
_readThread.exitWait();
}
void BlockingAbstractCodec::readPollOne() {
void BlockingTCPTransportCodec::readPollOne() {
throw std::logic_error("should not be called for blocking IO");
}
void BlockingAbstractCodec::writePollOne() {
void BlockingTCPTransportCodec::writePollOne() {
throw std::logic_error("should not be called for blocking IO");
}
void BlockingAbstractCodec::close() {
void BlockingTCPTransportCodec::close() {
if (_isOpen.getAndSet(false))
{
@@ -1101,26 +1033,41 @@ void BlockingAbstractCodec::close() {
}
}
void BlockingAbstractCodec::internalClose(bool /*force*/)
void BlockingTCPTransportCodec::waitJoin()
{
assert(!_isOpen.get());
_sendThread.exitWait();
_readThread.exitWait();
}
void BlockingTCPTransportCodec::internalClose(bool /*force*/)
{
this->internalDestroy();
// TODO sync
if (_securitySession)
_securitySession->close();
if (IS_LOGGABLE(logLevelDebug))
{
LOG(logLevelDebug,
"TCP socket to %s is to be closed.",
inetAddressToString(_socketAddress).c_str());
}
}
void BlockingAbstractCodec::internalPostClose(bool /*force*/) {
}
bool BlockingAbstractCodec::terminated() {
bool BlockingTCPTransportCodec::terminated() {
return !isOpen();
}
bool BlockingAbstractCodec::isOpen() {
bool BlockingTCPTransportCodec::isOpen() {
return _isOpen.get();
}
// NOTE: must not be called from constructor (e.g. needs shared_from_this())
void BlockingAbstractCodec::start() {
void BlockingTCPTransportCodec::start() {
_readThread.start();
@@ -1129,7 +1076,7 @@ void BlockingAbstractCodec::start() {
}
void BlockingAbstractCodec::receiveThread()
void BlockingTCPTransportCodec::receiveThread()
{
Transport::shared_pointer ptr = this->shared_from_this();
@@ -1152,7 +1099,7 @@ void BlockingAbstractCodec::receiveThread()
}
void BlockingAbstractCodec::sendThread()
void BlockingTCPTransportCodec::sendThread()
{
Transport::shared_pointer ptr = this->shared_from_this();
@@ -1178,7 +1125,7 @@ void BlockingAbstractCodec::sendThread()
}
void BlockingAbstractCodec::sendBufferFull(int tries) {
void BlockingTCPTransportCodec::sendBufferFull(int tries) {
// TODO constants
epicsThreadSleep(std::max<double>(tries * 0.1, 1));
}
@@ -1186,27 +1133,38 @@ void BlockingAbstractCodec::sendBufferFull(int tries) {
//
//
// BlockingSocketAbstractCodec
// BlockingTCPTransportCodec
//
//
//
BlockingSocketAbstractCodec::BlockingSocketAbstractCodec(
bool serverFlag,
SOCKET channel,
int32_t sendBufferSize,
int32_t receiveBufferSize):
BlockingAbstractCodec(
serverFlag,
std::tr1::shared_ptr<epics::pvData::ByteBuffer>(new ByteBuffer((std::max<std::size_t>((std::size_t)(
MAX_TCP_RECV + MAX_ENSURE_DATA_BUFFER_SIZE), receiveBufferSize) +
(PVA_ALIGNMENT - 1)) & (~(PVA_ALIGNMENT - 1)))),
std::tr1::shared_ptr<epics::pvData::ByteBuffer>(new ByteBuffer((std::max<std::size_t>((std::size_t)( MAX_TCP_RECV +
MAX_ENSURE_DATA_BUFFER_SIZE), receiveBufferSize) + (PVA_ALIGNMENT - 1))
& (~(PVA_ALIGNMENT - 1)))), sendBufferSize),
_channel(channel)
BlockingTCPTransportCodec::BlockingTCPTransportCodec(bool serverFlag, const Context::shared_pointer &context,
SOCKET channel, const ResponseHandler::shared_pointer &responseHandler,
size_t sendBufferSize,
size_t receiveBufferSize, int16 priority)
:AbstractCodec(
serverFlag,
sendBufferSize,
receiveBufferSize,
sendBufferSize,
true)
,_readThread(epics::pvData::Thread::Config(this, &BlockingTCPTransportCodec::receiveThread)
.prio(epicsThreadPriorityCAServerLow)
.name("TCP-rx")
.autostart(false))
,_sendThread(epics::pvData::Thread::Config(this, &BlockingTCPTransportCodec::sendThread)
.prio(epicsThreadPriorityCAServerLow)
.name("TCP-tx")
.autostart(false))
,_channel(channel)
,_context(context), _responseHandler(responseHandler)
,_remoteTransportReceiveBufferSize(MAX_TCP_RECV)
,_remoteTransportRevision(0), _priority(priority)
,_verified(false)
{
_isOpen.getAndSet(true);
// get remote address
osiSocklen_t saSize = sizeof(sockaddr);
int retval = getpeername(_channel, &(_socketAddress.sa), &saSize);
@@ -1226,7 +1184,7 @@ BlockingSocketAbstractCodec::BlockingSocketAbstractCodec(
}
// must be called only once, when there will be no operation on socket (e.g. just before tx/rx thread exists)
void BlockingSocketAbstractCodec::internalDestroy() {
void BlockingTCPTransportCodec::internalDestroy() {
if(_channel != INVALID_SOCKET) {
@@ -1262,15 +1220,17 @@ void BlockingSocketAbstractCodec::internalDestroy() {
_channel = INVALID_SOCKET; //TODO: mutex to guard _channel
}
Transport::shared_pointer thisSharedPtr = this->shared_from_this();
_context->getTransportRegistry()->remove(thisSharedPtr);
}
void BlockingSocketAbstractCodec::invalidDataStreamHandler() {
void BlockingTCPTransportCodec::invalidDataStreamHandler() {
close();
}
int BlockingSocketAbstractCodec::write(
int BlockingTCPTransportCodec::write(
epics::pvData::ByteBuffer *src) {
std::size_t remaining;
@@ -1307,7 +1267,7 @@ int BlockingSocketAbstractCodec::write(
}
std::size_t BlockingSocketAbstractCodec::getSocketReceiveBufferSize()
std::size_t BlockingTCPTransportCodec::getSocketReceiveBufferSize()
const {
osiSocklen_t intLen = sizeof(int);
@@ -1328,7 +1288,7 @@ const {
}
int BlockingSocketAbstractCodec::read(epics::pvData::ByteBuffer* dst) {
int BlockingTCPTransportCodec::read(epics::pvData::ByteBuffer* dst) {
std::size_t remaining;
while((remaining=dst->getRemaining()) > 0) {
@@ -1373,22 +1333,6 @@ int BlockingSocketAbstractCodec::read(epics::pvData::ByteBuffer* dst) {
}
void BlockingTCPTransportCodec::internalClose(bool force) {
BlockingSocketAbstractCodec::internalClose(force);
// TODO sync
if (_securitySession)
_securitySession->close();
if (IS_LOGGABLE(logLevelDebug))
{
LOG(logLevelDebug,
"TCP socket to %s is to be closed.",
inetAddressToString(_socketAddress).c_str());
}
}
bool BlockingTCPTransportCodec::verify(epics::pvData::int32 timeoutMs) {
return _verifiedEvent.wait(timeoutMs/1000.0) && _verified;
}
@@ -1437,9 +1381,6 @@ public:
control->flush(true);
}
void lock() {}
void unlock() {}
private:
PVField::shared_pointer _data;
};

View File

@@ -22,6 +22,7 @@
# undef beaconHandlerEpicsExportSharedSymbols
#endif
#include <pv/pvaDefs.h>
#include <pv/remote.h>
#include <pv/pvAccess.h>
@@ -56,7 +57,7 @@ public:
void beaconNotify(osiSockAddr* from,
epics::pvData::int8 remoteTransportRevision,
epics::pvData::TimeStamp* timestamp,
GUID const &guid,
ServerGUID const &guid,
epics::pvData::int16 sequentalID,
epics::pvData::int16 changeCount,
epics::pvData::PVFieldPtr data);
@@ -80,7 +81,7 @@ private:
/**
* Server GUID.
*/
GUID _serverGUID;
ServerGUID _serverGUID;
/**
* Server startup timestamp.
*/
@@ -101,7 +102,7 @@ private:
*/
bool updateBeacon(epics::pvData::int8 remoteTransportRevision,
epics::pvData::TimeStamp* timestamp,
GUID const &guid,
ServerGUID const &guid,
epics::pvData::int16 sequentalID,
epics::pvData::int16 changeCount);
/**

View File

@@ -28,6 +28,8 @@
# undef blockingUDPEpicsExportSharedSymbols
#endif
#include <shareLib.h>
#include <pv/remote.h>
#include <pv/pvaConstants.h>
#include <pv/inetAddressUtil.h>
@@ -35,6 +37,8 @@
namespace epics {
namespace pvAccess {
class BlockingUDPConnector;
enum InetAddressType { inetAddressType_all, inetAddressType_unicast, inetAddressType_broadcast_multicast };
class BlockingUDPTransport : public epics::pvData::NoDefaultMethods,
@@ -46,10 +50,13 @@ class BlockingUDPTransport : public epics::pvData::NoDefaultMethods,
public:
POINTER_DEFINITIONS(BlockingUDPTransport);
private:
friend class BlockingUDPConnector;
BlockingUDPTransport(bool serverFlag,
ResponseHandler::shared_pointer const & responseHandler,
SOCKET channel, osiSockAddr &bindAddress,
short remoteTransportRevision);
public:
static shared_pointer create(bool serverFlag,
ResponseHandler::shared_pointer const & responseHandler,
@@ -90,7 +97,7 @@ public:
}
virtual std::size_t getReceiveBufferSize() const {
return _receiveBuffer->getSize();
return _receiveBuffer.getSize();
}
virtual std::size_t getSocketReceiveBufferSize() const;
@@ -149,10 +156,10 @@ public:
// NOTE: this is not yet used for UDP
virtual void setByteOrder(int byteOrder) {
// called from receive thread... or before processing
_receiveBuffer->setEndianess(byteOrder);
_receiveBuffer.setEndianess(byteOrder);
// sync?!
_sendBuffer->setEndianess(byteOrder);
_sendBuffer.setEndianess(byteOrder);
}
virtual void enqueueSendRequest(TransportSender::shared_pointer const & sender);
@@ -164,12 +171,12 @@ public:
virtual void close();
virtual void ensureData(std::size_t size) {
if (_receiveBuffer->getRemaining() < size)
if (_receiveBuffer.getRemaining() < size)
throw std::underflow_error("no more data in UDP packet");
}
virtual void alignData(std::size_t alignment) {
_receiveBuffer->align(alignment);
_receiveBuffer.align(alignment);
}
virtual bool directSerialize(epics::pvData::ByteBuffer* /*existingBuffer*/, const char* /*toSerialize*/,
@@ -218,7 +225,7 @@ public:
}
virtual void alignBuffer(std::size_t alignment) {
_sendBuffer->align(alignment);
_sendBuffer.align(alignment);
}
virtual void cachedSerialize(
@@ -431,12 +438,12 @@ private:
/**
* Receive buffer.
*/
std::auto_ptr<epics::pvData::ByteBuffer> _receiveBuffer;
epics::pvData::ByteBuffer _receiveBuffer;
/**
* Send buffer.
*/
std::auto_ptr<epics::pvData::ByteBuffer> _sendBuffer;
epics::pvData::ByteBuffer _sendBuffer;
/**
* Last message start position.
@@ -448,7 +455,6 @@ private:
*/
epics::pvData::Mutex _mutex;
epics::pvData::Mutex _sendMutex;
epics::pvData::Event _shutdownEvent;
/**
* Thread ID
@@ -505,7 +511,7 @@ private:
typedef std::vector<BlockingUDPTransport::shared_pointer> BlockingUDPTransportVector;
epicsShareFunc void initializeUDPTransports(
void initializeUDPTransports(
bool serverFlag,
BlockingUDPTransportVector& udpTransports,
const IfaceNodeVector& ifaceList,

View File

@@ -19,7 +19,7 @@
# undef channelSearchManagerEpicsExportSharedSymbols
#endif
#include <pv/remote.h>
#include <pv/pvaDefs.h>
namespace epics {
namespace pvAccess {
@@ -46,7 +46,7 @@ public:
* @param serverAddress server address.
*/
// TODO make serverAddress an URI or similar
virtual void searchResponse(const GUID & guid, int8_t minorRevision, osiSockAddr* serverAddress) = 0;
virtual void searchResponse(const ServerGUID & guid, int8_t minorRevision, osiSockAddr* serverAddress) = 0;
};
class ChannelSearchManager {
@@ -85,7 +85,7 @@ public:
* @param minorRevision server minor PVA revision.
* @param serverAddress server address.
*/
virtual void searchResponse(const GUID & guid, pvAccessID cid, int32_t seqNo, int8_t minorRevision, osiSockAddr* serverAddress) = 0;
virtual void searchResponse(const ServerGUID & guid, pvAccessID cid, int32_t seqNo, int8_t minorRevision, osiSockAddr* serverAddress) = 0;
/**
* New server detected.

View File

@@ -39,6 +39,31 @@
#include <pv/namedLockPattern.h>
#include <pv/inetAddressUtil.h>
/* C++11 keywords
@code
struct Base {
virtual void foo();
};
struct Class : public Base {
virtual void foo() OVERRIDE FINAL FINAL;
};
@endcode
*/
#ifndef FINAL
# if __cplusplus>=201103L
# define FINAL final
# else
# define FINAL
# endif
#endif
#ifndef OVERRIDE
# if __cplusplus>=201103L
# define OVERRIDE override
# else
# define OVERRIDE
# endif
#endif
namespace epics {
namespace pvAccess {
@@ -150,8 +175,8 @@ public:
AbstractCodec(
bool serverFlag,
std::tr1::shared_ptr<epics::pvData::ByteBuffer> const & receiveBuffer,
std::tr1::shared_ptr<epics::pvData::ByteBuffer> const & sendBuffer,
size_t sendBufferSize,
size_t receiveBufferSize,
int32_t socketSendBufferSize,
bool blockingProcessQueue);
@@ -167,48 +192,47 @@ public:
virtual int write(epics::pvData::ByteBuffer* src) = 0;
virtual int read(epics::pvData::ByteBuffer* dst) = 0;
virtual bool isOpen() = 0;
virtual void close() = 0;
virtual ~AbstractCodec()
{
}
void alignBuffer(std::size_t alignment);
void ensureData(std::size_t size);
void alignData(std::size_t alignment);
void startMessage(
epics::pvData::int8 command,
std::size_t ensureCapacity = 0,
epics::pvData::int32 payloadSize = 0);
virtual void alignBuffer(std::size_t alignment) OVERRIDE FINAL;
virtual void ensureData(std::size_t size) OVERRIDE FINAL;
virtual void alignData(std::size_t alignment) OVERRIDE FINAL;
virtual void startMessage(
epics::pvData::int8 command,
std::size_t ensureCapacity = 0,
epics::pvData::int32 payloadSize = 0) OVERRIDE FINAL;
void putControlMessage(
epics::pvData::int8 command,
epics::pvData::int32 data);
void endMessage();
void ensureBuffer(std::size_t size);
void flushSerializeBuffer();
void flush(bool lastMessageCompleted);
epics::pvData::int8 command,
epics::pvData::int32 data);
virtual void endMessage() OVERRIDE FINAL;
virtual void ensureBuffer(std::size_t size) OVERRIDE FINAL;
virtual void flushSerializeBuffer() OVERRIDE FINAL;
virtual void flush(bool lastMessageCompleted) OVERRIDE FINAL;
void processWrite();
void processRead();
void processSendQueue();
void enqueueSendRequest(TransportSender::shared_pointer const & sender);
virtual void enqueueSendRequest(TransportSender::shared_pointer const & sender) OVERRIDE FINAL;
void enqueueSendRequest(TransportSender::shared_pointer const & sender,
std::size_t requiredBufferSize);
void setSenderThread();
void setRecipient(osiSockAddr const & sendTo);
void setByteOrder(int byteOrder);
virtual void setRecipient(osiSockAddr const & sendTo) OVERRIDE FINAL;
virtual void setByteOrder(int byteOrder) OVERRIDE FINAL;
static std::size_t alignedValue(std::size_t value, std::size_t alignment);
bool directSerialize(
epics::pvData::ByteBuffer * /*existingBuffer*/,
const char* /*toSerialize*/,
std::size_t /*elementCount*/, std::size_t /*elementSize*/);
virtual bool directSerialize(
epics::pvData::ByteBuffer * /*existingBuffer*/,
const char* /*toSerialize*/,
std::size_t /*elementCount*/, std::size_t /*elementSize*/) OVERRIDE;
bool directDeserialize(epics::pvData::ByteBuffer * /*existingBuffer*/,
char* /*deserializeTo*/,
std::size_t /*elementCount*/, std::size_t /*elementSize*/);
virtual bool directDeserialize(epics::pvData::ByteBuffer * /*existingBuffer*/,
char* /*deserializeTo*/,
std::size_t /*elementCount*/, std::size_t /*elementSize*/) OVERRIDE;
bool sendQueueEmpty() const {
return _sendQueue.empty();
@@ -235,8 +259,8 @@ protected:
bool _writeOpReady;
bool _lowLatency;
std::tr1::shared_ptr<epics::pvData::ByteBuffer> _socketBuffer;
std::tr1::shared_ptr<epics::pvData::ByteBuffer> _sendBuffer;
epics::pvData::ByteBuffer _socketBuffer;
epics::pvData::ByteBuffer _sendBuffer;
fair_queue<TransportSender> _sendQueue;
@@ -256,7 +280,7 @@ private:
std::size_t _storedLimit;
std::size_t _startPosition;
std::size_t _maxSendPayloadSize;
const std::size_t _maxSendPayloadSize;
std::size_t _lastMessageStartPosition;
std::size_t _lastSegmentedMessageType;
int8_t _lastSegmentedMessageCommand;
@@ -264,42 +288,163 @@ private:
epics::pvData::int8 _byteOrderFlag;
epics::pvData::int8 _clientServerFlag;
int32_t _socketSendBufferSize;
const size_t _socketSendBufferSize;
};
class epicsShareClass BlockingAbstractCodec:
class BlockingTCPTransportCodec:
public AbstractCodec,
public std::tr1::enable_shared_from_this<BlockingAbstractCodec>
public SecurityPluginControl,
public std::tr1::enable_shared_from_this<BlockingTCPTransportCodec>
{
public:
POINTER_DEFINITIONS(BlockingAbstractCodec);
POINTER_DEFINITIONS(BlockingTCPTransportCodec);
BlockingAbstractCodec(
bool serverFlag,
std::tr1::shared_ptr<epics::pvData::ByteBuffer> const & receiveBuffer,
std::tr1::shared_ptr<epics::pvData::ByteBuffer> const & sendBuffer,
int32_t socketSendBufferSize);
virtual ~BlockingAbstractCodec();
BlockingTCPTransportCodec(
bool serverFlag,
Context::shared_pointer const & context,
SOCKET channel,
ResponseHandler::shared_pointer const & responseHandler,
size_t sendBufferSize,
size_t receiveBufferSize,
epics::pvData::int16 priority);
virtual ~BlockingTCPTransportCodec();
void readPollOne();
void writePollOne();
void scheduleSend() {}
void sendCompleted() {}
void close();
bool terminated();
bool isOpen();
virtual void readPollOne() OVERRIDE FINAL;
virtual void writePollOne() OVERRIDE FINAL;
virtual void scheduleSend() OVERRIDE FINAL {}
virtual void sendCompleted() OVERRIDE FINAL {}
virtual void close() OVERRIDE FINAL;
virtual void waitJoin() OVERRIDE FINAL;
virtual bool terminated() OVERRIDE FINAL;
virtual bool isOpen() OVERRIDE FINAL;
void start();
virtual int read(epics::pvData::ByteBuffer* dst) OVERRIDE FINAL;
virtual int write(epics::pvData::ByteBuffer* src) OVERRIDE FINAL;
virtual const osiSockAddr* getLastReadBufferSocketAddress() OVERRIDE FINAL {
return &_socketAddress;
}
virtual void invalidDataStreamHandler() OVERRIDE FINAL;
virtual std::size_t getSocketReceiveBufferSize() const OVERRIDE FINAL;
virtual std::string getType() const OVERRIDE FINAL {
return std::string("tcp");
}
void internalDestroy();
virtual void processControlMessage() OVERRIDE FINAL {
if (_command == 2)
{
// check 7-th bit
setByteOrder(_flags < 0 ? EPICS_ENDIAN_BIG : EPICS_ENDIAN_LITTLE);
}
}
virtual void processApplicationMessage() OVERRIDE FINAL {
_responseHandler->handleResponse(&_socketAddress, shared_from_this(),
_version, _command, _payloadSize, &_socketBuffer);
}
virtual const osiSockAddr* getRemoteAddress() const OVERRIDE FINAL {
return &_socketAddress;
}
virtual const std::string& getRemoteName() const OVERRIDE FINAL {
return _socketName;
}
virtual epics::pvData::int8 getRevision() const OVERRIDE FINAL {
return PVA_PROTOCOL_REVISION;
}
virtual std::size_t getReceiveBufferSize() const OVERRIDE FINAL {
return _socketBuffer.getSize();
}
virtual epics::pvData::int16 getPriority() const OVERRIDE FINAL {
return _priority;
}
virtual void setRemoteRevision(epics::pvData::int8 revision) OVERRIDE FINAL {
_remoteTransportRevision = revision;
}
virtual void setRemoteTransportReceiveBufferSize(
std::size_t remoteTransportReceiveBufferSize) OVERRIDE FINAL {
_remoteTransportReceiveBufferSize = remoteTransportReceiveBufferSize;
}
virtual void setRemoteTransportSocketReceiveBufferSize(
std::size_t socketReceiveBufferSize) OVERRIDE FINAL {
_remoteTransportSocketReceiveBufferSize = socketReceiveBufferSize;
}
std::tr1::shared_ptr<const epics::pvData::Field>
virtual cachedDeserialize(epics::pvData::ByteBuffer* buffer) OVERRIDE FINAL
{
return _incomingIR.deserialize(buffer, this);
}
virtual void cachedSerialize(
const std::tr1::shared_ptr<const epics::pvData::Field>& field,
epics::pvData::ByteBuffer* buffer) OVERRIDE FINAL
{
_outgoingIR.serialize(field, buffer, this);
}
virtual void flushSendQueue() OVERRIDE FINAL { }
virtual bool isClosed() OVERRIDE FINAL {
return !isOpen();
}
void activate() {
Transport::shared_pointer thisSharedPtr = shared_from_this();
_context->getTransportRegistry()->put(thisSharedPtr);
start();
}
virtual bool verify(epics::pvData::int32 timeoutMs) OVERRIDE;
virtual void verified(epics::pvData::Status const & status) OVERRIDE;
bool isVerified() const {
return _verified; // TODO sync
}
virtual std::tr1::shared_ptr<SecuritySession> getSecuritySession() const OVERRIDE FINAL {
// TODO sync
return _securitySession;
}
virtual void authNZMessage(epics::pvData::PVField::shared_pointer const & data) OVERRIDE FINAL;
virtual void sendSecurityPluginMessage(epics::pvData::PVField::shared_pointer const & data) OVERRIDE FINAL;
private:
void receiveThread();
void sendThread();
protected:
void sendBufferFull(int tries);
virtual void internalDestroy() = 0;
virtual void sendBufferFull(int tries) OVERRIDE FINAL;
/**
* Called to any resources just before closing transport
@@ -313,191 +458,17 @@ protected:
* @param[in] force flag indicating if forced (e.g. forced
* disconnect) is required
*/
virtual void internalPostClose(bool force);
virtual void internalPostClose(bool force) {}
private:
AtomicValue<bool> _isOpen;
epics::pvData::Thread _readThread, _sendThread;
epics::pvData::Event _shutdownEvent;
};
class epicsShareClass BlockingSocketAbstractCodec:
public BlockingAbstractCodec
{
public:
BlockingSocketAbstractCodec(
bool serverFlag,
SOCKET channel,
int32_t sendBufferSize,
int32_t receiveBufferSize);
int read(epics::pvData::ByteBuffer* dst);
int write(epics::pvData::ByteBuffer* src);
const osiSockAddr* getLastReadBufferSocketAddress() {
return &_socketAddress;
}
void invalidDataStreamHandler();
std::size_t getSocketReceiveBufferSize() const;
protected:
void internalDestroy();
SOCKET _channel;
osiSockAddr _socketAddress;
std::string _socketName;
};
class BlockingTCPTransportCodec :
public BlockingSocketAbstractCodec,
public SecurityPluginControl
{
public:
std::string getType() const {
return std::string("tcp");
}
void internalDestroy() {
BlockingSocketAbstractCodec::internalDestroy();
Transport::shared_pointer thisSharedPtr = this->shared_from_this();
_context->getTransportRegistry()->remove(thisSharedPtr);
}
void changedTransport() {}
void processControlMessage() {
if (_command == 2)
{
// check 7-th bit
setByteOrder(_flags < 0 ? EPICS_ENDIAN_BIG : EPICS_ENDIAN_LITTLE);
}
}
void processApplicationMessage() {
_responseHandler->handleResponse(&_socketAddress, shared_from_this(),
_version, _command, _payloadSize, _socketBuffer.get());
}
const osiSockAddr* getRemoteAddress() const {
return &_socketAddress;
}
const std::string& getRemoteName() const {
return _socketName;
}
epics::pvData::int8 getRevision() const {
return PVA_PROTOCOL_REVISION;
}
std::size_t getReceiveBufferSize() const {
return _socketBuffer->getSize();
}
epics::pvData::int16 getPriority() const {
return _priority;
}
void setRemoteRevision(epics::pvData::int8 revision) {
_remoteTransportRevision = revision;
}
void setRemoteTransportReceiveBufferSize(
std::size_t remoteTransportReceiveBufferSize) {
_remoteTransportReceiveBufferSize = remoteTransportReceiveBufferSize;
}
void setRemoteTransportSocketReceiveBufferSize(
std::size_t socketReceiveBufferSize) {
_remoteTransportSocketReceiveBufferSize = socketReceiveBufferSize;
}
std::tr1::shared_ptr<const epics::pvData::Field>
cachedDeserialize(epics::pvData::ByteBuffer* buffer)
{
return _incomingIR.deserialize(buffer, this);
}
void cachedSerialize(
const std::tr1::shared_ptr<const epics::pvData::Field>& field,
epics::pvData::ByteBuffer* buffer)
{
_outgoingIR.serialize(field, buffer, this);
}
void flushSendQueue() { };
bool isClosed() {
return !isOpen();
}
void activate() {
Transport::shared_pointer thisSharedPtr = shared_from_this();
_context->getTransportRegistry()->put(thisSharedPtr);
start();
}
bool verify(epics::pvData::int32 timeoutMs);
void verified(epics::pvData::Status const & status);
bool isVerified() const {
return _verified; // TODO sync
}
std::tr1::shared_ptr<SecuritySession> getSecuritySession() const {
// TODO sync
return _securitySession;
}
void authNZMessage(epics::pvData::PVField::shared_pointer const & data);
void sendSecurityPluginMessage(epics::pvData::PVField::shared_pointer const & data);
protected:
BlockingTCPTransportCodec(
bool serverFlag,
Context::shared_pointer const & context,
SOCKET channel,
ResponseHandler::shared_pointer const & responseHandler,
int32_t sendBufferSize,
int32_t receiveBufferSize,
epics::pvData::int16 priority
):
BlockingSocketAbstractCodec(serverFlag, channel, sendBufferSize, receiveBufferSize),
_context(context), _responseHandler(responseHandler),
_remoteTransportReceiveBufferSize(MAX_TCP_RECV),
_remoteTransportRevision(0), _priority(priority),
_verified(false)
{
}
virtual void internalClose(bool force);
Context::shared_pointer _context;
IntrospectionRegistry _incomingIR;
@@ -515,11 +486,9 @@ private:
bool _verified;
epics::pvData::Mutex _verifiedMutex;
epics::pvData::Event _verifiedEvent;
};
class epicsShareClass BlockingServerTCPTransportCodec :
class BlockingServerTCPTransportCodec :
public BlockingTCPTransportCodec,
public ChannelHostingTransport,
public TransportSender {
@@ -554,38 +523,32 @@ public:
public:
bool acquire(std::tr1::shared_ptr<TransportClient> const & /*client*/)
virtual bool acquire(std::tr1::shared_ptr<TransportClient> const & /*client*/) OVERRIDE FINAL
{
return false;
}
void release(pvAccessID /*clientId*/) {}
virtual void release(pvAccessID /*clientId*/) OVERRIDE FINAL {}
pvAccessID preallocateChannelSID();
virtual void changedTransport() OVERRIDE {}
void depreallocateChannelSID(pvAccessID /*sid*/) {
virtual pvAccessID preallocateChannelSID() OVERRIDE FINAL;
virtual void depreallocateChannelSID(pvAccessID /*sid*/) OVERRIDE FINAL {
// noop
}
void registerChannel(
pvAccessID sid,
ServerChannel::shared_pointer const & channel);
virtual void registerChannel(
pvAccessID sid,
ServerChannel::shared_pointer const & channel) OVERRIDE FINAL;
void unregisterChannel(pvAccessID sid);
virtual void unregisterChannel(pvAccessID sid) OVERRIDE FINAL;
ServerChannel::shared_pointer getChannel(pvAccessID sid);
virtual ServerChannel::shared_pointer getChannel(pvAccessID sid) OVERRIDE FINAL;
int getChannelCount();
virtual int getChannelCount() OVERRIDE FINAL;
void lock() {
// noop
}
void unlock() {
// noop
}
bool verify(epics::pvData::int32 timeoutMs) {
virtual bool verify(epics::pvData::int32 timeoutMs) OVERRIDE FINAL {
TransportSender::shared_pointer transportSender =
std::tr1::dynamic_pointer_cast<TransportSender>(shared_from_this());
@@ -598,30 +561,30 @@ public:
return verifiedStatus;
}
void verified(epics::pvData::Status const & status) {
virtual void verified(epics::pvData::Status const & status) OVERRIDE FINAL {
_verificationStatusMutex.lock();
_verificationStatus = status;
_verificationStatusMutex.unlock();
BlockingTCPTransportCodec::verified(status);
}
void aliveNotification() {
virtual void aliveNotification() OVERRIDE FINAL {
// noop on server-side
}
void authNZInitialize(void *);
virtual void authNZInitialize(void *) OVERRIDE FINAL;
void authenticationCompleted(epics::pvData::Status const & status);
virtual void authenticationCompleted(epics::pvData::Status const & status) OVERRIDE FINAL;
void send(epics::pvData::ByteBuffer* buffer,
TransportSendControl* control);
virtual void send(epics::pvData::ByteBuffer* buffer,
TransportSendControl* control) OVERRIDE FINAL;
virtual ~BlockingServerTCPTransportCodec();
virtual ~BlockingServerTCPTransportCodec() OVERRIDE FINAL;
protected:
void destroyAllChannels();
virtual void internalClose(bool force);
virtual void internalClose(bool force) OVERRIDE FINAL;
private:
@@ -648,7 +611,7 @@ private:
};
class epicsShareClass BlockingClientTCPTransportCodec :
class BlockingClientTCPTransportCodec :
public BlockingTCPTransportCodec,
public TransportSender,
public epics::pvData::TimerCallback {
@@ -695,41 +658,33 @@ public:
void start();
virtual ~BlockingClientTCPTransportCodec();
virtual ~BlockingClientTCPTransportCodec() OVERRIDE FINAL;
virtual void timerStopped() {
virtual void timerStopped() OVERRIDE FINAL {
// noop
}
virtual void callback();
virtual void callback() OVERRIDE FINAL;
bool acquire(TransportClient::shared_pointer const & client);
virtual bool acquire(TransportClient::shared_pointer const & client) OVERRIDE FINAL;
void release(pvAccessID clientId);
virtual void release(pvAccessID clientId) OVERRIDE FINAL;
void changedTransport();
virtual void changedTransport() OVERRIDE FINAL;
void lock() {
// noop
}
virtual void aliveNotification() OVERRIDE FINAL;
void unlock() {
// noop
}
virtual void send(epics::pvData::ByteBuffer* buffer,
TransportSendControl* control) OVERRIDE FINAL;
void aliveNotification();
virtual void authNZInitialize(void *) OVERRIDE FINAL;
void send(epics::pvData::ByteBuffer* buffer,
TransportSendControl* control);
void authNZInitialize(void *);
void authenticationCompleted(epics::pvData::Status const & status);
virtual void authenticationCompleted(epics::pvData::Status const & status) OVERRIDE FINAL;
protected:
virtual void internalClose(bool force);
virtual void internalPostClose(bool force);
virtual void internalClose(bool force) OVERRIDE FINAL;
virtual void internalPostClose(bool force) OVERRIDE FINAL;
private:

View File

@@ -33,6 +33,7 @@
#include <pv/pvaConstants.h>
#include <pv/configuration.h>
#include <pv/fairQueue.h>
#include <pv/pvaDefs.h>
/// TODO only here because of the Lockable
#include <pv/pvAccess.h>
@@ -48,13 +49,6 @@ namespace pvAccess {
class TransportRegistry;
/**
* Globally unique ID.
*/
typedef struct {
char value[12];
} GUID;
enum QoS {
/**
* Default behavior.
@@ -94,8 +88,6 @@ enum QoS {
QOS_GET_PUT = 0x80
};
typedef epics::pvData::int32 pvAccessID;
enum ApplicationCommands {
CMD_BEACON = 0,
CMD_CONNECTION_VALIDATION = 1,
@@ -295,6 +287,9 @@ public:
*/
virtual void close() = 0;
//! Call after close() to wait for any worker threads to exit
virtual void waitJoin() {}
/**
* Check connection status.
* @return <code>true</code> if connected.
@@ -331,12 +326,12 @@ public:
virtual epics::pvData::Timer::shared_pointer getTimer() = 0;
//virtual TransportRegistry::shared_pointer getTransportRegistry() = 0;
virtual std::tr1::shared_ptr<TransportRegistry> getTransportRegistry() = 0;
virtual TransportRegistry* getTransportRegistry() = 0;
virtual Configuration::shared_pointer getConfiguration() = 0;
virtual Configuration::const_shared_pointer getConfiguration() = 0;
/**
* Get map of available security plug-ins.
@@ -566,13 +561,14 @@ public:
* Report status to clients (e.g. disconnected).
* @param status to report.
*/
virtual void reportStatus(epics::pvData::Status const & status) = 0;
virtual void reportStatus(Channel::ConnectionState status) = 0;
/**
* Get request requester.
* @return request requester.
* used by MessageHandler and reportChannelStateChange().
*
* May return NULL
*/
virtual std::tr1::shared_ptr<epics::pvData::Requester> getRequester() = 0;
virtual std::tr1::shared_ptr<ChannelBaseRequester> getRequester() = 0;
};
/**
@@ -617,38 +613,6 @@ public:
};
struct AtomicBoolean_null_deleter
{
void operator()(void const *) const {}
};
// standard performance on set/clear, use of tr1::shared_ptr lock-free counter for get
// alternative is to use boost::atomic
class AtomicBoolean
{
public:
AtomicBoolean() : counter(static_cast<void*>(0), AtomicBoolean_null_deleter()) {};
void set() {
mutex.lock();
setp = counter;
mutex.unlock();
}
void clear() {
mutex.lock();
setp.reset();
mutex.unlock();
}
bool get() const {
return counter.use_count() == 2;
}
private:
std::tr1::shared_ptr<void> counter;
std::tr1::shared_ptr<void> setp;
epics::pvData::Mutex mutex;
};
}
}

View File

@@ -25,7 +25,8 @@
# undef securityEpicsExportSharedSymbols
#endif
#include <pv/remote.h>
#include <pv/pvaDefs.h>
#include <pv/pvaConstants.h>
#include <pv/serializationHelper.h>
#include <pv/logger.h>
@@ -412,26 +413,6 @@ public:
}
};
class AuthNZHandler :
public AbstractResponseHandler,
private epics::pvData::NoDefaultMethods
{
public:
AuthNZHandler(Context* context) :
AbstractResponseHandler(context, "authNZ message")
{
}
virtual ~AuthNZHandler() {}
virtual void handleResponse(osiSockAddr* responseFrom,
Transport::shared_pointer const & transport,
epics::pvData::int8 version,
epics::pvData::int8 command,
size_t payloadSize,
epics::pvData::ByteBuffer* payloadBuffer);
};
class epicsShareClass SecurityPluginRegistry :
private epics::pvData::NoDefaultMethods
{

View File

@@ -0,0 +1,34 @@
#ifndef SECURITYIMPL_H
#define SECURITYIMPL_H
#include <pv/remote.h>
#include "security.h"
namespace epics {
namespace pvAccess {
class AuthNZHandler :
public AbstractResponseHandler,
private epics::pvData::NoDefaultMethods
{
public:
AuthNZHandler(Context* context) :
AbstractResponseHandler(context, "authNZ message")
{
}
virtual ~AuthNZHandler() {}
virtual void handleResponse(osiSockAddr* responseFrom,
Transport::shared_pointer const & transport,
epics::pvData::int8 version,
epics::pvData::int8 command,
size_t payloadSize,
epics::pvData::ByteBuffer* payloadBuffer);
};
}}
#endif // SECURITYIMPL_H

View File

@@ -22,6 +22,7 @@
#endif
#include <pv/channelSearchManager.h>
#include <pv/remote.h>
namespace epics {
namespace pvAccess {
@@ -95,7 +96,7 @@ public:
* @param minorRevision server minor PVA revision.
* @param serverAddress server address.
*/
void searchResponse(const GUID & guid, pvAccessID cid, int32_t seqNo, int8_t minorRevision, osiSockAddr* serverAddress);
void searchResponse(const ServerGUID & guid, pvAccessID cid, int32_t seqNo, int8_t minorRevision, osiSockAddr* serverAddress);
/**
* New server detected.
* Boost searching of all channels.
@@ -157,7 +158,8 @@ private:
/**
* Set of registered channels.
*/
std::map<pvAccessID,SearchInstance::shared_pointer> m_channels;
typedef std::map<pvAccessID,SearchInstance::weak_pointer> m_channels_t;
m_channels_t m_channels;
/**
* Time of last frame send.

View File

@@ -7,7 +7,7 @@
#include <osiProcess.h>
#define epicsExportSharedSymbols
#include <pv/security.h>
#include <pv/securityImpl.h>
using namespace epics::pvData;
using namespace epics::pvAccess;

View File

@@ -134,27 +134,30 @@ void SimpleChannelSearchManagerImpl::unregisterSearchInstance(SearchInstance::sh
{
Lock guard(m_channelMutex);
pvAccessID id = channel->getSearchInstanceID();
std::map<pvAccessID,SearchInstance::shared_pointer>::iterator channelsIter = m_channels.find(id);
m_channels_t::iterator channelsIter = m_channels.find(id);
if(channelsIter != m_channels.end())
m_channels.erase(id);
}
void SimpleChannelSearchManagerImpl::searchResponse(const GUID & guid, pvAccessID cid, int32_t /*seqNo*/, int8_t minorRevision, osiSockAddr* serverAddress)
void SimpleChannelSearchManagerImpl::searchResponse(const ServerGUID & guid, pvAccessID cid, int32_t /*seqNo*/, int8_t minorRevision, osiSockAddr* serverAddress)
{
Lock guard(m_channelMutex);
std::map<pvAccessID,SearchInstance::shared_pointer>::iterator channelsIter = m_channels.find(cid);
m_channels_t::iterator channelsIter = m_channels.find(cid);
if(channelsIter == m_channels.end())
{
guard.unlock();
Context::shared_pointer ctxt(m_context.lock());
// TODO: proper action if !ctxt???
if(!ctxt) return;
// enable duplicate reports
SearchInstance::shared_pointer si = std::tr1::dynamic_pointer_cast<SearchInstance>(m_context.lock()->getChannel(cid));
SearchInstance::shared_pointer si = std::tr1::dynamic_pointer_cast<SearchInstance>(ctxt->getChannel(cid));
if (si)
si->searchResponse(guid, minorRevision, serverAddress);
}
else
{
SearchInstance::shared_pointer si = channelsIter->second;
SearchInstance::shared_pointer si(channelsIter->second.lock());
// remove from search list
m_channels.erase(cid);
@@ -162,7 +165,8 @@ void SimpleChannelSearchManagerImpl::searchResponse(const GUID & guid, pvAccessI
guard.unlock();
// then notify SearchInstance
si->searchResponse(guid, minorRevision, serverAddress);
if(si)
si->searchResponse(guid, minorRevision, serverAddress);
}
}
@@ -275,10 +279,12 @@ void SimpleChannelSearchManagerImpl::boost()
{
Lock guard(m_channelMutex);
Lock guard2(m_userValueMutex);
std::map<pvAccessID,SearchInstance::shared_pointer>::iterator channelsIter = m_channels.begin();
m_channels_t::iterator channelsIter = m_channels.begin();
for(; channelsIter != m_channels.end(); channelsIter++)
{
int32_t& userValue = channelsIter->second->getUserValue();
SearchInstance::shared_pointer inst(channelsIter->second.lock());
if(!inst) continue;
int32_t& userValue = inst->getUserValue();
userValue = BOOST_VALUE;
}
}
@@ -306,9 +312,14 @@ void SimpleChannelSearchManagerImpl::callback()
{
Lock guard(m_channelMutex);
toSend.reserve(m_channels.size());
std::map<pvAccessID,SearchInstance::shared_pointer>::iterator channelsIter = m_channels.begin();
for(; channelsIter != m_channels.end(); channelsIter++)
toSend.push_back(channelsIter->second);
for(m_channels_t::iterator channelsIter = m_channels.begin();
channelsIter != m_channels.end(); channelsIter++)
{
SearchInstance::shared_pointer inst(channelsIter->second.lock());
if(!inst) continue;
toSend.push_back(inst);
}
}
vector<SearchInstance::shared_pointer>::iterator siter = toSend.begin();

View File

@@ -2,6 +2,4 @@
SRC_DIRS += $(PVACCESS_SRC)/remoteClient
INC += pv/clientContextImpl.h
LIBSRCS += clientContextImpl.cpp
pvAccess_SRCS += clientContextImpl.cpp

File diff suppressed because it is too large Load Diff

View File

@@ -26,8 +26,6 @@
#include <shareLib.h>
class ChannelSearchManager;
namespace epics {
namespace pvAccess {
@@ -47,7 +45,7 @@ public:
virtual void destroyChannel(bool force) = 0;
virtual void connectionCompleted(pvAccessID sid/*, rights*/) = 0;
virtual void createChannelFailed() = 0;
virtual std::tr1::shared_ptr<ClientContextImpl> getContext() = 0;
virtual ClientContextImpl* getContext() = 0;
virtual void channelDestroyedOnServer() = 0;
virtual pvAccessID getServerChannelID() = 0;
@@ -67,8 +65,6 @@ class ClientContextImpl : public Context
public:
POINTER_DEFINITIONS(ClientContextImpl);
static std::string PROVIDER_NAME;
/**
* Get context implementation version.
* @return version of the context implementation.
@@ -80,16 +76,10 @@ public:
*/
virtual void initialize() = 0; // public?
/**
* Get channel provider implementation.
* @return the channel provider.
*/
//virtual ChannelProvider::shared_pointer const & getProvider() = 0;
/**
* Prints detailed information about the context to the standard output stream.
*/
virtual void printInfo() = 0;
virtual void printInfo() {printInfo(std::cout);}
/**
* Prints detailed information about the context to the specified output stream.
@@ -101,7 +91,7 @@ public:
* Dispose (destroy) server context.
* This calls <code>destroy()</code> and silently handles all exceptions.
*/
virtual void dispose() = 0;
virtual void dispose() EPICS_DEPRECATED = 0;
virtual ChannelSearchManager::shared_pointer getChannelSearchManager() = 0;
@@ -110,8 +100,10 @@ public:
virtual void registerChannel(ChannelImpl::shared_pointer const & channel) = 0;
virtual void unregisterChannel(ChannelImpl::shared_pointer const & channel) = 0;
virtual void destroyChannel(ChannelImpl::shared_pointer const & channel, bool force) = 0;
virtual ChannelImpl::shared_pointer createChannelInternal(std::string const &name, ChannelRequester::shared_pointer const & requester, short priority, std::auto_ptr<InetAddrVector>& addresses) = 0;
virtual ChannelImpl::shared_pointer createChannelInternal(std::string const &name,
ChannelRequester::shared_pointer const & requester,
short priority,
std::auto_ptr<InetAddrVector>& addresses) = 0;
virtual ResponseRequest::shared_pointer getResponseRequest(pvAccessID ioid) = 0;
virtual pvAccessID registerResponseRequest(ResponseRequest::shared_pointer const & request) = 0;
@@ -125,13 +117,13 @@ public:
virtual std::tr1::shared_ptr<BeaconHandler> getBeaconHandler(std::string const & protocol, osiSockAddr* responseFrom) = 0;
virtual void configure(epics::pvData::PVStructure::shared_pointer configuration) = 0;
virtual void flush() = 0;
virtual void poll() = 0;
virtual void flush() {}
virtual void poll() {}
virtual void destroy() = 0;
};
epicsShareFunc ChannelProvider::shared_pointer createClientProvider(const Configuration::shared_pointer& conf);
ChannelProvider::shared_pointer createClientProvider(const Configuration::shared_pointer& conf);
}
}

View File

@@ -4,4 +4,4 @@ SRC_DIRS += $(PVACCESS_SRC)/rpcClient
INC += pv/rpcClient.h
LIBSRCS += rpcClient.cpp
pvAccess_SRCS += rpcClient.cpp

View File

@@ -14,6 +14,7 @@
# undef epicsExportSharedSymbols
#endif
#include <pv/pvData.h>
#include <pv/valueBuilder.h>
#ifdef rpcClientEpicsExportSharedSymbols
# define epicsExportSharedSymbols
# undef rpcClientEpicsExportSharedSymbols
@@ -40,14 +41,6 @@ class epicsShareClass RPCClient
public:
POINTER_DEFINITIONS(RPCClient);
/**
* Create a RPCClient.
*
* @param serviceName the service name
* @return the RPCClient interface
*/
static shared_pointer create(const std::string & serviceName);
/**
* Create a RPCClient.
*
@@ -56,7 +49,14 @@ public:
* @return the RPCClient interface
*/
static shared_pointer create(const std::string & serviceName,
epics::pvData::PVStructure::shared_pointer const & pvRequest);
epics::pvData::PVStructure::shared_pointer const & pvRequest = epics::pvData::PVStructure::shared_pointer());
RPCClient(const std::string & serviceName,
epics::pvData::PVStructure::shared_pointer const & pvRequest,
const ChannelProvider::shared_pointer& provider = ChannelProvider::shared_pointer(),
const std::string& address = std::string());
~RPCClient() {destroy();}
/**
* Performs complete blocking RPC call, opening a channel and connecting to the
@@ -69,7 +69,7 @@ public:
* @throws RPCRequestException exception thrown on error on timeout.
*/
static epics::pvData::PVStructure::shared_pointer sendRequest(const std::string & serviceName,
epics::pvData::PVStructure::shared_pointer const &request, double timeOut = RPCCLIENT_DEFAULT_TIMEOUT);
epics::pvData::PVStructure::shared_pointer const &request, double timeOut = RPCCLIENT_DEFAULT_TIMEOUT) EPICS_DEPRECATED;
@@ -136,15 +136,19 @@ public:
*/
epics::pvData::PVStructure::shared_pointer waitResponse(double timeout = RPCCLIENT_DEFAULT_TIMEOUT);
virtual ~RPCClient() {}
private:
protected:
RPCClient(const std::string & serviceName,
epics::pvData::PVStructure::shared_pointer const & pvRequest);
std::string m_serviceName;
const std::string m_serviceName;
ChannelProvider::shared_pointer m_provider;
Channel::shared_pointer m_channel;
epics::pvData::PVStructure::shared_pointer m_pvRequest;
ChannelRPC::shared_pointer m_rpc;
const epics::pvData::PVStructure::shared_pointer m_pvRequest;
struct RPCRequester;
std::tr1::shared_ptr<RPCRequester> m_rpc_requester;
RPCClient(const RPCClient&);
RPCClient& operator=(const RPCClient&);
};
}

View File

@@ -7,8 +7,10 @@
#include <iostream>
#include <string>
#include <epicsEvent.h>
#include <pv/pvData.h>
#include <pv/event.h>
#include <pv/current_function.h>
#define epicsExportSharedSymbols
#include <pv/pvAccess.h>
@@ -18,211 +20,126 @@
#include "pv/rpcClient.h"
#if 0
# define TRACE(msg) std::cerr<<"TRACE: "<<CURRENT_FUNCTION<<" : "<< msg <<"\n"
#else
# define TRACE(msg)
#endif
using namespace epics::pvData;
namespace TR1 = std::tr1;
using std::string;
namespace pvd = epics::pvData;
namespace pva = epics::pvAccess;
namespace epics
namespace epics{namespace pvAccess{
struct RPCClient::RPCRequester : public pva::ChannelRPCRequester
{
POINTER_DEFINITIONS(RPCRequester);
namespace pvAccess
{
pvd::Mutex mutex;
ChannelRPC::shared_pointer op;
pvd::Status conn_status, resp_status;
epics::pvData::PVStructure::shared_pointer next_args, last_data;
epicsEvent event;
bool inprogress, last;
RPCRequester()
:conn_status(pvd::Status::error("Never connected"))
,resp_status(pvd::Status::error("Never connected"))
,inprogress(false)
,last(false)
{}
virtual ~RPCRequester() {}
class ChannelAndRPCRequesterImpl :
public TR1::enable_shared_from_this<ChannelAndRPCRequesterImpl>,
public virtual epics::pvAccess::ChannelRequester,
public virtual epics::pvAccess::ChannelRPCRequester
{
private:
Mutex m_mutex;
Event m_event;
Event m_connectionEvent;
Status m_status;
PVStructure::shared_pointer m_response;
ChannelRPC::shared_pointer m_channelRPC;
PVStructure::shared_pointer m_pvRequest;
public:
ChannelAndRPCRequesterImpl(PVStructure::shared_pointer const & pvRequest)
: m_pvRequest(pvRequest)
{
}
virtual string getRequesterName()
{
return "ChannelAndRPCRequesterImpl";
}
virtual void message(std::string const & message, MessageType messageType)
{
std::cerr << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")" << std::endl;
}
void channelCreated(
const epics::pvData::Status& status,
Channel::shared_pointer const & channel)
{
if (status.isSuccess())
{
// show warning
if (!status.isOK())
{
std::cerr << "[" << channel->getChannelName() << "] channel create: " << status << std::endl;
}
}
else
{
std::cerr << "[" << channel->getChannelName() << "] failed to create a channel: " << status << std::endl;
{
Lock lock(m_mutex);
m_status = status;
}
m_connectionEvent.signal();
}
}
void channelStateChange(
Channel::shared_pointer const & channel,
Channel::ConnectionState connectionState)
{
if (connectionState == Channel::CONNECTED)
{
bool rpcAlreadyConnectedOnce = false;
{
Lock lock(m_mutex);
rpcAlreadyConnectedOnce = (m_channelRPC.get() != 0);
}
if (!rpcAlreadyConnectedOnce)
{
channel->createChannelRPC(shared_from_this(), m_pvRequest);
}
}
/*
else if (connectionState != Channel::DESTROYED)
{
std::cerr << "[" << channel->getChannelName() << "] channel state change: " << Channel::ConnectionStateNames[connectionState] << std::endl;
}
*/
}
virtual std::string getRequesterName() { return "RPCClient::RPCRequester"; }
virtual void channelRPCConnect(
const epics::pvData::Status & status,
ChannelRPC::shared_pointer const & channelRPC)
const pvd::Status& status,
ChannelRPC::shared_pointer const & operation)
{
if (status.isSuccess())
bool lastreq, inprog;
pvd::PVStructure::shared_pointer args;
{
if (!status.isOK())
std::cerr << "[" << channelRPC->getChannel()->getChannelName() << "] channel RPC create: " << status << std::endl;
pvd::Lock L(mutex);
TRACE("status="<<status);
op = operation;
conn_status = status;
args.swap(next_args);
lastreq = last;
inprog = inprogress;
if(inprog && args)
TRACE("request deferred: "<<args);
}
else
{
std::cerr << "[" << channelRPC->getChannel()->getChannelName() << "] failed to create channel RPC: " << status << std::endl;
if(inprog && args) {
TRACE("request deferred: "<<args);
if(lastreq)
operation->lastRequest();
operation->request(args);
}
{
Lock lock(m_mutex);
m_status = status;
m_channelRPC = channelRPC;
}
m_connectionEvent.signal();
event.signal();
}
virtual void requestDone(
const epics::pvData::Status & status,
ChannelRPC::shared_pointer const & channelRPC,
epics::pvData::PVStructure::shared_pointer const & pvResponse)
const pvd::Status& status,
ChannelRPC::shared_pointer const & operation,
pvd::PVStructure::shared_pointer const & pvResponse)
{
if (status.isSuccess())
TRACE("status="<<status<<" response:\n"<<pvResponse<<"\n");
{
if (!status.isOK())
std::cerr << "[" << channelRPC->getChannel()->getChannelName() << "] channel RPC: " << status << std::endl;
pvd::Lock L(mutex);
if(!inprogress) {
std::cerr<<"pva provider give RPC requestDone() when no request in progress\n";
} else {
resp_status = status;
last_data = pvResponse;
if(resp_status.isSuccess() && !last_data) {
resp_status = pvd::Status::error("No reply data");
}
inprogress = false;
}
}
else
event.signal();
}
virtual void channelDisconnect(bool destroy)
{
TRACE("destroy="<<destroy);
{
std::cerr << "[" << channelRPC->getChannel()->getChannelName() << "] failed to RPC: " << status << std::endl;
pvd::Lock L(mutex);
resp_status = conn_status = pvd::Status::error("Connection lost");
last_data.reset();
next_args.reset();
inprogress = false;
}
{
Lock lock(m_mutex);
m_status = status;
m_response = pvResponse;
}
m_event.signal();
}
bool waitForResponse(double timeOut)
{
return m_event.wait(timeOut);
}
bool waitUntilRPCConnected(double timeOut)
{
if (isRPCConnected())
return true;
return m_connectionEvent.wait(timeOut);
}
bool isRPCConnected()
{
Lock lock(m_mutex);
return (m_channelRPC.get() != 0);
}
PVStructure::shared_pointer & getResponse()
{
Lock lock(m_mutex);
return m_response;
}
Status & getStatus()
{
Lock lock(m_mutex);
return m_status;
}
void request(PVStructure::shared_pointer const & pvArgument, bool lastRequest)
{
ChannelRPC::shared_pointer rpc;
{
Lock lock(m_mutex);
rpc = m_channelRPC;
}
if (!rpc)
throw std::runtime_error("channel RPC not connected");
if (lastRequest)
rpc->lastRequest();
rpc->request(pvArgument);
event.signal();
}
};
RPCClient::RPCClient(const std::string & serviceName,
PVStructure::shared_pointer const & pvRequest)
: m_serviceName(serviceName), m_pvRequest(pvRequest)
pvd::PVStructure::shared_pointer const & pvRequest,
const ChannelProvider::shared_pointer &provider,
const std::string &address)
: m_serviceName(serviceName)
, m_provider(provider)
, m_pvRequest(pvRequest ? pvRequest : pvd::createRequest(""))
{
ClientFactory::start();
if(!m_provider)
m_provider = ChannelProviderRegistry::clients()->getProvider("pva");
if(!m_provider)
throw std::logic_error("Unknown Provider");
m_channel = m_provider->createChannel(serviceName, DefaultChannelRequester::build(),
ChannelProvider::PRIORITY_DEFAULT,
address);
if(!m_channel)
throw std::logic_error("provider createChannel() succeeds w/ NULL Channel");
m_rpc_requester.reset(new RPCRequester);
m_rpc = m_channel->createChannelRPC(m_rpc_requester, m_pvRequest);
if(!m_rpc)
throw std::logic_error("channel createChannelRPC() NULL");
}
void RPCClient::destroy()
@@ -232,43 +149,43 @@ void RPCClient::destroy()
m_channel->destroy();
m_channel.reset();
}
if (m_rpc)
{
m_rpc->destroy();
m_rpc.reset();
}
}
bool RPCClient::connect(double timeout)
{
if (m_channel &&
TR1::dynamic_pointer_cast<ChannelAndRPCRequesterImpl>(m_channel->getChannelRequester())->isRPCConnected())
return true;
issueConnect();
return waitConnect(timeout);
}
void RPCClient::issueConnect()
{
ChannelProvider::shared_pointer provider = getChannelProviderRegistry()->getProvider("pva");
// TODO try to reuse ChannelRequesterImpl instance (i.e. create only once)
TR1::shared_ptr<ChannelAndRPCRequesterImpl> channelRequesterImpl(new ChannelAndRPCRequesterImpl(m_pvRequest));
m_channel = provider->createChannel(m_serviceName, channelRequesterImpl);
}
bool RPCClient::waitConnect(double timeout)
{
if (!m_channel)
throw std::runtime_error("issueConnect() must be called before waitConnect()");
TR1::shared_ptr<ChannelAndRPCRequesterImpl> channelRequesterImpl =
TR1::dynamic_pointer_cast<ChannelAndRPCRequesterImpl>(m_channel->getChannelRequester());
return channelRequesterImpl->waitUntilRPCConnected(timeout) &&
channelRequesterImpl->isRPCConnected();
pvd::Lock L(m_rpc_requester->mutex);
TRACE("timeout="<<timeout);
while(!m_rpc_requester->conn_status.isSuccess()) {
L.unlock();
if(!m_rpc_requester->event.wait(timeout)) {
TRACE("TIMEOUT");
return false;
}
L.lock();
}
TRACE("Connected");
return true;
}
PVStructure::shared_pointer RPCClient::request(
PVStructure::shared_pointer const & pvArgument,
pvd::PVStructure::shared_pointer RPCClient::request(
pvd::PVStructure::shared_pointer const & pvArgument,
double timeout,
bool lastRequest)
{
@@ -278,69 +195,87 @@ PVStructure::shared_pointer RPCClient::request(
return waitResponse(timeout); // TODO reduce timeout for a time spent on connect
}
else
throw epics::pvAccess::RPCRequestException(Status::STATUSTYPE_ERROR, "connection timeout");
throw epics::pvAccess::RPCRequestException(pvd::Status::STATUSTYPE_ERROR, "connection timeout");
}
void RPCClient::issueRequest(
PVStructure::shared_pointer const & pvArgument,
pvd::PVStructure::shared_pointer const & pvArgument,
bool lastRequest)
{
if (!m_channel)
throw std::runtime_error("channel not connected");
TR1::shared_ptr<ChannelAndRPCRequesterImpl> channelRequesterImpl =
TR1::dynamic_pointer_cast<ChannelAndRPCRequesterImpl>(m_channel->getChannelRequester());
channelRequesterImpl->request(pvArgument, lastRequest);
}
PVStructure::shared_pointer RPCClient::waitResponse(double timeout)
{
TR1::shared_ptr<ChannelAndRPCRequesterImpl> channelRequesterImpl =
TR1::dynamic_pointer_cast<ChannelAndRPCRequesterImpl>(m_channel->getChannelRequester());
if (channelRequesterImpl->waitForResponse(timeout))
{
Status & status = channelRequesterImpl->getStatus();
if (status.isSuccess())
{
// release response structure
PVStructure::shared_pointer & response = channelRequesterImpl->getResponse();
PVStructure::shared_pointer retVal = response;
response.reset();
return retVal;
pvd::Lock L(m_rpc_requester->mutex);
TRACE("conn_status="<<m_rpc_requester->conn_status
<<" resp_status="<<m_rpc_requester->resp_status
<<" args:\n"<<pvArgument);
if(m_rpc_requester->inprogress)
throw std::logic_error("Request already in progress");
m_rpc_requester->inprogress = true;
m_rpc_requester->resp_status = pvd::Status::error("No Data");
if(!m_rpc_requester->conn_status.isSuccess()) {
TRACE("defer");
m_rpc_requester->last = lastRequest;
m_rpc_requester->next_args = pvArgument;
return;
}
else
throw epics::pvAccess::RPCRequestException(status.getType(), status.getMessage());
TRACE("request args: "<<pvArgument);
}
else
throw epics::pvAccess::RPCRequestException(Status::STATUSTYPE_ERROR, "RPC timeout");
if(lastRequest)
m_rpc->lastRequest();
m_rpc->request(pvArgument);
}
RPCClient::shared_pointer RPCClient::create(const std::string & serviceName)
pvd::PVStructure::shared_pointer RPCClient::waitResponse(double timeout)
{
PVStructure::shared_pointer pvRequest =
CreateRequest::create()->createRequest("");
return create(serviceName, pvRequest);
pvd::Lock L(m_rpc_requester->mutex);
TRACE("timeout="<<timeout);
while(m_rpc_requester->inprogress)
{
L.unlock();
if(!m_rpc_requester->event.wait(timeout)) {
TRACE("TIMEOUT");
throw RPCRequestException(pvd::Status::STATUSTYPE_ERROR, "RPC timeout");
}
L.lock();
}
TRACE("Complete: conn_status="<<m_rpc_requester->conn_status
<<" resp_status="<<m_rpc_requester->resp_status
<<" data:\n"<<m_rpc_requester->last_data);
if(!m_rpc_requester->conn_status.isSuccess())
throw RPCRequestException(pvd::Status::STATUSTYPE_ERROR, m_rpc_requester->conn_status.getMessage());
if(!m_rpc_requester->resp_status.isSuccess())
throw RPCRequestException(pvd::Status::STATUSTYPE_ERROR, m_rpc_requester->resp_status.getMessage());
// consume last_data so that we can't possibly return it twice
pvd::PVStructure::shared_pointer data;
data.swap(m_rpc_requester->last_data);
if(!data)
throw std::logic_error("No request in progress");
// copy it so that the caller need not worry about whether it will overwritten
// when the next request is issued
pvd::PVStructure::shared_pointer ret(pvd::getPVDataCreate()->createPVStructure(data->getStructure()));
ret->copyUnchecked(*data);
return ret;
}
RPCClient::shared_pointer RPCClient::create(const std::string & serviceName,
PVStructure::shared_pointer const & pvRequest)
pvd::PVStructure::shared_pointer const & pvRequest)
{
ClientFactory::start();
return RPCClient::shared_pointer(new RPCClient(serviceName, pvRequest));
}
PVStructure::shared_pointer RPCClient::sendRequest(const std::string & serviceName,
PVStructure::shared_pointer const & queryRequest,
pvd::PVStructure::shared_pointer RPCClient::sendRequest(const std::string & serviceName,
pvd::PVStructure::shared_pointer const & queryRequest,
double timeOut)
{
RPCClient::shared_pointer client = RPCClient::create(serviceName);
return client->request(queryRequest, timeOut);
RPCClient client(serviceName, queryRequest);
return client.request(queryRequest, timeOut);
}
}
}
}}// namespace epics::pvAccess

View File

@@ -5,5 +5,5 @@ SRC_DIRS += $(PVACCESS_SRC)/rpcService
INC += pv/rpcService.h
INC += pv/rpcServer.h
LIBSRCS += rpcService.cpp
LIBSRCS += rpcServer.cpp
pvAccess_SRCS += rpcService.cpp
pvAccess_SRCS += rpcServer.cpp

View File

@@ -28,26 +28,25 @@
namespace epics {
namespace pvAccess {
class ServerContext;
class RPCChannelProvider;
//! Serves (only) RPCServiceAsync and RPCService instances.
class epicsShareClass RPCServer :
public std::tr1::enable_shared_from_this<RPCServer>
{
private:
ServerContextImpl::shared_pointer m_serverContext;
ChannelProviderFactory::shared_pointer m_channelProviderFactory;
ChannelProvider::shared_pointer m_channelProviderImpl;
// TODO no thread poll implementation
std::tr1::shared_ptr<ServerContext> m_serverContext;
std::tr1::shared_ptr<RPCChannelProvider> m_channelProviderImpl;
public:
POINTER_DEFINITIONS(RPCServer);
RPCServer();
explicit RPCServer(const Configuration::const_shared_pointer& conf = Configuration::const_shared_pointer());
virtual ~RPCServer();
void registerService(std::string const & serviceName, RPCService::shared_pointer const & service);
void registerService(std::string const & serviceName, RPCServiceAsync::shared_pointer const & service);
void unregisterService(std::string const & serviceName);
@@ -65,12 +64,13 @@ public:
*/
void printInfo();
const std::tr1::shared_ptr<ServerContext>& getServer() const { return m_serverContext; }
};
epicsShareFunc Channel::shared_pointer createRPCChannel(ChannelProvider::shared_pointer const & provider,
std::string const & channelName,
ChannelRequester::shared_pointer const & channelRequester,
Service::shared_pointer const & rpcService);
RPCServiceAsync::shared_pointer const & rpcService);
}
}

View File

@@ -32,42 +32,25 @@ namespace pvAccess {
class epicsShareClass RPCRequestException : public std::runtime_error {
public:
explicit RPCRequestException(std::string const & message) :
std::runtime_error(message), m_status(epics::pvData::Status::STATUSTYPE_ERROR)
{}
RPCRequestException(epics::pvData::Status::StatusType status, std::string const & message) :
std::runtime_error(message), m_status(status)
{
}
{}
epics::pvData::Status::StatusType getStatus() const {
return m_status;
}
epics::pvData::Status asStatus() const {
return epics::pvData::Status(m_status, what());
}
private:
epics::pvData::Status::StatusType m_status;
};
class epicsShareClass Service
{
public:
POINTER_DEFINITIONS(Service);
virtual ~Service() {};
};
class epicsShareClass RPCService :
public virtual Service
{
public:
POINTER_DEFINITIONS(RPCService);
virtual ~RPCService() {};
virtual epics::pvData::PVStructure::shared_pointer request(
epics::pvData::PVStructure::shared_pointer const & args
) = 0;
};
class epicsShareClass RPCResponseCallback
{
public:
@@ -81,8 +64,7 @@ public:
) = 0;
};
class epicsShareClass RPCServiceAsync :
public virtual Service
class epicsShareClass RPCServiceAsync
{
public:
POINTER_DEFINITIONS(RPCServiceAsync);
@@ -95,6 +77,27 @@ public:
) = 0;
};
typedef RPCServiceAsync Service EPICS_DEPRECATED;
class epicsShareClass RPCService :
public RPCServiceAsync
{
public:
POINTER_DEFINITIONS(RPCService);
virtual ~RPCService() {};
virtual epics::pvData::PVStructure::shared_pointer request(
epics::pvData::PVStructure::shared_pointer const & args
) = 0;
private:
virtual void request(
epics::pvData::PVStructure::shared_pointer const & args,
RPCResponseCallback::shared_pointer const & callback
) OVERRIDE FINAL;
};
}
}

View File

@@ -10,6 +10,7 @@
#define epicsExportSharedSymbols
#include <pv/rpcServer.h>
#include <pv/serverContextImpl.h>
#include <pv/wildcard.h>
using namespace epics::pvData;
@@ -27,14 +28,14 @@ class ChannelRPCServiceImpl :
private:
Channel::shared_pointer m_channel;
ChannelRPCRequester::shared_pointer m_channelRPCRequester;
Service::shared_pointer m_rpcService;
RPCServiceAsync::shared_pointer m_rpcService;
AtomicBoolean m_lastRequest;
public:
ChannelRPCServiceImpl(
Channel::shared_pointer const & channel,
ChannelRPCRequester::shared_pointer const & channelRPCRequester,
Service::shared_pointer const & rpcService) :
RPCServiceAsync::shared_pointer const & rpcService) :
m_channel(channel),
m_channelRPCRequester(channelRPCRequester),
m_rpcService(rpcService),
@@ -47,46 +48,6 @@ public:
destroy();
}
void processRequest(RPCService::shared_pointer const & service,
epics::pvData::PVStructure::shared_pointer const & pvArgument)
{
epics::pvData::PVStructure::shared_pointer result;
Status status = Status::Ok;
bool ok = true;
try
{
result = service->request(pvArgument);
}
catch (RPCRequestException& rre)
{
status = Status(rre.getStatus(), rre.what());
ok = false;
}
catch (std::exception& ex)
{
status = Status(Status::STATUSTYPE_FATAL, ex.what());
ok = false;
}
catch (...)
{
// handle user unexpected errors
status = Status(Status::STATUSTYPE_FATAL, "Unexpected exception caught while calling RPCService.request(PVStructure).");
ok = false;
}
// check null result
if (ok && result.get() == 0)
{
status = Status(Status::STATUSTYPE_FATAL, "RPCService.request(PVStructure) returned null.");
}
m_channelRPCRequester->requestDone(status, shared_from_this(), result);
if (m_lastRequest.get())
destroy();
}
virtual void requestDone(
epics::pvData::Status const & status,
epics::pvData::PVStructure::shared_pointer const & result
@@ -98,12 +59,11 @@ public:
destroy();
}
void processRequest(RPCServiceAsync::shared_pointer const & service,
epics::pvData::PVStructure::shared_pointer const & pvArgument)
virtual void request(epics::pvData::PVStructure::shared_pointer const & pvArgument)
{
try
{
service->request(pvArgument, shared_from_this());
m_rpcService->request(pvArgument, shared_from_this());
}
catch (std::exception& ex)
{
@@ -130,25 +90,6 @@ public:
// we wait for callback to be called
}
virtual void request(epics::pvData::PVStructure::shared_pointer const & pvArgument)
{
RPCService::shared_pointer rpcService =
std::tr1::dynamic_pointer_cast<RPCService>(m_rpcService);
if (rpcService)
{
processRequest(rpcService, pvArgument);
return;
}
RPCServiceAsync::shared_pointer rpcServiceAsync =
std::tr1::dynamic_pointer_cast<RPCServiceAsync>(m_rpcService);
if (rpcServiceAsync)
{
processRequest(rpcServiceAsync, pvArgument);
return;
}
}
void lastRequest()
{
m_lastRequest.set();
@@ -168,16 +109,6 @@ public:
{
// noop
}
virtual void lock()
{
// noop
}
virtual void unlock()
{
// noop
}
};
@@ -195,7 +126,7 @@ private:
string m_channelName;
ChannelRequester::shared_pointer m_channelRequester;
Service::shared_pointer m_rpcService;
RPCServiceAsync::shared_pointer m_rpcService;
public:
POINTER_DEFINITIONS(RPCChannel);
@@ -204,7 +135,7 @@ public:
ChannelProvider::shared_pointer const & provider,
string const & channelName,
ChannelRequester::shared_pointer const & channelRequester,
Service::shared_pointer const & rpcService) :
RPCServiceAsync::shared_pointer const & rpcService) :
m_provider(provider),
m_channelName(channelName),
m_channelRequester(channelRequester),
@@ -250,12 +181,6 @@ public:
return none;
}
virtual void getField(GetFieldRequester::shared_pointer const & requester,std::string const & /*subField*/)
{
requester->getDone(epics::pvData::Status(epics::pvData::Status::STATUSTYPE_ERROR, "Only RPC is implemented"),
epics::pvData::Field::shared_pointer());
}
virtual ChannelRPC::shared_pointer createChannelRPC(
ChannelRPCRequester::shared_pointer const & channelRPCRequester,
epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/)
@@ -304,7 +229,7 @@ public:
Channel::shared_pointer createRPCChannel(ChannelProvider::shared_pointer const & provider,
std::string const & channelName,
ChannelRequester::shared_pointer const & channelRequester,
Service::shared_pointer const & rpcService)
RPCServiceAsync::shared_pointer const & rpcService)
{
// TODO use std::make_shared
std::tr1::shared_ptr<RPCChannel> tp(
@@ -386,7 +311,7 @@ public:
ChannelRequester::shared_pointer const & channelRequester,
short /*priority*/)
{
Service::shared_pointer service;
RPCServiceAsync::shared_pointer service;
RPCServiceMap::const_iterator iter;
{
@@ -429,7 +354,7 @@ public:
throw std::runtime_error("not supported");
}
void registerService(std::string const & serviceName, Service::shared_pointer const & service)
void registerService(std::string const & serviceName, RPCServiceAsync::shared_pointer const & service)
{
Lock guard(m_mutex);
m_services[serviceName] = service;
@@ -458,7 +383,7 @@ public:
private:
// assumes sync on services
Service::shared_pointer findWildService(string const & wildcard)
RPCServiceAsync::shared_pointer findWildService(string const & wildcard)
{
if (!m_wildServices.empty())
for (RPCWildServiceList::iterator iter = m_wildServices.begin();
@@ -467,7 +392,7 @@ private:
if (Wildcard::wildcardfit(iter->first.c_str(), wildcard.c_str()))
return iter->second;
return Service::shared_pointer();
return RPCServiceAsync::shared_pointer();
}
// (too) simple check
@@ -479,10 +404,10 @@ private:
(pattern.find('[') != string::npos && pattern.find(']') != string::npos));
}
typedef std::map<string, Service::shared_pointer> RPCServiceMap;
typedef std::map<string, RPCServiceAsync::shared_pointer> RPCServiceMap;
RPCServiceMap m_services;
typedef std::vector<std::pair<string, Service::shared_pointer> > RPCWildServiceList;
typedef std::vector<std::pair<string, RPCServiceAsync::shared_pointer> > RPCWildServiceList;
RPCWildServiceList m_wildServices;
epics::pvData::Mutex m_mutex;
@@ -492,52 +417,12 @@ string RPCChannelProvider::PROVIDER_NAME("rpcService");
Status RPCChannelProvider::noSuchChannelStatus(Status::STATUSTYPE_ERROR, "no such channel");
class RPCChannelProviderFactory : public ChannelProviderFactory
RPCServer::RPCServer(const Configuration::const_shared_pointer &conf)
:m_channelProviderImpl(new RPCChannelProvider)
{
public:
POINTER_DEFINITIONS(RPCChannelProviderFactory);
RPCChannelProviderFactory() :
m_channelProviderImpl(new RPCChannelProvider())
{
}
virtual std::string getFactoryName()
{
return RPCChannelProvider::PROVIDER_NAME;
}
virtual ChannelProvider::shared_pointer sharedInstance()
{
return m_channelProviderImpl;
}
virtual ChannelProvider::shared_pointer newInstance()
{
// TODO use std::make_shared
std::tr1::shared_ptr<RPCChannelProvider> tp(new RPCChannelProvider());
ChannelProvider::shared_pointer channelProvider = tp;
return channelProvider;
}
private:
RPCChannelProvider::shared_pointer m_channelProviderImpl;
};
RPCServer::RPCServer()
{
// TODO factory is never deregistered, multiple RPCServer instances create multiple factories, etc.
m_channelProviderFactory.reset(new RPCChannelProviderFactory());
registerChannelProviderFactory(m_channelProviderFactory);
m_channelProviderImpl = m_channelProviderFactory->sharedInstance();
m_serverContext = ServerContextImpl::create();
m_serverContext->setChannelProviderName(m_channelProviderImpl->getProviderName());
m_serverContext->initialize(getChannelProviderRegistry());
m_serverContext = ServerContext::create(ServerContext::Config()
.config(conf)
.provider(m_channelProviderImpl));
}
RPCServer::~RPCServer()
@@ -590,22 +475,17 @@ void RPCServer::runInNewThread(int seconds)
void RPCServer::destroy()
{
m_serverContext->destroy();
}
void RPCServer::registerService(std::string const & serviceName, RPCService::shared_pointer const & service)
{
std::tr1::dynamic_pointer_cast<RPCChannelProvider>(m_channelProviderImpl)->registerService(serviceName, service);
m_serverContext->shutdown();
}
void RPCServer::registerService(std::string const & serviceName, RPCServiceAsync::shared_pointer const & service)
{
std::tr1::dynamic_pointer_cast<RPCChannelProvider>(m_channelProviderImpl)->registerService(serviceName, service);
m_channelProviderImpl->registerService(serviceName, service);
}
void RPCServer::unregisterService(std::string const & serviceName)
{
std::tr1::dynamic_pointer_cast<RPCChannelProvider>(m_channelProviderImpl)->unregisterService(serviceName);
m_channelProviderImpl->unregisterService(serviceName);
}
}

View File

@@ -6,3 +6,30 @@
#define epicsExportSharedSymbols
#include <pv/rpcService.h>
namespace pvd = epics::pvData;
namespace epics{namespace pvAccess{
void RPCService::request(
pvd::PVStructure::shared_pointer const & args,
RPCResponseCallback::shared_pointer const & callback)
{
assert(callback && args);
pvd::PVStructure::shared_pointer ret;
pvd::Status sts;
try {
ret = request(args);
}catch(RPCRequestException& e){
sts = e.asStatus();
throw;
}catch(std::exception& e){
sts = pvd::Status::error(e.what());
}
if(!ret) {
sts = pvd::Status(pvd::Status::STATUSTYPE_FATAL, "RPCService.request(PVStructure) returned null.");
}
callback->requestDone(sts, ret);
}
}} // namespace epics::pvAccess

View File

@@ -3,15 +3,11 @@
SRC_DIRS += $(PVACCESS_SRC)/server
INC += pv/serverContext.h
INC += pv/responseHandlers.h
INC += pv/serverChannelImpl.h
INC += pv/baseChannelRequester.h
INC += pv/beaconEmitter.h
INC += pv/beaconServerStatusProvider.h
LIBSRCS += responseHandlers.cpp
LIBSRCS += serverContext.cpp
LIBSRCS += serverChannelImpl.cpp
LIBSRCS += baseChannelRequester.cpp
LIBSRCS += beaconEmitter.cpp
LIBSRCS += beaconServerStatusProvider.cpp
pvAccess_SRCS += responseHandlers.cpp
pvAccess_SRCS += serverContext.cpp
pvAccess_SRCS += serverChannelImpl.cpp
pvAccess_SRCS += baseChannelRequester.cpp
pvAccess_SRCS += beaconEmitter.cpp
pvAccess_SRCS += beaconServerStatusProvider.cpp

View File

@@ -100,16 +100,6 @@ void BaseChannelRequesterMessageTransportSender::send(ByteBuffer* buffer, Transp
epics::pvData::SerializeHelper::serializeString(_message, buffer, control);
}
void BaseChannelRequesterMessageTransportSender::lock()
{
// noop
}
void BaseChannelRequesterMessageTransportSender::unlock()
{
// noop
}
BaseChannelRequesterFailureMessageTransportSender::BaseChannelRequesterFailureMessageTransportSender(const int8 command,
Transport::shared_pointer const & transport, const pvAccessID ioid, const int8 qos, const Status& status) :
_command(command),
@@ -128,16 +118,6 @@ void BaseChannelRequesterFailureMessageTransportSender::send(ByteBuffer* buffer,
_status.serialize(buffer, control);
}
void BaseChannelRequesterFailureMessageTransportSender::lock()
{
// noop
}
void BaseChannelRequesterFailureMessageTransportSender::unlock()
{
// noop
}
}
}

View File

@@ -14,7 +14,7 @@
#include <pv/beaconEmitter.h>
#include <pv/serializationHelper.h>
#include <pv/logger.h>
#include <pv/serverContext.h>
#include <pv/serverContextImpl.h>
using namespace std;
using namespace epics::pvData;
@@ -49,16 +49,6 @@ BeaconEmitter::~BeaconEmitter()
// destroy();
}
void BeaconEmitter::lock()
{
//noop
}
void BeaconEmitter::unlock()
{
//noop
}
void BeaconEmitter::send(ByteBuffer* buffer, TransportSendControl* control)
{
// get server status

View File

@@ -7,26 +7,15 @@
#ifndef BASECHANNELREQUESTER_H_
#define BASECHANNELREQUESTER_H_
#ifdef epicsExportSharedSymbols
# define baseChannelRequesterEpicsExportSharedSymbols
# undef epicsExportSharedSymbols
#endif
#include <pv/requester.h>
#include <pv/destroyable.h>
#ifdef baseChannelRequesterEpicsExportSharedSymbols
# define epicsExportSharedSymbols
# undef baseChannelRequesterEpicsExportSharedSymbols
#endif
#include <pv/serverContext.h>
#include <pv/serverContextImpl.h>
#include <pv/serverChannelImpl.h>
namespace epics {
namespace pvAccess {
class BaseChannelRequester : virtual public epics::pvData::Requester, public epics::pvData::Destroyable
class BaseChannelRequester : virtual public epics::pvData::Requester, public Destroyable
{
public:
BaseChannelRequester(ServerContextImpl::shared_pointer const & context, ServerChannelImpl::shared_pointer const & channel,
@@ -65,8 +54,6 @@ class BaseChannelRequesterMessageTransportSender : public TransportSender
public:
BaseChannelRequesterMessageTransportSender(const pvAccessID _ioid, const std::string message,const epics::pvData::MessageType messageType);
void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control);
void lock();
void unlock();
private:
const pvAccessID _ioid;
const std::string _message;
@@ -78,8 +65,6 @@ class BaseChannelRequesterFailureMessageTransportSender : public TransportSender
public:
BaseChannelRequesterFailureMessageTransportSender(const epics::pvData::int8 command, Transport::shared_pointer const & transport, const pvAccessID ioid, const epics::pvData::int8 qos, const epics::pvData::Status& status);
void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control);
void lock();
void unlock();
private:
const epics::pvData::int8 _command;

View File

@@ -59,9 +59,6 @@ public:
virtual ~BeaconEmitter();
void lock();
void unlock();
void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control);
void timerStopped();
@@ -113,7 +110,7 @@ private:
/**
* Server GUID.
*/
GUID _guid;
ServerGUID _guid;
/**
* Fast (at startup) beacon period (in sec).

View File

@@ -20,6 +20,8 @@
# undef beaconServerStatusProviderEpicsExportSharedSymbols
#endif
#include <shareLib.h>
namespace epics {
namespace pvAccess {

Some files were not shown because too many files have changed in this diff Show More