pull from epics-base
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,8 +5,6 @@ include/
|
||||
db/
|
||||
dbd/
|
||||
html/
|
||||
documentation/html
|
||||
documentation/*.tag
|
||||
envPaths
|
||||
configure/*.local
|
||||
!configure/ExampleRELEASE.local
|
||||
|
||||
26
.travis.yml
Normal file
26
.travis.yml
Normal 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
|
||||
11
Makefile
11
Makefile
@@ -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
|
||||
|
||||
@@ -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
10
ci/travis-build.sh
Executable 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
69
ci/travis-prepare.sh
Executable 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
|
||||
@@ -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
5
documentation/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
*.tag
|
||||
*.db
|
||||
*.tmp
|
||||
html/
|
||||
latex/
|
||||
2413
documentation/Doxyfile
Normal file
2413
documentation/Doxyfile
Normal file
File diff suppressed because it is too large
Load Diff
14
documentation/Makefile
Normal file
14
documentation/Makefile
Normal 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
|
||||
74
documentation/client_ownership.dot
Normal file
74
documentation/client_ownership.dot
Normal 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
45
documentation/commit-gh.sh
Executable 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
57
documentation/examples.h
Normal 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
27
documentation/mainpage.h
Normal 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 */
|
||||
8
documentation/ownership.dot
Normal file
8
documentation/ownership.dot
Normal 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
345
documentation/providers.h
Normal 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
5
documentation/pvtools.h
Normal file
@@ -0,0 +1,5 @@
|
||||
/** @page pvtools Command Line Utilities
|
||||
|
||||
|
||||
|
||||
*/
|
||||
3
documentation/readme
Normal file
3
documentation/readme
Normal file
@@ -0,0 +1,3 @@
|
||||
get libstdc++ tag file from
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/libstdc++.tag
|
||||
181
documentation/release_notes.h
Normal file
181
documentation/release_notes.h
Normal 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
19
examples/Makefile
Normal 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
170
examples/getme.cpp
Normal 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
29
examples/miniget.cpp
Normal 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
103
examples/minimonitor.cpp
Normal 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
33
examples/miniput.cpp
Normal 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
285
examples/monitorme.cpp
Normal 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
235
examples/putme.cpp
Normal 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
316
examples/spamme.cpp
Normal 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
92
notes.md.txt
Normal 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.
|
||||
@@ -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
|
||||
#----------------------------------------
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
18
src/Makefile
18
src/Makefile
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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 --------------- */
|
||||
|
||||
@@ -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
234
src/client/client.cpp
Normal 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
213
src/client/clientGet.cpp
Normal 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
|
||||
217
src/client/clientMonitor.cpp
Normal file
217
src/client/clientMonitor.cpp
Normal 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
159
src/client/clientRPC.cpp
Normal 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
303
src/client/clientSync.cpp
Normal 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
211
src/client/pv/monitor.h
Normal 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
@@ -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
391
src/client/pva/client.h
Normal 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
|
||||
@@ -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, "");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
SRC_DIRS += $(PVACCESS_SRC)/factory
|
||||
|
||||
LIBSRCS += ChannelAccessFactory.cpp
|
||||
pvAccess_SRCS += ChannelAccessFactory.cpp
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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" {
|
||||
|
||||
@@ -4,4 +4,4 @@ SRC_DIRS += $(PVACCESS_SRC)/mb
|
||||
|
||||
INC += pv/pvAccessMB.h
|
||||
|
||||
LIBSRCS += pvAccessMB.cpp
|
||||
pvAccess_SRCS += pvAccessMB.cpp
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
48
src/pva/pv/pvaDefs.h
Normal 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
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
/**
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
34
src/remote/pv/securityImpl.h
Normal file
34
src/remote/pv/securityImpl.h
Normal 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
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,4 +4,4 @@ SRC_DIRS += $(PVACCESS_SRC)/rpcClient
|
||||
|
||||
INC += pv/rpcClient.h
|
||||
|
||||
LIBSRCS += rpcClient.cpp
|
||||
pvAccess_SRCS += rpcClient.cpp
|
||||
|
||||
@@ -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&);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user