Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a5ab8625d | ||
|
|
a115c9cd15 | ||
|
|
6ba40803d8 | ||
|
|
8d6094c55a |
@@ -1,10 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e -x
|
||||
|
||||
make -j2
|
||||
|
||||
if [ "$TEST" != "NO" ]
|
||||
then
|
||||
make -j2 tapfiles
|
||||
make -j2 -s test-results
|
||||
fi
|
||||
@@ -1,83 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e -x
|
||||
|
||||
cat << EOF > configure/RELEASE.local
|
||||
EPICS_BASE=$HOME/.source/epics-base
|
||||
EOF
|
||||
|
||||
install -d "$HOME/.source"
|
||||
cd "$HOME/.source"
|
||||
|
||||
git clone --quiet --depth 5 --branch "$BRBASE" https://github.com/${REPOBASE:-epics-base}/epics-base.git epics-base
|
||||
(cd epics-base && 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 >> epics-base/configure/CONFIG_SITE
|
||||
USR_CPPFLAGS += $USR_CPPFLAGS
|
||||
USR_CFLAGS += $USR_CFLAGS
|
||||
USR_CXXFLAGS += $USR_CXXFLAGS
|
||||
EOF
|
||||
|
||||
# set RTEMS to eg. "4.9" or "4.10"
|
||||
# requires qemu, bison, flex, texinfo, install-info
|
||||
if [ -n "$RTEMS" ]
|
||||
then
|
||||
echo "Cross RTEMS${RTEMS} for pc386"
|
||||
curl -L "https://github.com/mdavidsaver/rsb/releases/download/20171203-${RTEMS}/i386-rtems${RTEMS}-trusty-20171203-${RTEMS}.tar.bz2" \
|
||||
| tar -C / -xmj
|
||||
|
||||
sed -i -e '/^RTEMS_VERSION/d' -e '/^RTEMS_BASE/d' epics-base/configure/os/CONFIG_SITE.Common.RTEMS
|
||||
cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.RTEMS
|
||||
RTEMS_VERSION=$RTEMS
|
||||
RTEMS_BASE=$HOME/.rtems
|
||||
EOF
|
||||
cat << EOF >> epics-base/configure/CONFIG_SITE
|
||||
CROSS_COMPILER_TARGET_ARCHS += RTEMS-pc386-qemu
|
||||
EOF
|
||||
fi
|
||||
|
||||
make -j2 -C epics-base $EXTRA
|
||||
30
.gitignore
vendored
30
.gitignore
vendored
@@ -1,17 +1,15 @@
|
||||
/cfg/
|
||||
/bin/
|
||||
/lib/
|
||||
/db/
|
||||
/dbd/
|
||||
/html/
|
||||
/include/
|
||||
/templates/
|
||||
/configure/*.local
|
||||
/configure/RELEASE.*
|
||||
/configure/CONFIG_SITE.*
|
||||
O.*/
|
||||
/QtC-*
|
||||
bin/
|
||||
lib/
|
||||
doc/
|
||||
include/
|
||||
db/
|
||||
dbd/
|
||||
documentation/html
|
||||
documentation/*.tag
|
||||
envPaths
|
||||
*.orig
|
||||
*.log
|
||||
.*.swp
|
||||
configure/*.local
|
||||
configure/RELEASE.*
|
||||
configure/CONFIG_SITE.*
|
||||
!configure/ExampleRELEASE.local
|
||||
**/O.*
|
||||
QtC-*
|
||||
|
||||
30
.travis.yml
30
.travis.yml
@@ -1,30 +0,0 @@
|
||||
sudo: false
|
||||
dist: trusty
|
||||
language: c++
|
||||
compiler:
|
||||
- gcc
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libreadline6-dev
|
||||
- libncurses5-dev
|
||||
- perl
|
||||
- clang
|
||||
- g++-mingw-w64-i686
|
||||
- qemu-system-x86
|
||||
install:
|
||||
- ./.ci/travis-prepare.sh
|
||||
script:
|
||||
- ./.ci/travis-build.sh
|
||||
env:
|
||||
- BRBASE=7.0 CMPLR=gcc
|
||||
- BRBASE=7.0 CMPLR=clang
|
||||
- BRBASE=7.0 USR_CXXFLAGS=-std=c++11
|
||||
- BRBASE=7.0 CMPLR=clang USR_CXXFLAGS=-std=c++11
|
||||
- BRBASE=7.0 WINE=32 TEST=NO STATIC=YES
|
||||
- BRBASE=7.0 WINE=32 TEST=NO STATIC=NO
|
||||
- BRBASE=7.0 RTEMS=4.10 TEST=NO
|
||||
- BRBASE=7.0 RTEMS=4.9 TEST=NO
|
||||
- BRBASE=3.16
|
||||
- BRBASE=3.15
|
||||
- BRBASE=3.14
|
||||
3
Makefile
3
Makefile
@@ -11,7 +11,4 @@ src_DEPEND_DIRS = configure
|
||||
DIRS += testApp
|
||||
testApp_DEPEND_DIRS = src
|
||||
|
||||
DIRS += examples
|
||||
examples_DEPEND_DIRS = src
|
||||
|
||||
include $(TOP)/configure/RULES_TOP
|
||||
|
||||
61
README.md
61
README.md
@@ -1,16 +1,57 @@
|
||||
# pvaDataCPP
|
||||
pvaDataCPP
|
||||
==========
|
||||
|
||||
The EPICS **pvData** API provides a set of classes and utilities that form the core of the EPICS PVA implementation.
|
||||
pvDataCPP is a set of data types and utilities that form part of the EPICS V4 project.
|
||||
|
||||
The pvDataCPP module is a part of the EPICS software toolkit that implements pvData structures as C++ class objects.
|
||||
|
||||
## Links
|
||||
Further Info
|
||||
------------
|
||||
|
||||
- General information about EPICS can be found at the
|
||||
[EPICS Controls website](https://epics-controls.org).
|
||||
- API documentation for this module can be found on its
|
||||
[Github Pages website](https://epics-base.github.io/pvDataCPP/).
|
||||
Consult the documents in the documentation directory, in particular
|
||||
|
||||
* pvDataCPP.html
|
||||
* RELEASE_NOTES.md
|
||||
|
||||
Also see the [EPICS Version 4 website](http://epics-pvdata.sourceforge.net)
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
The pvDataCPP requires recent versions of the following software:
|
||||
|
||||
1. EPICS Base (v3.14.12.3 or later)
|
||||
2. EPICS4 pvCommonCPP (4.1.0 or later)
|
||||
|
||||
(pvCommonCPP may not be needed depending on host/compiler.)
|
||||
|
||||
|
||||
Building
|
||||
--------
|
||||
|
||||
Building uses the make utility and the EPICS base build system.
|
||||
|
||||
The build system needs the location of the prerequisites, e.g. by placing the
|
||||
lines of the form
|
||||
|
||||
PVCOMMON = /home/install/epicsV4/pvCommonCPP
|
||||
EPICS_BASE = /home/install/epics/base
|
||||
|
||||
pointing to the locations in a file called RELEASE.local
|
||||
in the configure directory or the parent directory of pvDataCPP.
|
||||
|
||||
With this in place, to build type make
|
||||
|
||||
make
|
||||
|
||||
To perform a clean build type
|
||||
|
||||
make clean uninstall
|
||||
|
||||
To run the unit tests type
|
||||
|
||||
make runtests
|
||||
|
||||
For more information on the EPICS build system consult the
|
||||
[Application Development guide](http://www.aps.anl.gov/epics/base/R3-14/12-docs/AppDevGuide.pdf).
|
||||
|
||||
## Building
|
||||
|
||||
This module is included as a submodule of a full EPICS 7 release and will be compiled during builds of that software.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
EPICS_PVD_MAJOR_VERSION = 8
|
||||
EPICS_PVD_MINOR_VERSION = 0
|
||||
EPICS_PVD_MAINTENANCE_VERSION = 0
|
||||
EPICS_PVD_DEVELOPMENT_FLAG = 0
|
||||
@@ -20,14 +20,9 @@ CHECK_RELEASE = YES
|
||||
# INSTALL_LOCATION here.
|
||||
#INSTALL_LOCATION=</path/name/to/install/top>
|
||||
|
||||
USR_CPPFLAGS += -DPVD_INTERNAL
|
||||
|
||||
-include $(TOP)/../CONFIG_SITE.local
|
||||
-include $(TOP)/configure/CONFIG_SITE.local
|
||||
|
||||
# MSVC - skip defining min()/max() macros
|
||||
USR_CPPFLAGS_WIN32 += -DNOMINMAX
|
||||
|
||||
ifdef WITH_COVERAGE
|
||||
USR_CPPFLAGS += --coverage
|
||||
USR_LDFLAGS += --coverage
|
||||
|
||||
@@ -2,8 +2,6 @@ TOP=..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
CFG += CONFIG_PVDATA_VERSION
|
||||
|
||||
TARGETS = $(CONFIG_TARGETS)
|
||||
CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS)))
|
||||
|
||||
|
||||
@@ -1,38 +1,25 @@
|
||||
# RELEASE - Location of external support modules
|
||||
# pvDataCPP RELEASE - Location of external support modules
|
||||
#
|
||||
# IF YOU CHANGE ANY PATHS in this file or make API changes to
|
||||
# any modules it refers to, you should do a "make rebuild" in
|
||||
# this application's top level directory.
|
||||
# IF YOU CHANGE this file or any file it includes you must
|
||||
# subsequently do a "gnumake rebuild" in the application's
|
||||
# top level directory.
|
||||
#
|
||||
# The EPICS build process does not check dependencies against
|
||||
# any files from outside the application, so it is safest to
|
||||
# rebuild it completely if any modules it depends on change.
|
||||
# The build process does not check dependencies against files
|
||||
# that are outside this application, thus you should also do a
|
||||
# "gnumake rebuild" in the top level directory after EPICS_BASE
|
||||
# or any other external module pointed to below is rebuilt.
|
||||
#
|
||||
# Host- or target-specific settings can be given in files named
|
||||
# RELEASE.$(EPICS_HOST_ARCH).Common
|
||||
# RELEASE.Common.$(T_A)
|
||||
# RELEASE.$(EPICS_HOST_ARCH).$(T_A)
|
||||
|
||||
# EPICS V4 Developers: Do not edit the locations in this file!
|
||||
#
|
||||
# This file is parsed by both GNUmake and an EPICS Perl script,
|
||||
# so it may ONLY contain definititions of paths to other support
|
||||
# modules, variable definitions that are used in module paths,
|
||||
# and include statements that pull in other RELEASE files.
|
||||
# Variables may be used before their values have been set.
|
||||
# Build variables that are NOT used in paths should be set in
|
||||
# the CONFIG_SITE file.
|
||||
# Create a file RELEASE.local pointing to your PVCOMMON
|
||||
# and EPICS_BASE build directories, e.g.
|
||||
# PVCOMMON = /home/install/epicsV4/pvCommonCPP
|
||||
# EPICS_BASE = /home/install/epics/base
|
||||
|
||||
# Variables and paths to dependent modules:
|
||||
#MODULES = /path/to/modules
|
||||
#MYMODULE = $(MODULES)/my-module
|
||||
|
||||
# If building the EPICS modules individually, set these:
|
||||
#EPICS_BASE = /path/to/base
|
||||
|
||||
# Set RULES here if you want to use build rules from elsewhere:
|
||||
#RULES = $(MODULES)/build-rules
|
||||
|
||||
# These allow developers to override the RELEASE variable settings
|
||||
# without having to modify the configure/RELEASE file itself.
|
||||
-include $(TOP)/../RELEASE.local
|
||||
-include $(TOP)/../RELEASE.$(EPICS_HOST_ARCH).local
|
||||
-include $(TOP)/configure/RELEASE.local
|
||||
|
||||
3
documentation/.gitignore
vendored
3
documentation/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
*.tag
|
||||
*.db
|
||||
html/
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +0,0 @@
|
||||
all: gen
|
||||
|
||||
clean:
|
||||
rm -rf doxygen_sqlite3.db html
|
||||
|
||||
gen: libstdc++.tag
|
||||
doxygen
|
||||
|
||||
commit: gen
|
||||
touch html/.nojekyll
|
||||
./commit-gh.sh documentation/html/ html/.nojekyll html/*.* html/search/*.*
|
||||
|
||||
libstdc++.tag:
|
||||
wget -O $@ https://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/libstdc++.tag
|
||||
|
||||
.PHONY: all clean gen commit
|
||||
315
documentation/RELEASE_NOTES.html
Normal file
315
documentation/RELEASE_NOTES.html
Normal file
@@ -0,0 +1,315 @@
|
||||
<h1>Release 6.0.0</h1>
|
||||
|
||||
<p>The main changes since release 5.0.4 are:</p>
|
||||
|
||||
<ul>
|
||||
<li>Linux shared library version added</li>
|
||||
<li>Headers have been moved into pv directories</li>
|
||||
<li>Bitset functions declared const where possible</li>
|
||||
<li>Bitset::swap added</li>
|
||||
<li>Requester::message has default implementation</li>
|
||||
<li>Serialization/deserialization helpers added</li>
|
||||
<li>Non-template getSubField char* overload added</li>
|
||||
<li>MonitorPlugin deprecated</li>
|
||||
<li>Field name validation performed</li>
|
||||
<li>Now builds for Cygwin and MinGW targets</li>
|
||||
<li>Fix for debug build issue.</li>
|
||||
<li>New license file replaces LICENSE and COPYRIGHT</li>
|
||||
</ul>
|
||||
|
||||
<h2>Shared library version added</h2>
|
||||
|
||||
<p>Linux shared library version numbers have been added by setting SHRLIB_VERSION
|
||||
(to 6.0 in this case). So shared object will be libpvData.so.6.0 instead of
|
||||
libpvData.so.</p>
|
||||
|
||||
<h2>Headers have been moved into pv directories</h2>
|
||||
|
||||
<p>E.g. src/property/alarm.h -> src/property/pv/alarm.h</p>
|
||||
|
||||
<p>This facilitates using some IDEs such as Qt Creator.</p>
|
||||
|
||||
<h2>Requester::message has default implementation</h2>
|
||||
|
||||
<p>Requester::message is no longer pure virtual. Default implementation sends
|
||||
string to std::cerr.</p>
|
||||
|
||||
<h2>Serialization/deserialization helpers added</h2>
|
||||
|
||||
<p>A helper function, serializeToVector, has been added which serializes a
|
||||
Serializable object into a standard vector of UInt8s.</p>
|
||||
|
||||
<p>Similarly a function deserializeFromVector deserializes a standard vector into
|
||||
a Deserializable object.</p>
|
||||
|
||||
<p>A function deserializeFromBuffer deserializes a ByteBuffer into a
|
||||
Deserializable object.</p>
|
||||
|
||||
<h2>Field name validation performed</h2>
|
||||
|
||||
<p>On creating a Structure or Union the field names are now validated.</p>
|
||||
|
||||
<p>Valid characters for a field name are upper or lowercase letters, numbers and
|
||||
underscores and intial numbers are invalid, i.e. names must be of the form
|
||||
[A-Za-z<em>][A-Za-z0-9</em>]*.</p>
|
||||
|
||||
<h2>Now builds for Cygwin and MinGW targets</h2>
|
||||
|
||||
<p>Includes cross-compiling MinGW on Linux.</p>
|
||||
|
||||
<h1>Release 5.0.4</h1>
|
||||
|
||||
<p>The changes since release 5.0.3 are:</p>
|
||||
|
||||
<ul>
|
||||
<li>Fixed bitset serialization (issue #24)</li>
|
||||
<li>Fixed truncation in BitSet::or_and (issue #27)</li>
|
||||
</ul>
|
||||
|
||||
<h2>Fixed bitset serialization (issue #24)</h2>
|
||||
|
||||
<p>C++ bitset serialization was not consistent with the C++ deserialization and
|
||||
Java code in some instances (depending on the endianness of the serializer and
|
||||
deserializer) when the number of bits was 56-63 modulo 64. C++ serialization
|
||||
has been fixed.</p>
|
||||
|
||||
<p>Fix exposed issue in deserialization on 32-bit platforms which
|
||||
has also been corrected. </p>
|
||||
|
||||
<h2>Fixed truncation in BitSet::or_and (issue #27)</h2>
|
||||
|
||||
<p>If n, n1 and n2 words are used to store the values of the bitsets bitset,
|
||||
bitset1 and bitset2 respectively then max(n, min(n1,n2)) words are needed
|
||||
to store bitset.or_(bitset1, bitset2).</p>
|
||||
|
||||
<p>Previously min(n1,n2) words were used and the result would be truncated in
|
||||
some instances. This has been fixed.</p>
|
||||
|
||||
<h1>Release 5.0.3</h1>
|
||||
|
||||
<p>The only change since release 5.0.2 is:</p>
|
||||
|
||||
<h2>Fixed buffer overflow in PVUnion::serialize() (issue #20)</h2>
|
||||
|
||||
<p>A PVUnion whose stored value was null was serialized without checking
|
||||
whether the buffer had sufficient capacity. This has been fixed by calling
|
||||
ensureBuffer().</p>
|
||||
|
||||
<h1>Release 5.0.2</h1>
|
||||
|
||||
<p>The main changes since release 4.0.3 are:</p>
|
||||
|
||||
<ul>
|
||||
<li>Deprecated getXXXField() methods have been removed from PVStructure</li>
|
||||
<li>Convert copy methods and equals operators (re)moved</li>
|
||||
<li>Convert::copyUnion now always copies between subfields.</li>
|
||||
<li>New method getSubFieldT, like getSubField except it throws an exception</li>
|
||||
<li>findSubField method removed from PVStructure</li>
|
||||
<li>New stream operators for Field and PVField are provided</li>
|
||||
<li>New template versions of Structure::getField</li>
|
||||
<li>Fixes for static initialisation order issues</li>
|
||||
<li>CreateRequest prevents a possible SEGFAULT</li>
|
||||
</ul>
|
||||
|
||||
<h2>Deprecated getXXXField methods have been removed from PVStructure</h2>
|
||||
|
||||
<p>The following methods have been removed from PVStructure</p>
|
||||
|
||||
<ul>
|
||||
<li>getBooleanField</li>
|
||||
<li>getByteField, getShortField, getIntField, getLongField</li>
|
||||
<li>getUByteField, getUShortField, getUIntField, getULongField</li>
|
||||
<li>getStringField</li>
|
||||
<li>getStructureField, getUnionField</li>
|
||||
<li>getScalarArrayField, getStructureArrayField, getUnionArrayField</li>
|
||||
</ul>
|
||||
|
||||
<p>Use template getSubField instead, e.g. use</p>
|
||||
|
||||
<pre><code>getSubField< PVInt >(fieldName)
|
||||
</code></pre>
|
||||
|
||||
<p>in place of</p>
|
||||
|
||||
<pre><code>getIntField(fieldName)
|
||||
</code></pre>
|
||||
|
||||
<h2>Convert copy methods and equals operators</h2>
|
||||
|
||||
<p>Convert copy methods where moved and replaced with methods
|
||||
on PVField classes, i.e.</p>
|
||||
|
||||
<pre><code>PVField::copy(const PVField& from)
|
||||
</code></pre>
|
||||
|
||||
<p>Methods</p>
|
||||
|
||||
<pre><code>PVField::copyUnchecked(const PVField& from)
|
||||
</code></pre>
|
||||
|
||||
<p>were added to allow unchecked copies, to gain performance
|
||||
where checked are not needed (anymore).</p>
|
||||
|
||||
<p>In addition:
|
||||
- isCompatibleXXX methods were removed in favour of Field::operator==.
|
||||
- equals methods were remove in favour of PVField::operator==.
|
||||
- operator== methods where moved to pvIntrospect.h and pvData.h</p>
|
||||
|
||||
<h2>Convert::copyUnion</h2>
|
||||
|
||||
<p>Before this method, depending on types for to and from,
|
||||
sometimes did a shallow copy, i.e. just made to shared_ptr for to
|
||||
share the same data as from.
|
||||
Now it always copies between the subfield of to and from.</p>
|
||||
|
||||
<h2>New method getSubFieldT, like getSubField except it throws an exception</h2>
|
||||
|
||||
<p>PVStructure has a new template member</p>
|
||||
|
||||
<pre><code>getSubFieldT(std::string const &fieldName)
|
||||
</code></pre>
|
||||
|
||||
<p>that is like <b>getSubField</b> except that it throws a runtime_error
|
||||
instead of returning null.</p>
|
||||
|
||||
<h2>findSubField method removed from PVStructure</h2>
|
||||
|
||||
<p>This was mainly used in the implementation of getSubField. With a change to
|
||||
the latter, findSubField was removed.</p>
|
||||
|
||||
<h2>New stream operators</h2>
|
||||
|
||||
<p>New steam operators are available for Field and PVField.
|
||||
Before to print a Field (or any extension) or a PVField (or any extension)
|
||||
it was necessary to have code like:</p>
|
||||
|
||||
<pre><code> void print(StructureConstPtr struc, PVStructurePtr pv)
|
||||
{
|
||||
if(struc) {
|
||||
cout << *struc << endl;
|
||||
} else {
|
||||
cout << "nullptr\n"
|
||||
}
|
||||
if(pv) {
|
||||
cout << *.struc << endl;
|
||||
} else {
|
||||
cout << "nullptr\n"
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
<p>Now it can be done as follows:</p>
|
||||
|
||||
<pre><code> void print(StructureConstPtr struc, PVStructurePtr pv)
|
||||
{
|
||||
cout << struc << endl;
|
||||
cout << pv << endl;
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
<h2>New template version of Structure::getField</h2>
|
||||
|
||||
<p>A new template getField method has been added to Structure</p>
|
||||
|
||||
<p>template<typename FT >
|
||||
std::tr1::shared_ptr< const FT > getField(std::string const &fieldName) const </p>
|
||||
|
||||
<p>Can be used, for example, as follows:</p>
|
||||
|
||||
<pre><code>StructurePtr tsStruc = struc->getField<Structure>("timeStamp");
|
||||
</code></pre>
|
||||
|
||||
<h2>Fixes for static initialisation order issues</h2>
|
||||
|
||||
<p>Certain static builds (in particular Windows builds) of applications using
|
||||
pvData had issues due to PVStructure::DEFAULT_ID being used before being initialised. This has been fixed.</p>
|
||||
|
||||
<h2>CreateRequest change</h2>
|
||||
|
||||
<p>createRequest could cause a SEGFAULT if passed a bad argument.
|
||||
This has been changed so the it returns a null pvStructure
|
||||
and provides an error.</p>
|
||||
|
||||
<h1>Release 4.0.3</h1>
|
||||
|
||||
<p>The main changes since release 3.0.2 are:</p>
|
||||
|
||||
<ul>
|
||||
<li>array semantics now enforce Copy On Write.</li>
|
||||
<li>String no longer defined.</li>
|
||||
<li>timeStamp and valueAlarm name changes</li>
|
||||
<li>toString replaced by stream I/O </li>
|
||||
<li>union is new type.</li>
|
||||
<li>copy is new.</li>
|
||||
<li>monitorPlugin is new.</li>
|
||||
</ul>
|
||||
|
||||
<h2>New Semantics for Arrays</h2>
|
||||
|
||||
<p>PVScalarArray, PVStructureArray, and PVUnionArray all enforce COW (Copy On Write) Semantics.
|
||||
In order to limit memory usage the storage for raw data is managed via a new shared<em>vector facility.
|
||||
This allows multiple instances of array data to use the shared raw data.
|
||||
COW is implemented via shared</em>vectors of const data, i. e. data that can not be modified.</p>
|
||||
|
||||
<h2>String no longer defined</h2>
|
||||
|
||||
<p>This is replaced by std::string.</p>
|
||||
|
||||
<h2>timeStamp and valueAlarm name changes</h2>
|
||||
|
||||
<p>In timeStamp nanoSeconds is changed to nanoseconds.</p>
|
||||
|
||||
<p>In valueAlarm hysteresis is changed to hysteresis</p>
|
||||
|
||||
<h2>toString replaced by stream I/O</h2>
|
||||
|
||||
<p>pvData.h and pvIntrospect no longer defines toString
|
||||
Instead they have stream support.
|
||||
pvIntrospect uses method dump and pvData uses dumpValue.
|
||||
For example:</p>
|
||||
|
||||
<pre><code> PVDoublePtr pvValue;
|
||||
String buffer;
|
||||
pvValue->toString(&buffer);
|
||||
cout << buffer << endl;
|
||||
buffer.clear();
|
||||
pvValue->getField()->toString(&buffer);
|
||||
cout << buffer << evdl;
|
||||
</code></pre>
|
||||
|
||||
<p>is replaced by</p>
|
||||
|
||||
<pre><code> PVDoublePtr pvValue;
|
||||
cout << *pvValue << endl
|
||||
cout << *pvValue->getField() << endl;
|
||||
</code></pre>
|
||||
|
||||
<h2>union is a new basic type.</h2>
|
||||
|
||||
<p>There are two new basic types: union_t and unionArray.</p>
|
||||
|
||||
<p>A union is like a structure that has a single subfield.
|
||||
There are two flavors:</p>
|
||||
|
||||
<ul>
|
||||
<li><b>variant union</b> The field can have any type.</li>
|
||||
<li><b>union</b> The field can any of specified set of types.</li>
|
||||
</ul>
|
||||
|
||||
<p>The field type can be dynamically changed.</p>
|
||||
|
||||
<h2>copy </h2>
|
||||
|
||||
<p>This consists of createRequest and pvCopy.
|
||||
createRequest was moved from pvAccess to here.
|
||||
pvCopy is moved from pvDatabaseCPP and now depends
|
||||
only on pvData, i.e. it no longer has any knowledge of PVRecord.</p>
|
||||
|
||||
<h2>monitorPlugin</h2>
|
||||
|
||||
<p>This is for is for use by code that implements pvAccess monitors.
|
||||
This is prototype and is subject to debate.</p>
|
||||
|
||||
<h1>Release 3.0.2</h1>
|
||||
|
||||
<p>This was the starting point for RELEASE_NOTES</p>
|
||||
@@ -1,103 +1,6 @@
|
||||
/**
|
||||
|
||||
@page release_notes Release Notes
|
||||
|
||||
Release 8.0.0 (July 2019)
|
||||
=========================
|
||||
|
||||
- Deprecations
|
||||
- ByteBuffer::getArray()
|
||||
- Removals
|
||||
- pv/localStaticLock.h
|
||||
- pv/pvCopy.h (see epics::pvData::PVRequestMapper)
|
||||
- Additions
|
||||
- Add {Structure,Union}::getFieldT
|
||||
|
||||
Release 7.1.3 (Apr 2019)
|
||||
========================
|
||||
|
||||
- Fix for array serialization error to/from big endian.
|
||||
https://github.com/epics-base/pvDataCPP/issues/65
|
||||
|
||||
Release 7.1.2 (Mar 2019)
|
||||
========================
|
||||
|
||||
- 7.1.1 tag pushed prematurely.
|
||||
|
||||
Release 7.1.1 (Mar 2019)
|
||||
========================
|
||||
|
||||
- Fixes
|
||||
- Init order issue with StandardField::getStandardField()
|
||||
- Build fix for Visual Studio 2013+
|
||||
|
||||
Release 7.1.0 (Nov 2018)
|
||||
========================
|
||||
|
||||
- Deprecations
|
||||
- BoundedString, BoundedScalarArray, and FixedScalarArray will be removed unless they are fixed.
|
||||
See https://github.com/epics-base/pvDataCPP/issues/52 for discussion.
|
||||
- pv/localStaticLock.h
|
||||
- pv/pvCopy.h (see epics::pvData::PVRequestMapper)
|
||||
- Removals
|
||||
- Remove previously deprecated executor.h, queue.h and timerFunction.h
|
||||
- Remove *HashFunction functors to "hash" Field sub-classes which were never fully implemented.
|
||||
- Fixes
|
||||
- Make thread safe getFieldCreate() and getPVDataCreate()
|
||||
- Workaround for MSVC pickyness that iterators be non-NULL, even when not de-referenced.
|
||||
- Fix alignment fault during (de)serialization on RTEMS/vxWorks.
|
||||
- Fix epics::pvData::shared_vector::swap() for void specialization.
|
||||
- Changes in several epics::pvData::Field sub-classes to return const ref. instead of a copy.
|
||||
- Additions
|
||||
- epics::pvData::shared_vector add c++11 move and construct for initializer list.
|
||||
- Add epics::pvData::AnyScalar::clear()
|
||||
- Add ctor epics::pvData::AnyScalar(ScalarType, const void*) to allow construction from an untyped buffer.
|
||||
- Add epics::pvData::Timer::close()
|
||||
- Allow epics::pvData::castUnsafe() from const char* without first allocating a std::string.
|
||||
- De-duplication of epics::pvData::Field instances is performed using a global hash table.
|
||||
Identical definitions will share a single instance. Allows O(0) comparision.
|
||||
- Add epics::pvData::PVRequestMapper to facilitate (partial) copying between PVStructure instances
|
||||
modified by a pvRequest.
|
||||
- Add shorthand notations epics::pvData::FieldBuilder::begin() and epics::pvData::Field::build()
|
||||
|
||||
Release 7.0.0 (Dec 2017)
|
||||
========================
|
||||
|
||||
- Removals
|
||||
- Remove requester.h, monitor.h, and destroyable.h.. Migrated to the pvAccessCPP module.
|
||||
- Previously deprecated monitorPlugin.h is removed.
|
||||
- Remove pv/messageQueue.h and epics::pvData::MessageQueue
|
||||
- Deprecate the following utility classes, to be removed in 8.0.
|
||||
- epics::pvData::Queue
|
||||
- epics::pvData::Executor
|
||||
- epics::pvData::TimeFunction
|
||||
- Additions
|
||||
- Add pv/pvdVersion.h which is included by pv/pvIntrospect.h
|
||||
- Add epics::pvData::createRequest() function. Alternative to epics::pvData::CreateRequest class which throws on error.
|
||||
- epics::pvData::FieldBuilder allow Structure defintion to be changed/appended
|
||||
- Add epics::pvData::ValueBuilder like FieldBuilder also sets initial values.
|
||||
- Can also be constructed using an existing PVStructure to allow "editing".
|
||||
- Add debugPtr.h wrapper with reference tracking to assist in troubleshooting shared_ptr related ref. loops.
|
||||
- Add @ref pvjson utilities
|
||||
- Add reftrack @ref pvd_reftrack
|
||||
- Add header typemap.h to facilitate boilerplate switch() over ScalarType
|
||||
- Add epics::auto_ptr typedef in help writing code supporting both c++98 and c++11 w/o copious deprecation warnings.
|
||||
|
||||
|
||||
Release 6.0.1
|
||||
Release 6.0.0
|
||||
=============
|
||||
|
||||
The changes since release 6.0.0 are:
|
||||
|
||||
* Fix "Problem building pvDataCPP for win32-x86-mingw" (issue #42)
|
||||
* In src/misc/bitSet.cpp #include "algorithm" required for MSVS 2015
|
||||
* In testApp/misc/testTypeCast.cpp print (u)int8 values as integers
|
||||
* Minor documentation updates
|
||||
|
||||
|
||||
Release 6.0.0 (Aug. 2016)
|
||||
=======================
|
||||
|
||||
The main changes since release 5.0.4 are:
|
||||
|
||||
* Linux shared library version added
|
||||
@@ -136,13 +39,13 @@ string to std::cerr.
|
||||
Serialization/deserialization helpers added
|
||||
-------------------------------------------
|
||||
|
||||
A helper function, serializeToVector, has been added which serializes a
|
||||
A helper function, serializeToVector, has been added which serializes a
|
||||
Serializable object into a standard vector of UInt8s.
|
||||
|
||||
Similarly a function deserializeFromVector deserializes a standard vector into
|
||||
a Deserializable object.
|
||||
|
||||
A function deserializeFromBuffer deserializes a ByteBuffer into a
|
||||
A function deserializeFromBuffer deserializes a ByteBuffer into a
|
||||
Deserializable object.
|
||||
|
||||
Field name validation performed
|
||||
@@ -177,7 +80,7 @@ deserializer) when the number of bits was 56-63 modulo 64. C++ serialization
|
||||
has been fixed.
|
||||
|
||||
Fix exposed issue in deserialization on 32-bit platforms which
|
||||
has also been corrected.
|
||||
has also been corrected.
|
||||
|
||||
Fixed truncation in BitSet::or_and (issue #27)
|
||||
----------------------------------------------
|
||||
@@ -198,38 +101,38 @@ The only change since release 5.0.2 is:
|
||||
Fixed buffer overflow in PVUnion::serialize() (issue #20)
|
||||
---------------------------------------------------------
|
||||
|
||||
A PVUnion whose stored value was null was serialized without checking
|
||||
A PVUnion whose stored value was null was serialized without checking
|
||||
whether the buffer had sufficient capacity. This has been fixed by calling
|
||||
ensureBuffer().
|
||||
|
||||
|
||||
Release 5.0.2 (Sep. 2015)
|
||||
=========================
|
||||
Release 5.0.2
|
||||
=============
|
||||
|
||||
The main changes since release 4.0 are:
|
||||
The main changes since release 4.0.3 are:
|
||||
|
||||
- Deprecated getXXXField() methods have been removed from PVStructure
|
||||
- Convert copy methods and equals operators (re)moved
|
||||
- Convert::copyUnion now always copies between subfields.
|
||||
- New method getSubFieldT, like getSubField except it throws an exception
|
||||
- findSubField method removed from PVStructure
|
||||
- New stream operators for Field and PVField are provided
|
||||
- New template versions of Structure::getField
|
||||
- Fixes for static initialisation order issues
|
||||
- CreateRequest prevents a possible SEGFAULT
|
||||
* Deprecated getXXXField() methods have been removed from PVStructure
|
||||
* Convert copy methods and equals operators (re)moved
|
||||
* Convert::copyUnion now always copies between subfields.
|
||||
* New method getSubFieldT, like getSubField except it throws an exception
|
||||
* findSubField method removed from PVStructure
|
||||
* New stream operators for Field and PVField are provided
|
||||
* New template versions of Structure::getField
|
||||
* Fixes for static initialisation order issues
|
||||
* CreateRequest prevents a possible SEGFAULT
|
||||
|
||||
|
||||
Deprecated getXXXField methods have been removed from PVStructure
|
||||
-------------------------------------------------------------------
|
||||
-----------------------------------------------------------------
|
||||
|
||||
The following methods have been removed from PVStructure
|
||||
|
||||
- getBooleanField
|
||||
- getByteField, getShortField, getIntField, getLongField
|
||||
- getUByteField, getUShortField, getUIntField, getULongField
|
||||
- getStringField
|
||||
- getStructureField, getUnionField
|
||||
- getScalarArrayField, getStructureArrayField, getUnionArrayField
|
||||
* getBooleanField
|
||||
* getByteField, getShortField, getIntField, getLongField
|
||||
* getUByteField, getUShortField, getUIntField, getULongField
|
||||
* getStringField
|
||||
* getStructureField, getUnionField
|
||||
* getScalarArrayField, getStructureArrayField, getUnionArrayField
|
||||
|
||||
Use template getSubField instead, e.g. use
|
||||
|
||||
@@ -277,7 +180,7 @@ PVStructure has a new template member
|
||||
|
||||
getSubFieldT(std::string const &fieldName)
|
||||
|
||||
that is like `getSubField()` except that it throws a runtime_error
|
||||
that is like <b>getSubField</b> except that it throws a runtime_error
|
||||
instead of returning null.
|
||||
|
||||
|
||||
@@ -323,8 +226,8 @@ New template version of Structure::getField
|
||||
|
||||
A new template getField method has been added to Structure
|
||||
|
||||
template<typename FT >
|
||||
std::tr1::shared_ptr< const FT > getField(std::string const &fieldName) const
|
||||
template<typename FT >
|
||||
std::tr1::shared_ptr< const FT > getField(std::string const &fieldName) const
|
||||
|
||||
Can be used, for example, as follows:
|
||||
|
||||
@@ -351,13 +254,13 @@ Release 4.0.3
|
||||
|
||||
The main changes since release 3.0.2 are:
|
||||
|
||||
- array semantics now enforce Copy On Write.
|
||||
- String no longer defined.
|
||||
- timeStamp and valueAlarm name changes
|
||||
- toString replaced by stream I/O
|
||||
- union is new type.
|
||||
- copy is new.
|
||||
- monitorPlugin is new.
|
||||
* array semantics now enforce Copy On Write.
|
||||
* String no longer defined.
|
||||
* timeStamp and valueAlarm name changes
|
||||
* toString replaced by stream I/O
|
||||
* union is new type.
|
||||
* copy is new.
|
||||
* monitorPlugin is new.
|
||||
|
||||
New Semantics for Arrays
|
||||
--------
|
||||
@@ -413,8 +316,8 @@ There are two new basic types: union_t and unionArray.
|
||||
A union is like a structure that has a single subfield.
|
||||
There are two flavors:
|
||||
|
||||
- *variant union* The field can have any type.
|
||||
- *union* The field can any of specified set of types.
|
||||
* <b>variant union</b> The field can have any type.
|
||||
* <b>union</b> The field can any of specified set of types.
|
||||
|
||||
The field type can be dynamically changed.
|
||||
|
||||
@@ -435,5 +338,3 @@ This is prototype and is subject to debate.
|
||||
Release 3.0.2
|
||||
==========
|
||||
This was the starting point for RELEASE_NOTES
|
||||
|
||||
*/
|
||||
@@ -12,3 +12,9 @@ valueAlarm
|
||||
normativeTypes.html describes valueAlarm only for a value field that has type
|
||||
double.
|
||||
The implementation also supports all the numeric scalar types.
|
||||
|
||||
monitorPlugin
|
||||
-------------
|
||||
|
||||
A debate is on-going about what semantics should be.
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/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"
|
||||
679
documentation/copyandmonitor.html
Normal file
679
documentation/copyandmonitor.html
Normal file
@@ -0,0 +1,679 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
|
||||
<title>EPICS pvDataCPP: copy and monitor</title>
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="http://epics-pvdata.sourceforge.net/base.css" />
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="http://epics-pvdata.sourceforge.net/epicsv4.css" />
|
||||
<style type="text/css">
|
||||
/*<![CDATA[*/
|
||||
.about { margin-left: 3em; margin-right: 3em; font-size: .83em}
|
||||
table { margin-left: auto; margin-right: auto }
|
||||
.diagram { text-align: center; margin: 2.5em 0 }
|
||||
span.opt { color: grey }
|
||||
span.nterm { font-style:italic }
|
||||
span.term { font-family:courier }
|
||||
span.user { font-family:courier }
|
||||
span.user:before { content:"<" }
|
||||
span.user:after { content:">" }
|
||||
.nonnorm { font-style:italic }
|
||||
p.ed { color: #AA0000 }
|
||||
span.ed { color: #AA0000 }
|
||||
p.ed.priv { display: inline; }
|
||||
span.ed.priv { display: inline; }
|
||||
/*]]>*/</style>
|
||||
<!-- Script that generates the Table of Contents -->
|
||||
<script type="text/javascript"
|
||||
src="http://epics-pvdata.sourceforge.net/script/tocgen.js">
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<div id="toc">
|
||||
<h2 class="nocount" style="page-break-before: always">Table of Contents</h2>
|
||||
</div>
|
||||
<div id="contents" class="contents">
|
||||
|
||||
<h2>support for copy and monitor</h2>
|
||||
<p><b>copy</b> and <b>monitor</b> are not used in this project.
|
||||
They are intended for use by pvAccess and by pvAccess servers.
|
||||
They are provided with this project because the code depends only on
|
||||
pvData itself.
|
||||
</p>
|
||||
<p>This document describes C++ specific code.
|
||||
<a href="http://epics-pvdata.sourceforge.net/informative/pvRequest.html">
|
||||
pvRequest.html</a>
|
||||
provides a language independent overview of <b>copy</b> and <b>monitor</b>.
|
||||
</p>
|
||||
<p>
|
||||
<b>NOTE:pvRequest.html</b> must be updated since it is based on an earlier version of pvCopy that
|
||||
had knowledge of PVRecord. The C++ version was implemented in pvDatabaseCPP
|
||||
and the Java version on pvIOCJava.
|
||||
At present only the C++ version of the new API for pvCopy is implemented.
|
||||
</p>
|
||||
<p>Copy provides:
|
||||
<dl>
|
||||
<dt>createRequest</dt>
|
||||
<dd>
|
||||
The Channel create methods in pvAccess all have an argument
|
||||
<b>PVStructure pvRequest</b>.<br />
|
||||
Given an ascii string createRequest creates a PVStructure that provides
|
||||
a pvData representation of the information from the ascii string.
|
||||
It is this structure that can be passed to the channel create methods.<br />
|
||||
The information in a pvRequest selects an arbitrary subset of the
|
||||
fields in a top level structure that resides in the server.
|
||||
In addition options can be specified. Both global and field specific
|
||||
options can be specified.
|
||||
</dd>
|
||||
<dt>pvCopy</dt>
|
||||
<dd>This is a facility used by channel providers.
|
||||
It provides client specific code that manages a copy of an arbitrary
|
||||
subset of the fields in a top level structure that resides in the
|
||||
provider. It also allows provider access to options specified
|
||||
by the client.
|
||||
</dd>
|
||||
</dl>
|
||||
Monitor provides:
|
||||
<dl>
|
||||
<dt>monitor</dt>
|
||||
<dd>This is support code for channel providers that implement channel
|
||||
monitor. It, together with the queue facility, provides support for
|
||||
monitor queues.
|
||||
</dd>
|
||||
<dt>monitorPlugin</dt>
|
||||
<dd>This is support for implementing monitor plugins.
|
||||
A monitor plugin can be developed that has no knowledge
|
||||
of pvAccess but only pvData.
|
||||
</dd>
|
||||
</dl>
|
||||
</p>
|
||||
|
||||
<h2>support for copy</h2>
|
||||
<p><b>copy</b> provides the ability to create a structure that has
|
||||
a copy of an arbitrary subset of the fields in an existing top level
|
||||
structure. In addition it allows global options and field specific options.
|
||||
It has two main components: <b>createRequest</b> and <b>pvCopy</b>.
|
||||
Given a string createRequest creates a pvRequest, which is a PVStructure
|
||||
that has the format expected by <b>pvCopy</b>.
|
||||
</p>
|
||||
|
||||
<h3>createRequest</h3>
|
||||
<p>This is mainly used by pvAccess clients. Given a request string it creates
|
||||
a pvRequest structure that can be passed to the pvAccess create methods.
|
||||
In turn pvAccess passes the pvRequest to a local channel provider which
|
||||
then passes it to pvCopy.
|
||||
</p>
|
||||
<p>The definition of the public members is:</p>
|
||||
<pre>
|
||||
class CreateRequest {
|
||||
...
|
||||
static CreateRequestPtr create();
|
||||
virtual PVStructurePtr createRequest(std::string const &request);
|
||||
std::string getMessage();
|
||||
};
|
||||
</pre>
|
||||
<p>An example of how it is used is:</p>
|
||||
<pre>
|
||||
CreateRequestPtr createRequest = CreateRequest::create();
|
||||
PVStructurePtr pvRequest = createRequest->createRequest(request);
|
||||
if(pvRequest==NULL) {
|
||||
std::string error = createRequest->getMessage();
|
||||
// take some action
|
||||
} else {
|
||||
//success do something
|
||||
}
|
||||
</pre>
|
||||
<h3>pvCopy</h3>
|
||||
<p>The definition of the public members is:</p>
|
||||
<pre>
|
||||
class epicsShareClass PVCopyTraverseMasterCallback
|
||||
{
|
||||
...
|
||||
virtual void nextMasterPVField(PVFieldPtr const &pvField);
|
||||
};
|
||||
|
||||
class class epicsShareClass PVCopy
|
||||
{
|
||||
...
|
||||
static PVCopyPtr create(
|
||||
PVStructurePtr const &pvMaster,
|
||||
PVStructurePtr const &pvRequest,
|
||||
std::string const & structureName);
|
||||
PVStructurePtr getPVMaster();
|
||||
void traverseMaster(PVCopyTraverseMasterCallbackPtr const & callback);
|
||||
StructureConstPtr getStructure();
|
||||
PVStructurePtr createPVStructure();
|
||||
size_t getCopyOffset(PVFieldPtr const &masterPVField);
|
||||
size_t getCopyOffset(
|
||||
PVStructurePtr const &masterPVStructure,
|
||||
PVFieldPtr const &masterPVField);
|
||||
PVFieldPtr getMasterPVField(std::size_t structureOffset);
|
||||
void initCopy(
|
||||
PVStructurePtr const &copyPVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
void updateCopySetBitSet(
|
||||
PVStructurePtr const &copyPVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
void updateCopyFromBitSet(
|
||||
PVStructurePtr const &copyPVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
void updateMaster(
|
||||
PVStructurePtr const &copyPVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
PVStructurePtr getOptions(std::size_t fieldOffset);
|
||||
...
|
||||
};
|
||||
</pre>
|
||||
where
|
||||
<dl>
|
||||
<dt>PVCopyTraverseMasterCallback::nextMasterPVField</dt>
|
||||
<dd>
|
||||
<b>PVCopyTraverseMasterCallback</b> is a callback which must
|
||||
be implemented by the code that uses pvCopy, normally
|
||||
the channel provider. It has the single method <b>nextMasterPVField</b>
|
||||
<br />
|
||||
<b>nextMasterPVField</b> is called for each field in the master
|
||||
as a result of a call to <b>traverseMaster</b>.
|
||||
</dd>
|
||||
<dt>create</dt>
|
||||
<dd>
|
||||
This is the method for creating a PVCopy instance.<br/>
|
||||
<dl>
|
||||
<dt>pvMaster</dt>
|
||||
<dd>the top level structure managed by the server.</dd>
|
||||
<dt>pvRequest</dt>
|
||||
<dd>selects the set of subfields desired
|
||||
and options for each field.</dd>
|
||||
<dt>structureName</dt>
|
||||
<dd>the name for the top level of any PVStructure created.
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt>getPVMaster</dt>
|
||||
<dd>
|
||||
Gets the top level structure from pvMaster.
|
||||
</dd>
|
||||
<dt>traverseMaster</dt>
|
||||
<dd>
|
||||
Traverse all fields of the top level structure of pvMaster.
|
||||
For each field the callback is called.
|
||||
</dd>
|
||||
<dt>getStructure</dt>
|
||||
<dd>
|
||||
Get the introspection interface for a PVStructure for e copy.
|
||||
</dd>
|
||||
<dt>createPVStructure</dt>
|
||||
<dd>Create a copy instance.
|
||||
Monitors keep a queue of monitor elements.
|
||||
Since each element needs a PVStructure, multiple top level structures
|
||||
will be created.
|
||||
</dd>
|
||||
<dt>getCopyOffset</dt>
|
||||
<dd>Given a field in pvMaster.
|
||||
return the offset in copy for the same field.
|
||||
A value of std::string::npos means that the copy does not have this field.
|
||||
Two overloaded methods are provided. The first is called if
|
||||
the field of master is not a structure. The second is for
|
||||
subfields of a structure.
|
||||
</dd>
|
||||
<dt>getMasterPVField</dt>
|
||||
<dd>
|
||||
Given a offset in the copy get the corresponding field in pvMaster.
|
||||
</dd>
|
||||
<dt>initCopy</dt>
|
||||
<dd>
|
||||
Initialize the fields in copyPVStructure
|
||||
by giving each field the value from the corresponding field in pvMaster.
|
||||
bitSet will be set to show that all fields are changed.
|
||||
This means that bit set will have the value <b>{0}</b>.
|
||||
</dd>
|
||||
<dt>updateCopySetBitSet</dt>
|
||||
<dd>
|
||||
Set all fields in copyPVStructure to the value of the corresponding field
|
||||
in pvMaster. Each field that is changed has it's corresponding
|
||||
bit set in bitSet.
|
||||
</dd>
|
||||
<dt>updateCopyFromBitSet</dt>
|
||||
<dd>
|
||||
For each set bit in bitSet set the field in copyPVStructure to the value
|
||||
of the corresponding field in pvMaster.
|
||||
</dd>
|
||||
<dt>updateMaster</dt>
|
||||
<dd>
|
||||
For each set bit in bitSet set the field in pvMaster to the value
|
||||
of the corresponding field in copyPVStructure.
|
||||
|
||||
</dd>
|
||||
<dt>getOptions</dt>
|
||||
<dd>
|
||||
Get the options for the field at the specified offset.
|
||||
A NULL is returned if no options were specified for the field.
|
||||
If options were specified,PVStructurePtr is
|
||||
a structure with a set of PVString subfields that specify name,value
|
||||
pairs. name is the subField name and value is the subField value.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
<h2>support for monitor</h2>
|
||||
<p>This consists of two components:
|
||||
<dl>
|
||||
<dt>monitor</dt>
|
||||
<dd>Used by code that implements pvAccess monitors.</dd>
|
||||
<dt>monitorPlugin</dt>
|
||||
<dd>Code that provides special semantics for monitors.</dd>
|
||||
</dl>
|
||||
</p>
|
||||
<h3>monitor</h3>
|
||||
<pre>
|
||||
class MonitorElement {
|
||||
MonitorElement(PVStructurePtr const & pvStructurePtr);
|
||||
PVStructurePtr pvStructurePtr;
|
||||
BitSetPtr changedBitSet;
|
||||
BitSetPtr overrunBitSet;
|
||||
};
|
||||
|
||||
class Monitor {
|
||||
virtual Status start() = 0;
|
||||
virtual Status stop() = 0;
|
||||
virtual MonitorElementPtr poll() = 0;
|
||||
virtual void release(MonitorElementPtr const & monitorElement) = 0;
|
||||
};
|
||||
|
||||
class MonitorRequester : public virtual Requester {
|
||||
virtual void monitorConnect(Status const & status,
|
||||
MonitorPtr const & monitor, StructureConstPtr const & structure) = 0;
|
||||
virtual void monitorEvent(MonitorPtr const & monitor) = 0;
|
||||
virtual void unlisten(MonitorPtr const & monitor) = 0;
|
||||
};
|
||||
</pre>
|
||||
<h4>monitorElement</h4>
|
||||
<p><b>MonitorElement</b> holds the data for one element of a monitor queue.
|
||||
It has the fields:
|
||||
<dl>
|
||||
<dt>pvStructurePtr</dt>
|
||||
<dd>A top level structure with data values at the time the monitors occurs.</dd>
|
||||
<dt>changedBitSet</dt>
|
||||
<dd>Shows which fields have changed since the previous monitor.</dd>
|
||||
<dt>overrunBitSet</dt>
|
||||
<dd>Shows which fields have changed more han once since the previous monitor.</dd>
|
||||
</dl>
|
||||
</p>
|
||||
<h4>monitorElement queue</h4>
|
||||
<p>
|
||||
A queue of monitor elements must be implemented by any channel provider that implements
|
||||
<b>Channel::createMonitor</b>.
|
||||
For an example implementation look at pvDatabaseCPP.
|
||||
It has the following:
|
||||
<pre>
|
||||
typedef Queue<MonitorElement> MonitorElementQueue;
|
||||
typedef std::tr1::shared_ptr<MonitorElementQueue> MonitorElementQueuePtr;
|
||||
|
||||
class MultipleElementQueue :
|
||||
public ElementQueue
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(MultipleElementQueue);
|
||||
virtual ~MultipleElementQueue(){}
|
||||
MultipleElementQueue(
|
||||
MonitorLocalPtr const &monitorLocal,
|
||||
MonitorElementQueuePtr const &queue,
|
||||
size_t nfields);
|
||||
virtual void destroy(){}
|
||||
virtual Status start();
|
||||
virtual Status stop();
|
||||
virtual bool dataChanged();
|
||||
virtual MonitorElementPtr poll();
|
||||
virtual void release(MonitorElementPtr const &monitorElement);
|
||||
...
|
||||
};
|
||||
</pre>
|
||||
<h4>Monitor</h4>
|
||||
<p><b>Monitor</b> must be implemented by any channel provider that implements
|
||||
<b>Channel::createMonitor</b>.
|
||||
Remote PVAccess also implements Monitor on the client side.
|
||||
Note that each client has it's own queue that is not shared with other client.
|
||||
</p>
|
||||
<p>Monitor has the following methods:</p>
|
||||
<dl>
|
||||
<dt>start</dt>
|
||||
<dd>
|
||||
Start monitoring.
|
||||
This will result in a an initial monitor that has the current value
|
||||
of all fields.
|
||||
</dd>
|
||||
<dt>stop</dt>
|
||||
<dd>
|
||||
Stop monitoring.
|
||||
</dd>
|
||||
<dt>poll</dt>
|
||||
<dd>
|
||||
Called to get a monitor element.
|
||||
If no new elements are available then a null pointer is returned.
|
||||
</dd>
|
||||
<dt>release</dt>
|
||||
<dd>
|
||||
Release the monitor element.
|
||||
The caller owns the monitor element between the calls to poll and release.
|
||||
</dd>
|
||||
<dl>
|
||||
</dl>
|
||||
<h4>MonitorRequester</h4>
|
||||
<p>This must be implemented by a pvAccess client.
|
||||
It has the methods:</p>
|
||||
<dl>
|
||||
<dt>monitorConnect</dt>
|
||||
<dd>
|
||||
A monitor has either connected of disconnected.
|
||||
</dd>
|
||||
<dt>monitorEvent</dt>
|
||||
<dd>
|
||||
A new monitor element is available.
|
||||
</dd>
|
||||
<dt>unlisten</dt>
|
||||
<dd>
|
||||
The channel is going away. The client cam no longer access the monitor.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<h3>monitorPlugin</h3>
|
||||
<pre>
|
||||
class MonitorPlugin
|
||||
{
|
||||
virtual std::string const & getName() = 0;
|
||||
virtual bool causeMonitor(
|
||||
PVFieldPtr const &pvField,
|
||||
PVStructurePtr const &pvTop,
|
||||
MonitorElementPtr const &monitorElement) = 0;
|
||||
virtual void monitorDone(
|
||||
MonitorElementPtr const &monitorElement);
|
||||
virtual void startMonitoring();
|
||||
virtual void stopMonitoring();
|
||||
virtual void beginGroupPut();
|
||||
virtual void endGroupPut();
|
||||
};
|
||||
|
||||
class MonitorPluginCreator
|
||||
{
|
||||
virtual MonitorPluginPtr create(
|
||||
FieldConstPtr const &field,
|
||||
StructureConstPtr const &top,
|
||||
PVStructurePtr const &pvFieldOptions) = 0;
|
||||
virtual std::string const & getName() = 0;
|
||||
}
|
||||
|
||||
class MonitorPluginManager
|
||||
{
|
||||
static MonitorPluginManagerPtr get();
|
||||
bool addPlugin(
|
||||
std::string const &pluginName,
|
||||
MonitorPluginCreatorPtr const &creator);
|
||||
MonitorPluginCreatorPtr findPlugin(std::string const &pluginName);
|
||||
void showNames();
|
||||
};
|
||||
|
||||
</pre>
|
||||
<h4>MonitorPlugin</h4>
|
||||
<p><b>MonitorPlugin</b> must be implemented by the plugin implementation.
|
||||
It has methods:</p>
|
||||
<dl>
|
||||
<dt>getName</dt>
|
||||
<dd>Get the name of the plugin.</dd>
|
||||
<dt>causeMonitor</dt>
|
||||
<dd>
|
||||
Should the value of pvField cause a monitor to be raised.
|
||||
pvField and pvTop are fields in the top level structure
|
||||
being monitored. monitorElement has the top level structure
|
||||
for the copy</b>.
|
||||
The implementation should <b>not</b> modify the fields in the structure
|
||||
being monitored.
|
||||
Called with pvTop locked.
|
||||
</dd>
|
||||
<dt>monitorDone</dt>
|
||||
<dd>
|
||||
Called just before monitorElement will be given to client.
|
||||
The plugin can change the data values and bitSets in monitorElement.
|
||||
Called with pvTop unlocked.
|
||||
</dd>
|
||||
<dt>startMonitoring</dt>
|
||||
<dd>
|
||||
Monitoring is starting.
|
||||
</dd>
|
||||
<dt>stopMonitoring</dt>
|
||||
<dd>
|
||||
Monitoring is being stopped.
|
||||
</dd>
|
||||
<dt>beginGroupPut</dt>
|
||||
<dd>
|
||||
A set of puts is starting.
|
||||
Called with pvTop locked.
|
||||
</dd>
|
||||
<dt>endGroupPut</dt>
|
||||
<dd>
|
||||
The set of puts is complete.
|
||||
Called with pvTop locked.
|
||||
</dd>
|
||||
</dl>
|
||||
<h4>MonitorPluginCreator</h4>
|
||||
<p><b>MonitorPluginCreator</b> must also be implemented by the plugin implementation.
|
||||
It is called for each field instance that has options of the from
|
||||
<b>[plugin=name...]</b> where <b>name</b> is the name of the plugin.
|
||||
Note that a plugin instance will belong to a single client.
|
||||
It has methods:</p>
|
||||
<dl>
|
||||
<dt>getName</dt>
|
||||
<dd>Get the name of the plugin.</dd>
|
||||
<dt>create</dt>
|
||||
<dd>
|
||||
Create a new plugin instance.
|
||||
If the arguments are not compatible with the plugin a NULL shared pointer is
|
||||
returned.<br/>
|
||||
pvFieldOptions is
|
||||
a structure with a set of PVString subfields that specify <b>name,value</b>
|
||||
pairs. name is the subField name and value is the subField value.<br/>
|
||||
Note that a plugin will below to a single client.
|
||||
</dd>
|
||||
<dl>
|
||||
<h4>MonitorPluginManager</h4>
|
||||
<p><b>MonitorPluginManager</b> has the methods:</p>
|
||||
<dl>
|
||||
<dt>get</dt>
|
||||
<dd>
|
||||
MonitorPluginManager is a singleton.
|
||||
The first call to get will create the single instance.
|
||||
Further calls will return the single instance.
|
||||
</dd>
|
||||
<dt>addPlugin</dt>
|
||||
<dd>
|
||||
Add a new plugin.
|
||||
</dd>
|
||||
<dt>findPlugin</dt>
|
||||
<dd>
|
||||
Find a plugin. A NULL shared pointer is returned if it has not been added.
|
||||
</dd>
|
||||
<dt>showNames</dt>
|
||||
<dd>
|
||||
Show the names of all plugins that have been added.
|
||||
</dd>
|
||||
</dl>
|
||||
<p><b>NOTE:</b>
|
||||
Should the method <b>causeMonitor</b>
|
||||
have arguments <b>pvField</b> and <b>pvTop</b>
|
||||
be defined so that they can not be modified.
|
||||
This would be possible if the following was defined:
|
||||
<pre>
|
||||
typedef std::tr1::shared_ptr<const PVField> PVFieldConstPtr;
|
||||
typedef std::tr1::shared_ptr<const PVStructure> PVStructureConstPtr;
|
||||
</pre>
|
||||
then the definition for causeMonitor could be:
|
||||
<pre>
|
||||
virtual bool causeMonitor(
|
||||
PVFieldConstPtr const &pvField,
|
||||
PVStructureConstPtr const &pvTop,
|
||||
MonitorElementPtr const &monitorElement) = 0;
|
||||
</pre>
|
||||
But just adding these definitions is not sufficient.
|
||||
In addition all methods defined in pvDataCPP must be checked.
|
||||
In particular many of the methods in <b>Convert</b> must have
|
||||
their arguments modified.
|
||||
Big job.
|
||||
</p>
|
||||
<h2>monitorPlugin example</h2>
|
||||
<h3>Example Plugin Overview</h3>
|
||||
<p>This section describes an example plugin that:</p>
|
||||
<ul>
|
||||
<li>Only raises monitors when a field changes value.<br />
|
||||
If no plugin is provided
|
||||
the default is to raise a monitor when a put is issued to a field.</li>
|
||||
<li>Optionally a change will not raise a monitor.<br />
|
||||
The change will, however,
|
||||
appear if a put to another field raise a monitor.</li>
|
||||
</ul>
|
||||
<p>As an example assume that a channel provided by pvAccess has a top level structure
|
||||
that represents a power supply.</p>
|
||||
<pre>
|
||||
structure powerSupply
|
||||
structure alarm
|
||||
structure timeStamp
|
||||
structure power
|
||||
double value
|
||||
structure alarm
|
||||
structure display
|
||||
structure voltage
|
||||
double value
|
||||
structure alarm
|
||||
structure display
|
||||
structure current
|
||||
double value
|
||||
structure alarm
|
||||
structure display
|
||||
</pre>
|
||||
<p>A pvAccess client wants to create a monitor on the powerSupply as follows:
|
||||
The client wants a top level structure that looks like:
|
||||
<pre>
|
||||
structure powerSupply
|
||||
structure alarm
|
||||
structure timeStamp
|
||||
structure power
|
||||
double value
|
||||
structure voltage
|
||||
double value
|
||||
structure current
|
||||
double value
|
||||
</pre>
|
||||
In addition the client wants monitors to occur only when one of the monitored
|
||||
fields changes value but not just because a put occured.
|
||||
Also if only the timeStamp changes value then that should not cause a monitor.
|
||||
</p>
|
||||
<p>The example monitor plugin implements the semantics the
|
||||
client wants. It can be attached to any field via the following options:
|
||||
<pre>
|
||||
[plugin=onChange,raiseMonitor=value]
|
||||
</pre>
|
||||
This plugin will trigger a monitor for the field only if the field changes
|
||||
value. In addition <b>value</b> equals <b>false</b> means do not raise a monitor
|
||||
for changes to this field.
|
||||
But if a change to another field does cause a monitor the change to this field
|
||||
will be passed to the client.
|
||||
</p>
|
||||
<p>
|
||||
Assume that the client has already connected to the channel.
|
||||
The client can then issue the commands:</p>
|
||||
<pre>
|
||||
std::string request("field(alarm[plugin=onChange]");
|
||||
request += ",timeStamp[plugin=onChange,raiseMonitor=false]";
|
||||
request += ",power.value[plugin=onChange";
|
||||
request += ",voltage.value[plugin=onChange";
|
||||
request += ",current.value[plugin=onChange";
|
||||
|
||||
PVStructurePtr pvRequest = createRequest->createRequest(request);
|
||||
|
||||
MonitorPtr monitor = channel->createMonitor(monitorRequester,pvRequest);
|
||||
</pre>
|
||||
<h3>Example Plugin Code</h3>
|
||||
<p>The header file to create the example has the definition:</p>
|
||||
<pre>
|
||||
class ExampleMonitorPlugin{
|
||||
public:
|
||||
static void create();
|
||||
};
|
||||
</pre>
|
||||
<p>The implementation is:</p>
|
||||
<pre>
|
||||
class OnChangePlugin : public MonitorPlugin
|
||||
{
|
||||
public:
|
||||
virtual ~OnChangePlugin(){}
|
||||
OnChangePlugin() {}
|
||||
bool init(
|
||||
FieldConstPtr const &field,
|
||||
StructureConstPtr const &top,
|
||||
PVStructurePtr const &pvFieldOptions)
|
||||
{
|
||||
pvField = getPVDataCreate()->createPVField(field);
|
||||
raiseMonitor = true;
|
||||
if(pvFieldOptions!=NULL) {
|
||||
PVStringPtr pvString =
|
||||
pvFieldOptions->getSubField<PVString>("raiseMonitor");
|
||||
if(pvString!=NULL) {
|
||||
std::string value = pvString->get();
|
||||
if(value.compare("false")==0) raiseMonitor = false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
virtual std::string &getName(){return pluginName;}
|
||||
virtual bool causeMonitor(
|
||||
PVFieldPtr const &pvNew,
|
||||
PVStructurePtr const &pvTop,
|
||||
MonitorElementPtr const &monitorElement)
|
||||
{
|
||||
bool isSame = convert->equals(pvNew,pvField);
|
||||
if(isSame) return false;
|
||||
convert->copy(pvNew,pvField);
|
||||
return raiseMonitor;
|
||||
}
|
||||
private:
|
||||
PVFieldPtr pvField;
|
||||
bool raiseMonitor;
|
||||
};
|
||||
class OnChangePluginCreator : public MonitorPluginCreator
|
||||
{
|
||||
public:
|
||||
virtual std::string &getName(){return pluginName;}
|
||||
virtual MonitorPluginPtr create(
|
||||
FieldConstPtr const &field,
|
||||
StructureConstPtr const &top,
|
||||
PVStructurePtr const &pvFieldOptions)
|
||||
{
|
||||
OnChangePluginPtr plugin(new OnChangePlugin());
|
||||
bool result = plugin->init(field,top,pvFieldOptions);
|
||||
if(!result) return MonitorPluginPtr();
|
||||
return plugin;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void ExampleMonitorPlugin::create()
|
||||
{
|
||||
static OnChangePluginCreatorPtr plugin;
|
||||
static Mutex mutex;
|
||||
Lock xx(mutex);
|
||||
if(plugin==NULL) {
|
||||
plugin = OnChangePluginCreatorPtr(new OnChangePluginCreator());
|
||||
MonitorPluginManager::get()->addPlugin(pluginName,plugin);
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
documentation/examples.zip
Normal file
BIN
documentation/examples.zip
Normal file
Binary file not shown.
@@ -1,58 +0,0 @@
|
||||
#ifndef MAINPAGE_H
|
||||
#define MAINPAGE_H
|
||||
/**
|
||||
@mainpage pvDataCPP documentation
|
||||
|
||||
- This module is included in [EPICS Base releases](https://epics-controls.org/resources-and-support/base/) beginning with 7.0.1
|
||||
- It may also be [Downloaded](https://github.com/epics-base/pvDataCPP/releases) and built separately.
|
||||
- @htmlonly <a href="modules.html">API components</a> @endhtmlonly
|
||||
- @ref release_notes
|
||||
|
||||
The epics::pvData namespace.
|
||||
See pv/pvData.h header.
|
||||
|
||||
@code
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/createRequest.h>
|
||||
@endcode
|
||||
|
||||
- Type description epics::pvData::Field and sub-classes
|
||||
- Value container epics::pvData::PVField and sub-classes
|
||||
- POD array handling epics::pvData::shared_vector
|
||||
- pvRequest parsing epics::pvData::createRequest()
|
||||
|
||||
Define a structure type and create a container with default values.
|
||||
|
||||
@code
|
||||
namespace pvd = epics::pvData;
|
||||
pvd::StructureConstPtr stype(pvd::FieldBuilder::begin()
|
||||
->add("fld1", pvd::pvInt)
|
||||
->addNestedStructure("sub")
|
||||
->add("fld2", pvd::pvString)
|
||||
->endNested()
|
||||
->createStructure());
|
||||
|
||||
pvd::PVStructuretPtr value(stype->build());
|
||||
|
||||
value->getSubFieldT<pvd::PVInt>("fld1")->put(4); // store integer 4. would throw if not pvInt
|
||||
value->getSubFieldT<pvd::PVScalar>("sub.fld2")->putFrom(4.2); // convert and store string "4.2"
|
||||
@endcode
|
||||
|
||||
is equivalent to the following pseudo-code.
|
||||
|
||||
@code
|
||||
struct stype {
|
||||
pvd::int32 fld1;
|
||||
struct {
|
||||
std::string fld2;
|
||||
} sub;
|
||||
};
|
||||
stype value;
|
||||
value.fld1 = 4;
|
||||
value.fld2 = pvd::castUnsafe<std::string>(4.2);
|
||||
@endcode
|
||||
|
||||
*/
|
||||
|
||||
#endif /* MAINPAGE_H */
|
||||
|
||||
5470
documentation/pvDataCPP.html
Normal file
5470
documentation/pvDataCPP.html
Normal file
File diff suppressed because it is too large
Load Diff
179
documentation/pvDataCPPCookbook.txt
Normal file
179
documentation/pvDataCPPCookbook.txt
Normal file
@@ -0,0 +1,179 @@
|
||||
pvDataCPP cookbook
|
||||
------------------
|
||||
|
||||
|
||||
Creating introspection data interfaces
|
||||
|
||||
// create a scalar
|
||||
getFieldCreate()->createScalar(pvDouble);
|
||||
|
||||
// create a scalar array
|
||||
getFieldCreate()->createScalarArray(pvDouble);
|
||||
|
||||
// create a structure
|
||||
getFieldCreate()->createFieldBuilder()->
|
||||
setId("enum_t")->
|
||||
add("index", pvDouble)->
|
||||
addArray("choices", pvString)->
|
||||
createStructure();
|
||||
|
||||
// create a structure (cntd.)
|
||||
StructureConstPtr enum_t =
|
||||
getFieldCreate()->createFieldBuilder()->
|
||||
setId("enum_t")->
|
||||
add("index", pvInt)->
|
||||
addArray("choices", pvString)->
|
||||
createStructure();
|
||||
|
||||
// create a structure (cntd.)
|
||||
StructureConstPtr ntEnum =
|
||||
getFieldCreate()->createFieldBuilder()->
|
||||
setId("epics:nt/NTEnum:1.0")->
|
||||
add("value", enum_t)->
|
||||
addNestedStructure("timeStamp")->
|
||||
setId("time_t")->
|
||||
add("secondsPastEpoch", pvLong)->
|
||||
add("nanoseconds", pvInt)->
|
||||
add("userTag", pvInt)->
|
||||
endNested()->
|
||||
createStructure();
|
||||
|
||||
// create an union == same as structure
|
||||
|
||||
---
|
||||
|
||||
Creating data containers
|
||||
|
||||
// create a scalar
|
||||
PVDouble::shared_pointer doubleValue = getPVDataCreate()->createPVScalar<PVDouble>();
|
||||
|
||||
// create a scalar array
|
||||
PVDoubleArray::shared_pointer doubleArrayValue = getPVDataCreate()->createPVScalarArray<PVDouble>();
|
||||
|
||||
// create a structure
|
||||
PVStructure::shared_pointer struct = getPVDataCreate()->createPVStructure(ntEnum);
|
||||
|
||||
// create an union
|
||||
PVUnion::shared_pointer pvUnion = getPVDataCreate()->createPVUnion(unionIF);
|
||||
|
||||
// create a structure array
|
||||
PVStructureArray::shared_pointer structArray = getPVDataCreate()->createPVStructureArray(ntEnum);
|
||||
|
||||
|
||||
// scalar usage
|
||||
PVInt::shared_pointer index = struct->getSubField<PVInt>("value.index");
|
||||
int32 ix = index->get();
|
||||
index->put(3);
|
||||
std::cout << *index << std::endl;
|
||||
|
||||
|
||||
// using <<=, >>= operators to get/set
|
||||
*doubleValue <<= 12.3;
|
||||
|
||||
double val;
|
||||
*doubleValue >>= val;
|
||||
|
||||
|
||||
|
||||
// array usage
|
||||
PVStringArray::shared_pointer choices = struct->getSubField<PVStringArray>("value.choices");
|
||||
|
||||
// use view() to access read-only data
|
||||
PVStringArray::const_svector data(choices->view());
|
||||
for (std::size_t i = 0; i < data.size(); i++)
|
||||
std::cout << data[i] << std::endl;
|
||||
|
||||
// use replace() to put new data
|
||||
PVStringArray::svector newdata;
|
||||
newdata.push_back("zero");
|
||||
newdata.push_back("one");
|
||||
newdata.push_back("two");
|
||||
choices->replace(freeze(newdata));
|
||||
|
||||
// (add more use-cases) here
|
||||
|
||||
// print entire array
|
||||
std::cout << *choices << std::endl;
|
||||
|
||||
// print elmenet at index == 1
|
||||
std::cout << format::array_at(1) << *choices << std::endl;
|
||||
|
||||
----
|
||||
|
||||
Union handling
|
||||
|
||||
|
||||
Union::const_shared_pointer punion =
|
||||
getFieldCreate()->createFieldBuilder()->
|
||||
add("doubleValue", pvDouble)->
|
||||
add("intValue", pvInt)->
|
||||
createUnion();
|
||||
|
||||
PVUnion::shared_pointer u = getPVDataCreate()->createPVUnion(punion);
|
||||
|
||||
// select and put
|
||||
// this create a new instance of PVDouble (everytime select() is called)
|
||||
PVDouble::shared_pointer doubleValue = u->select<PVDouble>("doubleValue");
|
||||
doubeValue->put(12);
|
||||
// select using index (and direct put)
|
||||
u->select<PVDouble>(0)->put(12);
|
||||
// select using existing PVField (PVUnion stores by-reference)
|
||||
u->set("doubleValue", doubleValue);
|
||||
|
||||
// get selected field name or index
|
||||
std::string selectedFN = u->getSelectedFieldName();
|
||||
int32 selectedIndex = u->getSelectedIndex();
|
||||
|
||||
// get currently selected (knowing it's PVDouble)
|
||||
PVDouble value = u->get<PVDouble>();
|
||||
|
||||
|
||||
|
||||
Variant Union handling
|
||||
|
||||
|
||||
PVUnion::shared_pointer any = getPVDataCreate()->createPVVariantUnion();
|
||||
|
||||
PVDouble::shared_pointer doubleValue = getPVDataCreate()->createPVScalar<PVDouble>();
|
||||
doubleValue->put(12.8);
|
||||
any->set(doubleValue);
|
||||
|
||||
PVDouble::shared_pointer doubleValue2 = any->get<PVDouble>();
|
||||
|
||||
// variant union work by-reference (pointers match also)
|
||||
// doubleValue.get() == doubleValue2.get()
|
||||
|
||||
|
||||
|
||||
------
|
||||
|
||||
Convert
|
||||
|
||||
|
||||
// convert to int
|
||||
int32 i = doubleValue->getAs<int32>();
|
||||
|
||||
// from int
|
||||
doubleValue->putFrom<int32>(i);
|
||||
|
||||
// from string
|
||||
doubleValue->putFrom<std::string>("12.3");
|
||||
|
||||
// from scalar field
|
||||
doubleValue->assign(pvScalar);
|
||||
|
||||
|
||||
|
||||
// convert to int array
|
||||
PVIntArray::const_svector intData;
|
||||
doubleArrayValue->getAs<int32>(intData);
|
||||
|
||||
// from string array
|
||||
PVStringArray::svector labels;
|
||||
labels.push_back("zero");
|
||||
labels.push_back("one");
|
||||
labels.push_back("two");
|
||||
doubleArrayValue->putFrom<std::string>(labels);
|
||||
|
||||
// from scalar array
|
||||
doubleArrayValue->assign(pvScalarArray);
|
||||
@@ -1,14 +0,0 @@
|
||||
# Makefile for the examples
|
||||
# make sure they compile
|
||||
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
PROD_LIBS += pvData Com
|
||||
|
||||
TESTPROD_HOST += unittest
|
||||
unittest_SRCS += unittest.cpp
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/* c++ unittest skeleton */
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <testMain.h>
|
||||
|
||||
#include <pv/pvUnitTest.h>
|
||||
#include <pv/epicsException.h>
|
||||
|
||||
namespace {
|
||||
void testCase1() {
|
||||
testEqual(1+1, 2);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
MAIN(testUnitTest)
|
||||
{
|
||||
testPlan(1);
|
||||
try {
|
||||
testCase1();
|
||||
}catch(std::exception& e){
|
||||
PRINT_EXCEPTION(e); // print stack trace if thrown with THROW_EXCEPTION()
|
||||
testAbort("Unhandled exception: %s", e.what());
|
||||
}
|
||||
return testDone();
|
||||
}
|
||||
@@ -21,15 +21,23 @@ installE4 () {
|
||||
local module=$1
|
||||
local branch=$2
|
||||
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE}/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
|
||||
# If microbench version does not exist, try without
|
||||
if [ "${MB}" = "WITH_MICROBENCH" ]; then
|
||||
if ! wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE},MB=WITH_MICROBENCH/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz; then
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE},MB=NO_MICROBENCH/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
|
||||
fi
|
||||
else
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE},MB=NO_MICROBENCH/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
|
||||
fi
|
||||
tar -xzf ${module}.CB-dist.tar.gz
|
||||
}
|
||||
|
||||
###########################################
|
||||
# Defaults for EPICS Base
|
||||
# Defaults for EPICS Base and MB
|
||||
|
||||
DEFAULT_BASE=3.15.4
|
||||
BASE=${BASE:-${DEFAULT_BASE}}
|
||||
MB=${MB:-"NO_MICROBENCH"}
|
||||
|
||||
###########################################
|
||||
# Fetch and unpack dependencies
|
||||
@@ -65,6 +73,6 @@ make distclean all
|
||||
make runtests
|
||||
|
||||
###########################################
|
||||
# Create cache
|
||||
# Create distribution
|
||||
|
||||
tar czf pvData.CB-dist.tar.gz lib include cfg LICENSE
|
||||
tar czf pvData.CB-dist.tar.gz lib include LICENSE
|
||||
|
||||
@@ -21,7 +21,14 @@ installE4 () {
|
||||
local module=$1
|
||||
local branch=$2
|
||||
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE}/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
|
||||
# If microbench version does not exist, try without
|
||||
if [ "${MB}" = "WITH_MICROBENCH" ]; then
|
||||
if ! wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE},MB=WITH_MICROBENCH/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz; then
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE},MB=NO_MICROBENCH/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
|
||||
fi
|
||||
else
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE},MB=NO_MICROBENCH/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
|
||||
fi
|
||||
tar -xzf ${module}.CB-dist.tar.gz
|
||||
}
|
||||
|
||||
@@ -31,6 +38,7 @@ installE4 () {
|
||||
BASE=3.15.4
|
||||
PUBLISH=${PUBLISH:-NO}
|
||||
BRANCH=${BRANCH:-master}
|
||||
MB=NO_MICROBENCH
|
||||
|
||||
###########################################
|
||||
# Fetch and unpack dependencies
|
||||
|
||||
16
src/Makefile
16
src/Makefile
@@ -11,26 +11,14 @@ include $(PVDATA_SRC)/factory/Makefile
|
||||
include $(PVDATA_SRC)/property/Makefile
|
||||
include $(PVDATA_SRC)/copy/Makefile
|
||||
include $(PVDATA_SRC)/pvMisc/Makefile
|
||||
include $(PVDATA_SRC)/json/Makefile
|
||||
include $(PVDATA_SRC)/monitor/Makefile
|
||||
|
||||
LIBRARY = pvData
|
||||
|
||||
pvData_LIBS += Com
|
||||
|
||||
EXPANDVARS += EPICS_PVD_MAJOR_VERSION
|
||||
EXPANDVARS += EPICS_PVD_MINOR_VERSION
|
||||
EXPANDVARS += EPICS_PVD_MAINTENANCE_VERSION
|
||||
EXPANDVARS += EPICS_PVD_DEVELOPMENT_FLAG
|
||||
|
||||
EXPANDFLAGS += $(foreach var,$(EXPANDVARS),-D$(var)="$(strip $($(var)))")
|
||||
|
||||
# shared library ABI version.
|
||||
SHRLIB_VERSION = $(EPICS_PVD_MAJOR_VERSION).$(EPICS_PVD_MINOR_VERSION).$(EPICS_PVD_MAINTENANCE_VERSION)
|
||||
SHRLIB_VERSION ?= 6.0
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
# Can't use EXPAND as generated headers must appear
|
||||
# in O.Common, but EXPAND emits rules for O.$(T_A)
|
||||
../O.Common/pv/pvdVersionNum.h: ../pv/pvdVersionNum.h@
|
||||
$(MKDIR) $(COMMON_DIR)/pv
|
||||
$(EXPAND_TOOL) $(EXPANDFLAGS) $($@_EXPANDFLAGS) $< $@
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
SRC_DIRS += $(PVDATA_SRC)/copy
|
||||
|
||||
INC += pv/createRequest.h
|
||||
INC += pv/pvCopy.h
|
||||
|
||||
LIBSRCS += createRequest.cpp
|
||||
LIBSRCS += requestmapper.cpp
|
||||
LIBSRCS += pvCopy.cpp
|
||||
|
||||
@@ -22,13 +22,13 @@ using std::endl;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
namespace {
|
||||
using namespace epics::pvData;
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
static PVDataCreatePtr pvDataCreate = getPVDataCreate();
|
||||
static FieldCreatePtr fieldCreate = getFieldCreate();
|
||||
|
||||
struct CreateRequestImpl {
|
||||
class CreateRequestImpl : public CreateRequest {
|
||||
private:
|
||||
|
||||
struct Node
|
||||
{
|
||||
@@ -54,7 +54,12 @@ struct CreateRequestImpl {
|
||||
string fullFieldName;
|
||||
|
||||
|
||||
CreateRequestImpl() {}
|
||||
public:
|
||||
CreateRequestImpl()
|
||||
{
|
||||
fullFieldName = "";
|
||||
}
|
||||
private:
|
||||
|
||||
|
||||
void removeBlanks(string& str)
|
||||
@@ -70,7 +75,8 @@ struct CreateRequestImpl {
|
||||
size_t openBrace = request.find('{', index+1);
|
||||
size_t closeBrace = request.find('}', index+1);
|
||||
if(openBrace == string::npos && closeBrace == string::npos){
|
||||
throw std::runtime_error(request + " mismatched {}");
|
||||
message = request + " mismatched {}";
|
||||
throw std::logic_error("message");
|
||||
}
|
||||
if (openBrace != string::npos && openBrace!=0) {
|
||||
if(openBrace<closeBrace) return findMatchingBrace(request,openBrace,numOpen+1);
|
||||
@@ -85,12 +91,14 @@ struct CreateRequestImpl {
|
||||
for(size_t i=index+1; i< request.size(); ++i) {
|
||||
if(request[i] == ']') {
|
||||
if(i==index+1) {
|
||||
throw std::runtime_error(request + " mismatched []");
|
||||
message = request + " mismatched []";
|
||||
throw std::logic_error("message");
|
||||
}
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error(request + " missing ]");
|
||||
message = request + " missing ]";
|
||||
throw std::logic_error("message");
|
||||
}
|
||||
|
||||
size_t findEndField(string& request) {
|
||||
@@ -145,7 +153,7 @@ struct CreateRequestImpl {
|
||||
string const & request)
|
||||
{
|
||||
if(request.length()<=1) {
|
||||
throw std::runtime_error("logic error empty options");
|
||||
throw std::logic_error("logic error empty options");
|
||||
}
|
||||
vector<Node> top;
|
||||
vector<string> items = split(request);
|
||||
@@ -155,7 +163,8 @@ struct CreateRequestImpl {
|
||||
string item = items[j];
|
||||
size_t equals = item.find('=');
|
||||
if(equals==string::npos || equals==0) {
|
||||
throw std::runtime_error(item + " illegal option " + request);
|
||||
message = item + " illegal option " + request;
|
||||
throw std::logic_error("message");
|
||||
}
|
||||
top.push_back(Node(item.substr(0,equals)));
|
||||
string name = fullFieldName + "._options." + item.substr(0,equals);
|
||||
@@ -204,7 +213,7 @@ struct CreateRequestImpl {
|
||||
if(end==0) end = request.size();
|
||||
string name = request.substr(0,end);
|
||||
if(name.size()<1) {
|
||||
throw std::runtime_error("null field name " + request);
|
||||
throw std::logic_error("null field name " + request);
|
||||
}
|
||||
string saveFullName = fullFieldName;
|
||||
fullFieldName += "." + name;
|
||||
@@ -227,7 +236,7 @@ struct CreateRequestImpl {
|
||||
if(chr=='.') {
|
||||
request = request.substr(end+1);
|
||||
if(request.size()==string::npos || request.size()<1) {
|
||||
throw std::runtime_error("null field name " + request);
|
||||
throw std::logic_error("null field name " + request);
|
||||
}
|
||||
Node subNode(name);
|
||||
if(optionNode.name.size()>0) subNode.nodes.push_back(optionNode);
|
||||
@@ -248,11 +257,11 @@ struct CreateRequestImpl {
|
||||
if(chr=='{') {
|
||||
size_t endBrace = findEndField(request);
|
||||
if((end+1)>=(endBrace-1)) {
|
||||
throw std::runtime_error("illegal syntax " + request);
|
||||
throw std::logic_error("illegal syntax " + request);
|
||||
}
|
||||
string subRequest = request.substr(end+1,endBrace-1 -end -1);
|
||||
if(subRequest.size()<1) {
|
||||
throw std::runtime_error("empty {} " + request);
|
||||
throw std::logic_error("empty {} " + request);
|
||||
}
|
||||
Node subNode(name);
|
||||
if(optionNode.name.size()>0) subNode.nodes.push_back(optionNode);
|
||||
@@ -268,7 +277,7 @@ struct CreateRequestImpl {
|
||||
createSubNode(node,request);
|
||||
return;
|
||||
}
|
||||
throw std::runtime_error("logic error");
|
||||
throw std::logic_error("logic error");
|
||||
}
|
||||
|
||||
FieldConstPtr createSubStructure(vector<Node> & nodes)
|
||||
@@ -309,16 +318,17 @@ struct CreateRequestImpl {
|
||||
return structure;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
PVStructurePtr createRequest(
|
||||
virtual PVStructurePtr createRequest(
|
||||
string const & crequest)
|
||||
{
|
||||
{
|
||||
try {
|
||||
string request = crequest;
|
||||
if (!request.empty()) removeBlanks(request);
|
||||
if (request.empty())
|
||||
{
|
||||
return fieldCreate->createStructure()->build();
|
||||
return pvDataCreate->createPVStructure(fieldCreate->createStructure());
|
||||
}
|
||||
size_t offsetRecord = request.find("record[");
|
||||
size_t offsetField = request.find("field(");
|
||||
@@ -347,17 +357,20 @@ struct CreateRequestImpl {
|
||||
if(numParan!=0) {
|
||||
ostringstream oss;
|
||||
oss << "mismatched () " << numParan;
|
||||
throw std::runtime_error(oss.str());
|
||||
message = oss.str();
|
||||
return PVStructurePtr();
|
||||
}
|
||||
if(numBrace!=0) {
|
||||
ostringstream oss;
|
||||
oss << "mismatched {} " << numBrace;
|
||||
throw std::runtime_error(oss.str());
|
||||
message = oss.str();
|
||||
return PVStructurePtr();
|
||||
}
|
||||
if(numBracket!=0) {
|
||||
ostringstream oss;
|
||||
oss << "mismatched [] " << numBracket;
|
||||
throw std::runtime_error(oss.str());
|
||||
message = oss.str();
|
||||
return PVStructurePtr();
|
||||
}
|
||||
vector<Node> top;
|
||||
try {
|
||||
@@ -366,8 +379,9 @@ struct CreateRequestImpl {
|
||||
size_t openBracket = request.find('[', offsetRecord);
|
||||
size_t closeBracket = request.find(']', openBracket);
|
||||
if(closeBracket==string::npos) {
|
||||
throw std::runtime_error(request.substr(offsetRecord) +
|
||||
"record[ does not have matching ]");
|
||||
message = request.substr(offsetRecord) +
|
||||
"record[ does not have matching ]";
|
||||
return PVStructurePtr();
|
||||
}
|
||||
if(closeBracket-openBracket > 3) {
|
||||
Node node("record");
|
||||
@@ -383,8 +397,9 @@ struct CreateRequestImpl {
|
||||
size_t openParan = request.find('(', offsetField);
|
||||
size_t closeParan = request.find(')', openParan);
|
||||
if(closeParan==string::npos) {
|
||||
throw std::runtime_error(request.substr(offsetField)
|
||||
+ " field( does not have matching )");
|
||||
message = request.substr(offsetField)
|
||||
+ " field( does not have matching )";
|
||||
return PVStructurePtr();
|
||||
}
|
||||
if(closeParan>openParan+1) {
|
||||
createSubNode(node,request.substr(openParan+1,closeParan-openParan-1));
|
||||
@@ -397,8 +412,9 @@ struct CreateRequestImpl {
|
||||
size_t openParan = request.find('(', offsetGetField);
|
||||
size_t closeParan = request.find(')', openParan);
|
||||
if(closeParan==string::npos) {
|
||||
throw std::runtime_error(request.substr(offsetField)
|
||||
+ " getField( does not have matching )");
|
||||
message = request.substr(offsetField)
|
||||
+ " getField( does not have matching )";
|
||||
return PVStructurePtr();
|
||||
}
|
||||
if(closeParan>openParan+1) {
|
||||
createSubNode(node,request.substr(openParan+1,closeParan-openParan-1));
|
||||
@@ -411,8 +427,9 @@ struct CreateRequestImpl {
|
||||
size_t openParan = request.find('(', offsetPutField);
|
||||
size_t closeParan = request.find(')', openParan);
|
||||
if(closeParan==string::npos) {
|
||||
throw std::runtime_error(request.substr(offsetField)
|
||||
+ " putField( does not have matching )");
|
||||
message = request.substr(offsetField)
|
||||
+ " putField( does not have matching )";
|
||||
return PVStructurePtr();
|
||||
}
|
||||
if(closeParan>openParan+1) {
|
||||
createSubNode(node,request.substr(openParan+1,closeParan-openParan-1));
|
||||
@@ -420,7 +437,9 @@ struct CreateRequestImpl {
|
||||
top.push_back(node);
|
||||
}
|
||||
} catch (std::exception &e) {
|
||||
throw std::runtime_error(std::string("while creating Structure exception ")+e.what());
|
||||
string xxx = e.what();
|
||||
message = "while creating Structure exception " + xxx;
|
||||
return PVStructurePtr();
|
||||
}
|
||||
size_t num = top.size();
|
||||
StringArray names(num);
|
||||
@@ -437,7 +456,7 @@ struct CreateRequestImpl {
|
||||
}
|
||||
StructureConstPtr structure = fieldCreate->createStructure(names, fields);
|
||||
if(!structure) throw std::invalid_argument("bad request " + crequest);
|
||||
PVStructurePtr pvStructure = structure->build();
|
||||
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(structure);
|
||||
for(size_t i=0; i<optionList.size(); ++i) {
|
||||
OptionPair pair = optionList[i];
|
||||
string name = pair.name;
|
||||
@@ -448,39 +467,20 @@ struct CreateRequestImpl {
|
||||
}
|
||||
optionList.clear();
|
||||
return pvStructure;
|
||||
} catch (std::exception &e) {
|
||||
message = e.what();
|
||||
return PVStructurePtr();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace epics {namespace pvData {
|
||||
|
||||
CreateRequest::shared_pointer CreateRequest::create()
|
||||
{
|
||||
CreateRequest::shared_pointer createRequest(new CreateRequest());
|
||||
CreateRequest::shared_pointer createRequest(new CreateRequestImpl());
|
||||
return createRequest;
|
||||
}
|
||||
|
||||
PVStructure::shared_pointer CreateRequest::createRequest(std::string const & request)
|
||||
{
|
||||
message.clear();
|
||||
try {
|
||||
return ::createRequest(request);
|
||||
} catch(std::exception& e) {
|
||||
message = e.what();
|
||||
return PVStructure::shared_pointer();
|
||||
}
|
||||
}
|
||||
|
||||
PVStructure::shared_pointer createRequest(std::string const & request)
|
||||
{
|
||||
CreateRequestImpl I;
|
||||
return I.createRequest(request);
|
||||
}
|
||||
|
||||
|
||||
}} // namespace
|
||||
}}
|
||||
|
||||
|
||||
@@ -8,18 +8,14 @@
|
||||
#define CREATEREQUEST_H
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/lock.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class BitSet;
|
||||
|
||||
/**
|
||||
* @brief Create pvRequest structure for Channel methods.
|
||||
*
|
||||
@@ -36,7 +32,7 @@ class epicsShareClass CreateRequest {
|
||||
* @returns A shared pointer to the new instance.
|
||||
*/
|
||||
static CreateRequest::shared_pointer create();
|
||||
~CreateRequest() {};
|
||||
virtual ~CreateRequest() {};
|
||||
/**
|
||||
* Create a request structure for the create calls in Channel.
|
||||
* See the package overview documentation for details.
|
||||
@@ -45,7 +41,7 @@ class epicsShareClass CreateRequest {
|
||||
* If a NULL PVStructure is returned then getMessage will return
|
||||
* the reason.
|
||||
*/
|
||||
PVStructure::shared_pointer createRequest(std::string const & request);
|
||||
virtual PVStructure::shared_pointer createRequest(std::string const & request) = 0;
|
||||
/**
|
||||
* Get the error message of createRequest returns NULL
|
||||
* return the error message
|
||||
@@ -56,176 +52,6 @@ protected:
|
||||
std::string message;
|
||||
};
|
||||
|
||||
/** Parse and build pvRequest structure.
|
||||
*
|
||||
@params request the Request string to be parsed. eg. "field(value)"
|
||||
@returns The resulting strucuture. Never NULL
|
||||
@throws std::exception for various parsing errors
|
||||
*/
|
||||
epicsShareExtern
|
||||
PVStructure::shared_pointer createRequest(std::string const & request);
|
||||
|
||||
/** Helper for implementations of epics::pvAccess::ChannelProvider in interpreting the
|
||||
* 'field' substructure of a pvRequest.
|
||||
* Copies between an internal (base) Structure, and a client/user visible (requested) Structure.
|
||||
*
|
||||
* @note PVRequestMapper is not re-entrant. It is copyable and swap()able.
|
||||
*/
|
||||
class epicsShareClass PVRequestMapper {
|
||||
public:
|
||||
enum mode_t {
|
||||
/** Masking mode.
|
||||
*
|
||||
* Requested Structure is identical to Base.
|
||||
* The 'field' substructure of the provided pvRequest is used to construct a BitSet
|
||||
* where the bits corresponding to the "selected" fields are set. This mask can be
|
||||
* access via. requestedMask(). The copy* and mask* methods operate only
|
||||
* on "selected" fields.
|
||||
*/
|
||||
Mask,
|
||||
/** Slice mode
|
||||
*
|
||||
* The Requested Structure is a strict sub-set of the Base Structure containing
|
||||
* those fields "selected" by the 'field' substructure of the provided pvRequest.
|
||||
*/
|
||||
Slice,
|
||||
};
|
||||
|
||||
PVRequestMapper();
|
||||
//! @see compute()
|
||||
PVRequestMapper(const PVStructure& base,
|
||||
const PVStructure& pvRequest,
|
||||
mode_t mode = Mask);
|
||||
|
||||
//! return to state of default ctor
|
||||
void reset();
|
||||
|
||||
//! @returns the Structure of the PVStructure previously passed to compute(). NULL if never computed()'d
|
||||
inline const StructureConstPtr& base() const { return typeBase; }
|
||||
//! @returns the Structure which is the selected sub-set of the base Structure. NULL if never computed()'d
|
||||
inline const StructureConstPtr& requested() const { return typeRequested; }
|
||||
|
||||
/** A mask of all fields in the base structure which are also in the requested structure,
|
||||
* and any parent/structure "compress" bits. eg. bit 0 is always set.
|
||||
*
|
||||
@code
|
||||
PVRequestMapper mapper(...);
|
||||
...
|
||||
BitSet changed = ...; // a base changed mask
|
||||
bool wouldcopy = changed.logical_and(mapper.requestedMask());
|
||||
// wouldcopy==false means that copyBaseToRequested(..., changed, ...) would be a no-op
|
||||
@endcode
|
||||
*
|
||||
* eg. allows early detection of empty monitor updates.
|
||||
*/
|
||||
inline const BitSet& requestedMask() const { return maskRequested; }
|
||||
|
||||
//! @returns A new instance of the requested() Structure
|
||||
PVStructurePtr buildRequested() const;
|
||||
//! @returns A new instance of the base() Structure
|
||||
PVStructurePtr buildBase() const;
|
||||
|
||||
/** (re)compute the selected subset of provided base structure.
|
||||
* @param base A full base structure.
|
||||
* Must be "top level" (field offset zero).
|
||||
* @param pvRequest The user/client provided request modifier
|
||||
* @param mode Control how the mapping is constructed. @see mode_t for a description of mapping modes.
|
||||
*
|
||||
* @post Updates warnings()
|
||||
* @throws std::runtime_error For errors involving invalid pvRequest
|
||||
* @throws std::logic_error if the provided base is not a "top level" PVStructure.
|
||||
*/
|
||||
void compute(const PVStructure& base,
|
||||
const PVStructure& pvRequest,
|
||||
mode_t mode = Mask);
|
||||
|
||||
//! After compute(), check if !warnings().empty()
|
||||
inline const std::string& warnings() const { return messages; }
|
||||
|
||||
/** Copy field values from Base structure into Requested structure
|
||||
*
|
||||
* @param base An instance of the base Structure. Field values are copied from it.
|
||||
* Need not be the same instance passed to compute().
|
||||
* @param baseMask A bit mask selecting those base fields to copy.
|
||||
* @param request An instance of the requested() Structure. Field values are copied to it.
|
||||
* @param requestMask A bit mask indicating which requested fields were copied.
|
||||
* BitSet::clear() is not called.
|
||||
*/
|
||||
void copyBaseToRequested(
|
||||
const PVStructure& base,
|
||||
const BitSet& baseMask,
|
||||
PVStructure& request,
|
||||
BitSet& requestMask
|
||||
) const;
|
||||
|
||||
/** Copy field values into Base structure from Requested structure
|
||||
*
|
||||
* @param base An instance of the base Structure. Field values are copied into it.
|
||||
* Need not be the same instance passed to compute().
|
||||
* @param baseMask A bit mask indicating which base fields were copied.
|
||||
* BitSet::clear() is not called.
|
||||
* @param request An instance of the requested() Structure. Field values are copied from it.
|
||||
* @param requestMask A bit mask selecting those requested fields to copy.
|
||||
*/
|
||||
void copyBaseFromRequested(
|
||||
PVStructure& base,
|
||||
BitSet& baseMask,
|
||||
const PVStructure& request,
|
||||
const BitSet& requestMask
|
||||
) const;
|
||||
|
||||
//! Translate Base bit mask into requested bit mask.
|
||||
//! BitSet::clear() is not called.
|
||||
inline void maskBaseToRequested(
|
||||
const BitSet& baseMask,
|
||||
BitSet& requestMask
|
||||
) const
|
||||
{ _mapMask(baseMask, requestMask, false); }
|
||||
|
||||
//! Translate requested bit mask into base bit mask.
|
||||
//! BitSet::clear() is not called.
|
||||
inline void maskBaseFromRequested(
|
||||
BitSet& baseMask,
|
||||
const BitSet& requestMask
|
||||
) const
|
||||
{ _mapMask(requestMask, baseMask, true); }
|
||||
|
||||
//! Exchange contents of two mappers. O(0) and never throws.
|
||||
void swap(PVRequestMapper& other);
|
||||
|
||||
private:
|
||||
bool _compute(const PVStructure& base, const PVStructure& pvReq,
|
||||
FieldBuilderPtr& builder, bool keepids, unsigned depth);
|
||||
|
||||
void _map(const PVStructure& src,
|
||||
const BitSet& maskSrc,
|
||||
PVStructure& dest,
|
||||
BitSet& maskDest,
|
||||
bool dir_r2b) const;
|
||||
void _mapMask(const BitSet& maskSrc,
|
||||
BitSet& maskDest,
|
||||
bool dir_r2b) const;
|
||||
|
||||
StructureConstPtr typeBase, typeRequested;
|
||||
BitSet maskRequested;
|
||||
// Map between field offsets of base and requested Structures.
|
||||
// Include all fields, both leaf and sub-structure.
|
||||
struct Mapping {
|
||||
size_t to; // offset in destination Structure
|
||||
BitSet tomask, // if !leaf these are the other bits in the destination mask to changed
|
||||
frommask; // if !leaf these are the other bits in the source mask to be copied
|
||||
bool valid; // only true in (sparse) base -> requested mapping
|
||||
bool leaf; // not a (sub)Structure?
|
||||
Mapping() :valid(false) {}
|
||||
Mapping(size_t to, bool leaf) :to(to), valid(true), leaf(leaf) {}
|
||||
};
|
||||
typedef std::vector<Mapping> mapping_t;
|
||||
mapping_t base2req, req2base;
|
||||
|
||||
std::string messages;
|
||||
|
||||
mutable BitSet scratch; // avoid temporary allocs. (we aren't re-entrant!)
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
|
||||
232
src/copy/pv/pvCopy.h
Normal file
232
src/copy/pv/pvCopy.h
Normal file
@@ -0,0 +1,232 @@
|
||||
/* pvCopy.h */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author Marty Kraimer
|
||||
* @date 2013.04
|
||||
*/
|
||||
#ifndef PVCOPY_H
|
||||
#define PVCOPY_H
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
namespace epics { namespace pvData{
|
||||
|
||||
class PVCopyTraverseMasterCallback;
|
||||
typedef std::tr1::shared_ptr<PVCopyTraverseMasterCallback> PVCopyTraverseMasterCallbackPtr;
|
||||
|
||||
/**
|
||||
* @brief Callback for traversing master structure
|
||||
*
|
||||
* Must be implemented by code that creates pvCopy.
|
||||
*/
|
||||
class epicsShareClass PVCopyTraverseMasterCallback
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(PVCopyTraverseMasterCallback);
|
||||
virtual ~PVCopyTraverseMasterCallback() {}
|
||||
/**
|
||||
* Called once for each field in master.
|
||||
* @param pvField The field in master.
|
||||
*/
|
||||
virtual void nextMasterPVField(epics::pvData::PVFieldPtr const &pvField) = 0;
|
||||
};
|
||||
|
||||
|
||||
class PVCopy;
|
||||
typedef std::tr1::shared_ptr<PVCopy> PVCopyPtr;
|
||||
|
||||
struct CopyNode;
|
||||
typedef std::tr1::shared_ptr<CopyNode> CopyNodePtr;
|
||||
struct CopyMasterNode;
|
||||
typedef std::tr1::shared_ptr<CopyMasterNode> CopyMasterNodePtr;
|
||||
struct CopyStructureNode;
|
||||
typedef std::tr1::shared_ptr<CopyStructureNode> CopyStructureNodePtr;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Support for subset of fields in a pvStructure.
|
||||
*
|
||||
* Class that manages one or more PVStructures that holds an arbitrary subset of the fields
|
||||
* in another PVStructure called master.
|
||||
*/
|
||||
class epicsShareClass PVCopy :
|
||||
public std::tr1::enable_shared_from_this<PVCopy>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(PVCopy);
|
||||
/**
|
||||
* Create a new pvCopy
|
||||
* @param pvMaster The top-level structure for which a copy of
|
||||
* an arbitrary subset of the fields in master will be created and managed.
|
||||
* @param pvRequest Selects the set of subfields desired and options for each field.
|
||||
* @param structureName The name for the top level of any PVStructure created.
|
||||
*/
|
||||
static PVCopyPtr create(
|
||||
PVStructurePtr const &pvMaster,
|
||||
PVStructurePtr const &pvRequest,
|
||||
std::string const & structureName);
|
||||
virtual ~PVCopy(){}
|
||||
virtual void destroy();
|
||||
/**
|
||||
* Get the top-level structure of master
|
||||
* @returns The master top-level structure.
|
||||
* This should not be modified.
|
||||
*/
|
||||
PVStructurePtr getPVMaster();
|
||||
/**
|
||||
* Traverse all the fields in master.
|
||||
* @param callback This is called for each field on master.
|
||||
*/
|
||||
void traverseMaster(PVCopyTraverseMasterCallbackPtr const & callback)
|
||||
{
|
||||
traverseMaster(headNode,callback);
|
||||
}
|
||||
/**
|
||||
* Get the introspection interface for a PVStructure for e copy.
|
||||
*/
|
||||
StructureConstPtr getStructure();
|
||||
/**
|
||||
* Create a copy instance. Monitors keep a queue of monitor elements.
|
||||
* Since each element needs a PVStructure, multiple top-level structures will be created.
|
||||
*/
|
||||
PVStructurePtr createPVStructure();
|
||||
/**
|
||||
* Given a field in pvMaster. return the offset in copy for the same field.
|
||||
* A value of std::string::npos means that the copy does not have this field.
|
||||
* @param masterPVField The field in master.
|
||||
*/
|
||||
std::size_t getCopyOffset(PVFieldPtr const &masterPVField);
|
||||
/**
|
||||
* Given a field in pvMaster. return the offset in copy for the same field.
|
||||
* A value of std::string::npos means that the copy does not have this field.
|
||||
* @param masterPVStructure A structure in master that has masterPVField.
|
||||
* @param masterPVField The field in master.
|
||||
*/
|
||||
std::size_t getCopyOffset(
|
||||
PVStructurePtr const &masterPVStructure,
|
||||
PVFieldPtr const &masterPVField);
|
||||
/**
|
||||
* Given an offset in the copy get the corresponding field in pvMaster.
|
||||
* @param structureOffset The offset in the copy.
|
||||
*/
|
||||
PVFieldPtr getMasterPVField(std::size_t structureOffset);
|
||||
/**
|
||||
* Initialize the fields in copyPVStructure by giving each field
|
||||
* the value from the corresponding field in pvMaster.
|
||||
* bitSet will be set to show that all fields are changed.
|
||||
* @param copyPVStructure A copy top-level structure.
|
||||
* @param bitSet A bitSet for copyPVStructure.
|
||||
*/
|
||||
void initCopy(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
/**
|
||||
* Set all fields in copyPVStructure to the value of the corresponding field in pvMaster.
|
||||
* Each field that is changed has it's corresponding bit set in bitSet.
|
||||
* @param copyPVStructure A copy top-level structure.
|
||||
* @param bitSet A bitSet for copyPVStructure.
|
||||
*/
|
||||
void updateCopySetBitSet(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
/**
|
||||
* For each set bit in bitSet
|
||||
* set the field in copyPVStructure to the value of the corresponding field in pvMaster.
|
||||
* @param copyPVStructure A copy top-level structure.
|
||||
* @param bitSet A bitSet for copyPVStructure.
|
||||
*/
|
||||
void updateCopyFromBitSet(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
/**
|
||||
* For each set bit in bitSet
|
||||
* set the field in pvMaster to the value of the corresponding field in copyPVStructure
|
||||
* @param copyPVStructure A copy top-level structure.
|
||||
* @param bitSet A bitSet for copyPVStructure.
|
||||
*/
|
||||
void updateMaster(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
/**
|
||||
* Get the options for the field at the specified offset.
|
||||
* @param fieldOffset the offset in copy.
|
||||
* @returns A NULL is returned if no options were specified for the field.
|
||||
* If options were specified,PVStructurePtr is a structures
|
||||
* with a set of PVString subfields that specify name,value pairs.s
|
||||
* name is the subField name and value is the subField value.
|
||||
*/
|
||||
PVStructurePtr getOptions(std::size_t fieldOffset);
|
||||
/**
|
||||
* For debugging.
|
||||
*/
|
||||
std::string dump();
|
||||
private:
|
||||
void dump(
|
||||
std::string *builder,
|
||||
CopyNodePtr const &node,
|
||||
int indentLevel);
|
||||
PVCopyPtr getPtrSelf()
|
||||
{
|
||||
return shared_from_this();
|
||||
}
|
||||
void traverseMaster(CopyNodePtr const &node, PVCopyTraverseMasterCallbackPtr const & callback);
|
||||
|
||||
PVStructurePtr pvMaster;
|
||||
StructureConstPtr structure;
|
||||
CopyNodePtr headNode;
|
||||
PVStructurePtr cacheInitStructure;
|
||||
PVCopy(PVStructurePtr const &pvMaster);
|
||||
friend class PVCopyMonitor;
|
||||
bool init(PVStructurePtr const &pvRequest);
|
||||
std::string dump(
|
||||
std::string const &value,
|
||||
CopyNodePtr const &node,
|
||||
int indentLevel);
|
||||
StructureConstPtr createStructure(
|
||||
PVStructurePtr const &pvMaster,
|
||||
PVStructurePtr const &pvFromRequest);
|
||||
CopyNodePtr createStructureNodes(
|
||||
PVStructurePtr const &pvMasterStructure,
|
||||
PVStructurePtr const &pvFromRequest,
|
||||
PVStructurePtr const &pvFromField);
|
||||
void updateStructureNodeSetBitSet(
|
||||
PVStructurePtr const &pvCopy,
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
BitSetPtr const &bitSet);
|
||||
void updateSubFieldSetBitSet(
|
||||
PVFieldPtr const &pvCopy,
|
||||
PVFieldPtr const &pvMaster,
|
||||
BitSetPtr const &bitSet);
|
||||
void updateStructureNodeFromBitSet(
|
||||
PVStructurePtr const &pvCopy,
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
BitSetPtr const &bitSet,
|
||||
bool toCopy,
|
||||
bool doAll);
|
||||
void updateSubFieldFromBitSet(
|
||||
PVFieldPtr const &pvCopy,
|
||||
PVFieldPtr const &pvMasterField,
|
||||
BitSetPtr const &bitSet,
|
||||
bool toCopy,
|
||||
bool doAll);
|
||||
CopyMasterNodePtr getCopyOffset(
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
PVFieldPtr const &masterPVField);
|
||||
CopyMasterNodePtr getMasterNode(
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
std::size_t structureOffset);
|
||||
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* PVCOPY_H */
|
||||
656
src/copy/pvCopy.cpp
Normal file
656
src/copy/pvCopy.cpp
Normal file
@@ -0,0 +1,656 @@
|
||||
/* pvCopy.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author Marty Kraimer
|
||||
* @date 2013.04
|
||||
*/
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include <epicsThread.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
|
||||
#include <pv/thread.h>
|
||||
|
||||
#include <pv/pvCopy.h>
|
||||
|
||||
using std::tr1::static_pointer_cast;
|
||||
using std::tr1::dynamic_pointer_cast;
|
||||
using std::string;
|
||||
using std::size_t;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
/**
|
||||
* Convenience method for implementing dump.
|
||||
* It generates a newline and inserts blanks at the beginning of the newline.
|
||||
* @param builder The std::string * being constructed.
|
||||
* @param indentLevel Indent level, Each level is four spaces.
|
||||
*/
|
||||
static void newLine(string *buffer, int indentLevel)
|
||||
{
|
||||
*buffer += "\n";
|
||||
*buffer += string(indentLevel*4, ' ');
|
||||
}
|
||||
|
||||
static PVCopyPtr NULLPVCopy;
|
||||
static FieldConstPtr NULLField;
|
||||
static StructureConstPtr NULLStructure;
|
||||
static PVStructurePtr NULLPVStructure;
|
||||
static CopyNodePtr NULLCopyNode;
|
||||
static CopyMasterNodePtr NULLCopyMasterNode;
|
||||
|
||||
struct CopyNode {
|
||||
CopyNode()
|
||||
: isStructure(false),
|
||||
structureOffset(0),
|
||||
nfields(0)
|
||||
{}
|
||||
bool isStructure;
|
||||
size_t structureOffset; // In the copy
|
||||
size_t nfields;
|
||||
PVStructurePtr options;
|
||||
};
|
||||
|
||||
struct CopyMasterNode : public CopyNode{
|
||||
PVFieldPtr masterPVField;
|
||||
};
|
||||
|
||||
typedef std::vector<CopyNodePtr> CopyNodePtrArray;
|
||||
typedef std::tr1::shared_ptr<CopyNodePtrArray> CopyNodePtrArrayPtr;
|
||||
|
||||
struct CopyStructureNode : public CopyNode {
|
||||
CopyNodePtrArrayPtr nodes;
|
||||
};
|
||||
|
||||
PVCopyPtr PVCopy::create(
|
||||
PVStructurePtr const &pvMaster,
|
||||
PVStructurePtr const &pvRequest,
|
||||
string const & structureName)
|
||||
{
|
||||
PVStructurePtr pvStructure(pvRequest);
|
||||
if(structureName.size()>0) {
|
||||
if(pvRequest->getStructure()->getNumberFields()>0) {
|
||||
pvStructure = pvRequest->getSubField<PVStructure>(structureName);
|
||||
if(!pvStructure) return NULLPVCopy;
|
||||
}
|
||||
} else if(pvStructure->getSubField<PVStructure>("field")) {
|
||||
pvStructure = pvRequest->getSubField<PVStructure>("field");
|
||||
}
|
||||
PVCopyPtr pvCopy = PVCopyPtr(new PVCopy(pvMaster));
|
||||
bool result = pvCopy->init(pvStructure);
|
||||
if(!result) pvCopy.reset();
|
||||
return pvCopy;
|
||||
}
|
||||
|
||||
PVCopy::PVCopy(
|
||||
PVStructurePtr const &pvMaster)
|
||||
: pvMaster(pvMaster)
|
||||
{
|
||||
}
|
||||
|
||||
void PVCopy::destroy()
|
||||
{
|
||||
headNode.reset();
|
||||
}
|
||||
|
||||
PVStructurePtr PVCopy::getPVMaster()
|
||||
{
|
||||
return pvMaster;
|
||||
}
|
||||
|
||||
void PVCopy::traverseMaster(CopyNodePtr const &innode, PVCopyTraverseMasterCallbackPtr const & callback)
|
||||
{
|
||||
CopyNodePtr node = innode;
|
||||
if(!node->isStructure) {
|
||||
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(node);
|
||||
callback->nextMasterPVField(masterNode->masterPVField);
|
||||
return;
|
||||
}
|
||||
CopyStructureNodePtr structNode = static_pointer_cast<CopyStructureNode>(node);
|
||||
CopyNodePtrArrayPtr nodes = structNode->nodes;
|
||||
for(size_t i=0; i< nodes->size(); i++) {
|
||||
node = (*nodes)[i];
|
||||
traverseMaster(node,callback);
|
||||
}
|
||||
}
|
||||
|
||||
StructureConstPtr PVCopy::getStructure()
|
||||
{
|
||||
return structure;
|
||||
}
|
||||
|
||||
PVStructurePtr PVCopy::createPVStructure()
|
||||
{
|
||||
if(cacheInitStructure) {
|
||||
PVStructurePtr save = cacheInitStructure;
|
||||
cacheInitStructure.reset();
|
||||
return save;
|
||||
}
|
||||
PVStructurePtr pvStructure =
|
||||
getPVDataCreate()->createPVStructure(structure);
|
||||
return pvStructure;
|
||||
}
|
||||
|
||||
PVStructurePtr PVCopy::getOptions(std::size_t fieldOffset)
|
||||
{
|
||||
if(fieldOffset==0) return headNode->options;
|
||||
CopyNodePtr node = headNode;
|
||||
while(true) {
|
||||
if(!node->isStructure) {
|
||||
if(node->structureOffset==fieldOffset) return node->options;
|
||||
return NULLPVStructure;
|
||||
}
|
||||
CopyStructureNodePtr structNode = static_pointer_cast<CopyStructureNode>(node);
|
||||
CopyNodePtrArrayPtr nodes = structNode->nodes;
|
||||
boolean okToContinue = false;
|
||||
for(size_t i=0; i< nodes->size(); i++) {
|
||||
node = (*nodes)[i];
|
||||
size_t soff = node->structureOffset;
|
||||
if(fieldOffset>=soff && fieldOffset<soff+node->nfields) {
|
||||
if(fieldOffset==soff) return node->options;
|
||||
if(!node->isStructure) {
|
||||
return NULLPVStructure;
|
||||
}
|
||||
okToContinue = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(okToContinue) continue;
|
||||
throw std::invalid_argument("fieldOffset not valid");
|
||||
}
|
||||
}
|
||||
|
||||
size_t PVCopy::getCopyOffset(PVFieldPtr const &masterPVField)
|
||||
{
|
||||
if(masterPVField->getFieldOffset()==0) return 0;
|
||||
if(!headNode->isStructure) {
|
||||
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(headNode);
|
||||
if((masterNode->masterPVField.get())==masterPVField.get()) {
|
||||
return headNode->structureOffset;
|
||||
}
|
||||
PVStructure * parent = masterPVField->getParent();
|
||||
size_t offsetParent = parent->getFieldOffset();
|
||||
size_t off = masterPVField->getFieldOffset();
|
||||
size_t offdiff = off -offsetParent;
|
||||
if(offdiff<masterNode->nfields) return headNode->structureOffset + offdiff;
|
||||
return string::npos;
|
||||
}
|
||||
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
|
||||
CopyMasterNodePtr masterNode = getCopyOffset(node,masterPVField);
|
||||
if(masterNode) return masterNode->structureOffset;
|
||||
return string::npos;
|
||||
}
|
||||
|
||||
size_t PVCopy::getCopyOffset(
|
||||
PVStructurePtr const &masterPVStructure,
|
||||
PVFieldPtr const &masterPVField)
|
||||
{
|
||||
CopyMasterNodePtr masterNode;
|
||||
if(!headNode->isStructure) {
|
||||
masterNode = static_pointer_cast<CopyMasterNode>(headNode);
|
||||
if(masterNode->masterPVField.get()!=masterPVStructure.get()) return string::npos;
|
||||
} else {
|
||||
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
|
||||
masterNode = getCopyOffset(node,masterPVField);
|
||||
}
|
||||
if(!masterNode) return string::npos;
|
||||
size_t diff = masterPVField->getFieldOffset()
|
||||
- masterPVStructure->getFieldOffset();
|
||||
return masterNode->structureOffset + diff;
|
||||
}
|
||||
|
||||
PVFieldPtr PVCopy::getMasterPVField(size_t structureOffset)
|
||||
{
|
||||
CopyMasterNodePtr masterNode;
|
||||
if(!headNode->isStructure) {
|
||||
masterNode = static_pointer_cast<CopyMasterNode>(headNode);
|
||||
} else {
|
||||
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
|
||||
masterNode = getMasterNode(node,structureOffset);
|
||||
}
|
||||
if(!masterNode) {
|
||||
throw std::invalid_argument(
|
||||
"PVCopy::getMasterPVField: setstructureOffset not valid");
|
||||
}
|
||||
size_t diff = structureOffset - masterNode->structureOffset;
|
||||
PVFieldPtr pvMasterField = masterNode->masterPVField;
|
||||
if(diff==0) return pvMasterField;
|
||||
PVStructurePtr pvStructure
|
||||
= static_pointer_cast<PVStructure>(pvMasterField);
|
||||
return pvStructure->getSubField(
|
||||
pvMasterField->getFieldOffset() + diff);
|
||||
}
|
||||
|
||||
void PVCopy::initCopy(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet)
|
||||
{
|
||||
bitSet->clear();
|
||||
bitSet->set(0);
|
||||
updateCopyFromBitSet(copyPVStructure,bitSet);
|
||||
}
|
||||
|
||||
void PVCopy::updateCopySetBitSet(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet)
|
||||
{
|
||||
if(headNode->isStructure) {
|
||||
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
|
||||
updateStructureNodeSetBitSet(copyPVStructure,node,bitSet);
|
||||
} else {
|
||||
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(headNode);
|
||||
PVFieldPtr pvMasterField= masterNode->masterPVField;
|
||||
PVFieldPtr copyPVField = copyPVStructure;
|
||||
PVFieldPtr pvField = pvMasterField;
|
||||
if(pvField->getField()->getType()==epics::pvData::structure) {
|
||||
updateSubFieldSetBitSet(copyPVField,pvMasterField,bitSet);
|
||||
return;
|
||||
}
|
||||
bool isEqual = (*copyPVField == *pvField);
|
||||
if(!isEqual) {
|
||||
copyPVField->copyUnchecked(*pvField);
|
||||
bitSet->set(copyPVField->getFieldOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PVCopy::updateCopyFromBitSet(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet)
|
||||
{
|
||||
bool doAll = bitSet->get(0);
|
||||
if(headNode->isStructure) {
|
||||
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
|
||||
updateStructureNodeFromBitSet(copyPVStructure,node,bitSet,true,doAll);
|
||||
} else {
|
||||
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(headNode);
|
||||
updateSubFieldFromBitSet(copyPVStructure, masterNode->masterPVField,bitSet, true,doAll);
|
||||
}
|
||||
}
|
||||
|
||||
void PVCopy::updateMaster(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet)
|
||||
{
|
||||
bool doAll = bitSet->get(0);
|
||||
if(headNode->isStructure) {
|
||||
CopyStructureNodePtr node =
|
||||
static_pointer_cast<CopyStructureNode>(headNode);
|
||||
updateStructureNodeFromBitSet(
|
||||
copyPVStructure,node,bitSet,false,doAll);
|
||||
} else {
|
||||
CopyMasterNodePtr masterNode =
|
||||
static_pointer_cast<CopyMasterNode>(headNode);
|
||||
updateSubFieldFromBitSet( copyPVStructure,masterNode->masterPVField,bitSet,false,doAll);
|
||||
}
|
||||
}
|
||||
|
||||
string PVCopy::dump()
|
||||
{
|
||||
string builder;
|
||||
dump(&builder,headNode,0);
|
||||
return builder;
|
||||
}
|
||||
|
||||
void PVCopy::dump(string *builder,CopyNodePtr const &node,int indentLevel)
|
||||
{
|
||||
newLine(builder,indentLevel);
|
||||
std::stringstream ss;
|
||||
ss << (node->isStructure ? "structureNode" : "masterNode");
|
||||
ss << " structureOffset " << node->structureOffset;
|
||||
ss << " nfields " << node->nfields;
|
||||
*builder += ss.str();
|
||||
PVStructurePtr options = node->options;
|
||||
if(options) {
|
||||
newLine(builder,indentLevel +1);
|
||||
|
||||
// TODO !!! ugly
|
||||
std::ostringstream oss;
|
||||
oss << *options;
|
||||
*builder += oss.str();
|
||||
|
||||
newLine(builder,indentLevel);
|
||||
}
|
||||
if(!node->isStructure) {
|
||||
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(node);
|
||||
string name = masterNode->masterPVField->getFullName();
|
||||
*builder += " masterField " + name;
|
||||
return;
|
||||
}
|
||||
CopyStructureNodePtr structureNode =
|
||||
static_pointer_cast<CopyStructureNode>(node);
|
||||
CopyNodePtrArrayPtr nodes = structureNode->nodes;
|
||||
for(size_t i=0; i<nodes->size(); ++i) {
|
||||
if((*nodes)[i].get()==NULL) {
|
||||
newLine(builder,indentLevel +1);
|
||||
ss.str("");
|
||||
ss << "node[" << i << "] is null";
|
||||
*builder += ss.str();
|
||||
continue;
|
||||
}
|
||||
dump(builder,(*nodes)[i],indentLevel+1);
|
||||
}
|
||||
}
|
||||
|
||||
bool PVCopy::init(epics::pvData::PVStructurePtr const &pvRequest)
|
||||
{
|
||||
PVStructurePtr pvMasterStructure = pvMaster;
|
||||
size_t len = pvRequest->getPVFields().size();
|
||||
bool entireMaster = false;
|
||||
if(len==string::npos) entireMaster = true;
|
||||
if(len==0) entireMaster = true;
|
||||
PVStructurePtr pvOptions;
|
||||
if(len==1) {
|
||||
pvOptions = pvRequest->getSubField<PVStructure>("_options");
|
||||
}
|
||||
if(entireMaster) {
|
||||
structure = pvMasterStructure->getStructure();
|
||||
CopyMasterNodePtr masterNode(new CopyMasterNode());
|
||||
headNode = masterNode;
|
||||
masterNode->options = pvOptions;
|
||||
masterNode->isStructure = false;
|
||||
masterNode->structureOffset = 0;
|
||||
masterNode->masterPVField = pvMasterStructure;
|
||||
masterNode->nfields = pvMasterStructure->getNumberFields();
|
||||
return true;
|
||||
}
|
||||
structure = createStructure(pvMasterStructure,pvRequest);
|
||||
if(!structure) return false;
|
||||
cacheInitStructure = createPVStructure();
|
||||
headNode = createStructureNodes(
|
||||
pvMaster,
|
||||
pvRequest,
|
||||
cacheInitStructure);
|
||||
return true;
|
||||
}
|
||||
|
||||
string PVCopy::dump(
|
||||
string const &value,
|
||||
CopyNodePtr const &node,
|
||||
int indentLevel)
|
||||
{
|
||||
throw std::logic_error(string("Not Implemented"));
|
||||
}
|
||||
|
||||
|
||||
StructureConstPtr PVCopy::createStructure(
|
||||
PVStructurePtr const &pvMaster,
|
||||
PVStructurePtr const &pvFromRequest)
|
||||
{
|
||||
if(pvFromRequest->getStructure()->getNumberFields()==0) {
|
||||
return pvMaster->getStructure();
|
||||
}
|
||||
PVFieldPtrArray const &pvFromRequestFields = pvFromRequest->getPVFields();
|
||||
StringArray const &fromRequestFieldNames = pvFromRequest->getStructure()->getFieldNames();
|
||||
size_t length = pvFromRequestFields.size();
|
||||
if(length==0) return NULLStructure;
|
||||
FieldConstPtrArray fields; fields.reserve(length);
|
||||
StringArray fieldNames; fields.reserve(length);
|
||||
for(size_t i=0; i<length; ++i) {
|
||||
string const &fieldName = fromRequestFieldNames[i];
|
||||
PVFieldPtr pvMasterField = pvMaster->getSubField(fieldName);
|
||||
if(!pvMasterField) continue;
|
||||
FieldConstPtr field = pvMasterField->getField();
|
||||
if(field->getType()==epics::pvData::structure) {
|
||||
PVStructurePtr pvRequestStructure = static_pointer_cast<PVStructure>(
|
||||
pvFromRequestFields[i]);
|
||||
if(pvRequestStructure->getNumberFields()>0) {
|
||||
StringArray const &names = pvRequestStructure->getStructure()->
|
||||
getFieldNames();
|
||||
size_t num = names.size();
|
||||
if(num>0 && names[0].compare("_options")==0) --num;
|
||||
if(num>0) {
|
||||
if(pvMasterField->getField()->getType()!=epics::pvData::structure) continue;
|
||||
fieldNames.push_back(fieldName);
|
||||
fields.push_back(createStructure(
|
||||
static_pointer_cast<PVStructure>(pvMasterField),
|
||||
pvRequestStructure));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
fieldNames.push_back(fieldName);
|
||||
fields.push_back(field);
|
||||
}
|
||||
size_t numsubfields = fields.size();
|
||||
if(numsubfields==0) return NULLStructure;
|
||||
return getFieldCreate()->createStructure(fieldNames, fields);
|
||||
}
|
||||
|
||||
CopyNodePtr PVCopy::createStructureNodes(
|
||||
PVStructurePtr const &pvMasterStructure,
|
||||
PVStructurePtr const &pvFromRequest,
|
||||
PVStructurePtr const &pvFromCopy)
|
||||
{
|
||||
PVFieldPtrArray const & copyPVFields = pvFromCopy->getPVFields();
|
||||
PVStructurePtr pvOptions;
|
||||
PVFieldPtr pvField = pvFromRequest->getSubField("_options");
|
||||
if(pvField) pvOptions = static_pointer_cast<PVStructure>(pvField);
|
||||
size_t number = copyPVFields.size();
|
||||
CopyNodePtrArrayPtr nodes(new CopyNodePtrArray());
|
||||
nodes->reserve(number);
|
||||
for(size_t i=0; i<number; i++) {
|
||||
PVFieldPtr copyPVField = copyPVFields[i];
|
||||
string fieldName = copyPVField->getFieldName();
|
||||
|
||||
PVStructurePtr requestPVStructure = pvFromRequest->getSubField<PVStructure>(fieldName);
|
||||
PVStructurePtr pvSubFieldOptions = requestPVStructure->getSubField<PVStructure>("_options");
|
||||
PVFieldPtr pvMasterField;
|
||||
PVFieldPtrArray const & pvMasterFields = pvMasterStructure->getPVFields();
|
||||
for(size_t j=0; i<pvMasterFields.size(); j++ ) {
|
||||
if(pvMasterFields[j]->getFieldName().compare(fieldName)==0) {
|
||||
pvMasterField = pvMasterFields[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
size_t numberRequest = requestPVStructure->getPVFields().size();
|
||||
if(pvSubFieldOptions) numberRequest--;
|
||||
if(numberRequest>0) {
|
||||
nodes->push_back(createStructureNodes(
|
||||
static_pointer_cast<PVStructure>(pvMasterField),
|
||||
requestPVStructure,
|
||||
static_pointer_cast<PVStructure>(copyPVField)));
|
||||
continue;
|
||||
}
|
||||
CopyMasterNodePtr masterNode(new CopyMasterNode());
|
||||
masterNode->options = pvSubFieldOptions;
|
||||
masterNode->isStructure = false;
|
||||
masterNode->masterPVField = pvMasterField;
|
||||
masterNode->nfields = copyPVField->getNumberFields();
|
||||
masterNode->structureOffset = copyPVField->getFieldOffset();
|
||||
nodes->push_back(masterNode);
|
||||
}
|
||||
CopyStructureNodePtr structureNode(new CopyStructureNode());
|
||||
structureNode->isStructure = true;
|
||||
structureNode->nodes = nodes;
|
||||
structureNode->structureOffset = pvFromCopy->getFieldOffset();
|
||||
structureNode->nfields = pvFromCopy->getNumberFields();
|
||||
structureNode->options = pvOptions;
|
||||
return structureNode;
|
||||
}
|
||||
|
||||
void PVCopy::updateStructureNodeSetBitSet(
|
||||
PVStructurePtr const &pvCopy,
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
epics::pvData::BitSetPtr const &bitSet)
|
||||
{
|
||||
for(size_t i=0; i<structureNode->nodes->size(); i++) {
|
||||
CopyNodePtr node = (*structureNode->nodes)[i];
|
||||
PVFieldPtr pvField = pvCopy->getSubField(node->structureOffset);
|
||||
if(node->isStructure) {
|
||||
PVStructurePtr xxx = static_pointer_cast<PVStructure>(pvField);
|
||||
CopyStructureNodePtr yyy =
|
||||
static_pointer_cast<CopyStructureNode>(node);
|
||||
updateStructureNodeSetBitSet(xxx,yyy,bitSet);
|
||||
} else {
|
||||
CopyMasterNodePtr masterNode =
|
||||
static_pointer_cast<CopyMasterNode>(node);
|
||||
updateSubFieldSetBitSet(pvField,masterNode->masterPVField,bitSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PVCopy::updateSubFieldSetBitSet(
|
||||
PVFieldPtr const &pvCopy,
|
||||
PVFieldPtr const &pvMaster,
|
||||
BitSetPtr const &bitSet)
|
||||
{
|
||||
FieldConstPtr field = pvCopy->getField();
|
||||
Type type = field->getType();
|
||||
if(type!=epics::pvData::structure) {
|
||||
bool isEqual = (*pvCopy == *pvMaster);
|
||||
if(isEqual) {
|
||||
if(type==structureArray) {
|
||||
// always act as though a change occurred.
|
||||
// Note that array elements are shared.
|
||||
bitSet->set(pvCopy->getFieldOffset());
|
||||
}
|
||||
}
|
||||
if(isEqual) return;
|
||||
pvCopy->copyUnchecked(*pvMaster);
|
||||
bitSet->set(pvCopy->getFieldOffset());
|
||||
return;
|
||||
}
|
||||
PVStructurePtr pvCopyStructure = static_pointer_cast<PVStructure>(pvCopy);
|
||||
PVFieldPtrArray const & pvCopyFields = pvCopyStructure->getPVFields();
|
||||
PVStructurePtr pvMasterStructure =
|
||||
static_pointer_cast<PVStructure>(pvMaster);
|
||||
PVFieldPtrArray const & pvMasterFields =
|
||||
pvMasterStructure->getPVFields();
|
||||
size_t length = pvCopyFields.size();
|
||||
for(size_t i=0; i<length; i++) {
|
||||
updateSubFieldSetBitSet(pvCopyFields[i],pvMasterFields[i],bitSet);
|
||||
}
|
||||
}
|
||||
|
||||
void PVCopy::updateStructureNodeFromBitSet(
|
||||
PVStructurePtr const &pvCopy,
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
BitSetPtr const &bitSet,
|
||||
bool toCopy,
|
||||
bool doAll)
|
||||
{
|
||||
size_t offset = structureNode->structureOffset;
|
||||
if(!doAll) {
|
||||
size_t nextSet = bitSet->nextSetBit(offset);
|
||||
if(nextSet==string::npos) return;
|
||||
}
|
||||
if(offset>=pvCopy->getNextFieldOffset()) return;
|
||||
if(!doAll) doAll = bitSet->get(offset);
|
||||
CopyNodePtrArrayPtr nodes = structureNode->nodes;
|
||||
for(size_t i=0; i<nodes->size(); i++) {
|
||||
CopyNodePtr node = (*nodes)[i];
|
||||
PVFieldPtr pvField = pvCopy->getSubFieldT(node->structureOffset);
|
||||
if(node->isStructure) {
|
||||
PVStructurePtr xxx = static_pointer_cast<PVStructure>(pvField);
|
||||
CopyStructureNodePtr subStructureNode =
|
||||
static_pointer_cast<CopyStructureNode>(node);
|
||||
updateStructureNodeFromBitSet(
|
||||
xxx,subStructureNode,bitSet,toCopy,doAll);
|
||||
} else {
|
||||
CopyMasterNodePtr masterNode =
|
||||
static_pointer_cast<CopyMasterNode>(node);
|
||||
updateSubFieldFromBitSet(
|
||||
pvField,masterNode->masterPVField,bitSet,toCopy,doAll);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PVCopy::updateSubFieldFromBitSet(
|
||||
PVFieldPtr const &pvCopy,
|
||||
PVFieldPtr const &pvMasterField,
|
||||
BitSetPtr const &bitSet,
|
||||
bool toCopy,
|
||||
bool doAll)
|
||||
{
|
||||
if(!doAll) {
|
||||
doAll = bitSet->get(pvCopy->getFieldOffset());
|
||||
}
|
||||
if(!doAll) {
|
||||
size_t offset = pvCopy->getFieldOffset();
|
||||
size_t nextSet = bitSet->nextSetBit(offset);
|
||||
if(nextSet==string::npos) return;
|
||||
if(nextSet>=pvCopy->getNextFieldOffset()) return;
|
||||
}
|
||||
if(pvCopy->getField()->getType()==epics::pvData::structure) {
|
||||
PVStructurePtr pvCopyStructure =
|
||||
static_pointer_cast<PVStructure>(pvCopy);
|
||||
PVFieldPtrArray const & pvCopyFields = pvCopyStructure->getPVFields();
|
||||
if(pvMasterField->getField()->getType() !=epics::pvData::structure)
|
||||
{
|
||||
throw std::logic_error(string("Logic error"));
|
||||
}
|
||||
PVStructurePtr pvMasterStructure =
|
||||
static_pointer_cast<PVStructure>(pvMasterField);
|
||||
PVFieldPtrArray const & pvMasterFields =
|
||||
pvMasterStructure->getPVFields();
|
||||
for(size_t i=0; i<pvCopyFields.size(); i++) {
|
||||
updateSubFieldFromBitSet(
|
||||
pvCopyFields[i],
|
||||
pvMasterFields[i],
|
||||
bitSet,toCopy,doAll);
|
||||
}
|
||||
} else {
|
||||
if(toCopy) {
|
||||
pvCopy->copyUnchecked(*pvMasterField);
|
||||
} else {
|
||||
pvMasterField->copyUnchecked(*pvCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CopyMasterNodePtr PVCopy::getCopyOffset(
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
PVFieldPtr const &masterPVField)
|
||||
{
|
||||
size_t offset = masterPVField->getFieldOffset();
|
||||
CopyNodePtrArrayPtr nodes = structureNode->nodes;
|
||||
for(size_t i=0; i< nodes->size(); i++) {
|
||||
CopyNodePtr node = (*nodes)[i];
|
||||
if(!node->isStructure) {
|
||||
CopyMasterNodePtr masterNode =
|
||||
static_pointer_cast<CopyMasterNode>(node);
|
||||
size_t off = masterNode->masterPVField->getFieldOffset();
|
||||
size_t nextOffset = masterNode->masterPVField->getNextFieldOffset();
|
||||
if(offset>= off && offset<nextOffset) return masterNode;
|
||||
} else {
|
||||
CopyStructureNodePtr subNode =
|
||||
static_pointer_cast<CopyStructureNode>(node);
|
||||
CopyMasterNodePtr masterNode =
|
||||
getCopyOffset(subNode,masterPVField);
|
||||
if(masterNode) return masterNode;
|
||||
}
|
||||
}
|
||||
return NULLCopyMasterNode;
|
||||
}
|
||||
|
||||
CopyMasterNodePtr PVCopy::getMasterNode(
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
std::size_t structureOffset)
|
||||
{
|
||||
CopyNodePtrArrayPtr nodes = structureNode->nodes;
|
||||
for(size_t i=0; i< nodes->size(); i++) {
|
||||
CopyNodePtr node = (*nodes)[i];
|
||||
if(structureOffset>=(node->structureOffset + node->nfields)) continue;
|
||||
if(!node->isStructure) {
|
||||
CopyMasterNodePtr masterNode =
|
||||
static_pointer_cast<CopyMasterNode>(node);
|
||||
return masterNode;
|
||||
}
|
||||
CopyStructureNodePtr subNode =
|
||||
static_pointer_cast<CopyStructureNode>(node);
|
||||
return getMasterNode(subNode,structureOffset);
|
||||
}
|
||||
return NULLCopyMasterNode;
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -1,328 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <epicsAssert.h>
|
||||
#include <epicsTypes.h>
|
||||
#include <epicsVersion.h>
|
||||
#include <epicsConvert.h>
|
||||
#include <epicsAssert.h>
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsGuard.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/createRequest.h>
|
||||
#include <pv/epicsException.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
// Our arbitrary limit on pvRequest structure depth to bound stack usage during recursion
|
||||
static const unsigned maxDepth = 5;
|
||||
|
||||
namespace epics{namespace pvData {
|
||||
|
||||
PVRequestMapper::PVRequestMapper() {}
|
||||
|
||||
PVRequestMapper::PVRequestMapper(const PVStructure &base,
|
||||
const PVStructure &pvRequest,
|
||||
mode_t mode)
|
||||
{
|
||||
compute(base, pvRequest, mode);
|
||||
}
|
||||
|
||||
PVStructurePtr PVRequestMapper::buildRequested() const
|
||||
{
|
||||
if(!typeRequested)
|
||||
THROW_EXCEPTION2(std::logic_error, "No mapping compute()d");
|
||||
return typeRequested->build();
|
||||
}
|
||||
|
||||
PVStructurePtr PVRequestMapper::buildBase() const
|
||||
{
|
||||
if(!typeBase)
|
||||
THROW_EXCEPTION2(std::logic_error, "No mapping compute()d");
|
||||
return typeBase->build();
|
||||
}
|
||||
|
||||
void PVRequestMapper::compute(const PVStructure &base,
|
||||
const PVStructure &pvRequest,
|
||||
mode_t mode)
|
||||
{
|
||||
if(base.getFieldOffset()!=0)
|
||||
THROW_EXCEPTION2(std::logic_error, "Mapper must be used with top level PVStructure");
|
||||
|
||||
bool ok = true;
|
||||
|
||||
// we want to be transactional, which requires a second copy of everything.
|
||||
PVRequestMapper temp;
|
||||
|
||||
// whether to preserve IDs of partial structures.
|
||||
bool keepids = false;
|
||||
PVScalar::const_shared_pointer pbp(pvRequest.getSubField<PVScalar>("record._options.keepIDs"));
|
||||
try {
|
||||
if(pbp) keepids = pbp->getAs<boolean>();
|
||||
}catch(std::runtime_error& e){
|
||||
std::ostringstream msg;
|
||||
msg<<"Can't parse keepIDs : '"<<e.what()<<"' ";
|
||||
temp.messages+=msg.str();
|
||||
}
|
||||
|
||||
PVStructure::const_shared_pointer fields(pvRequest.getSubField<PVStructure>("field"));
|
||||
if(!fields || fields->getPVFields().empty()) {
|
||||
// not selection, or empty selection, treated as select all
|
||||
temp.typeBase = temp.typeRequested = base.getStructure();
|
||||
|
||||
for(size_t i=1, N=base.getNextFieldOffset(); i<N; i++)
|
||||
temp.maskRequested.set(i);
|
||||
|
||||
} else {
|
||||
FieldBuilderPtr builder(getFieldCreate()->createFieldBuilder());
|
||||
|
||||
if(keepids)
|
||||
builder = builder->setId(base.getStructure()->getID());
|
||||
|
||||
ok &= temp._compute(base, *fields, builder, keepids, 0); // fills in builder
|
||||
|
||||
temp.typeBase = base.getStructure();
|
||||
temp.typeRequested = builder->createStructure();
|
||||
// possible that typeBase==typeRequested if all fields explicitly selected
|
||||
}
|
||||
|
||||
if(mode==Mask) {
|
||||
// short circuit use of masked Structure, but keep maskRequested
|
||||
temp.typeRequested = temp.typeBase;
|
||||
}
|
||||
|
||||
{
|
||||
PVStructurePtr proto(temp.typeRequested->build());
|
||||
|
||||
// base -> request may be sparce mapping
|
||||
temp.base2req.resize(base.getNextFieldOffset());
|
||||
// request -> base is dense mapping
|
||||
temp.req2base.resize(proto->getNextFieldOffset());
|
||||
|
||||
// special handling for whole structure mapping. in part because getSubField(0) isn't allowed
|
||||
temp.base2req[0] = Mapping(0, false);
|
||||
temp.req2base[0] = Mapping(0, false);
|
||||
|
||||
// Iterate prototype of requested to map with base field offsets.
|
||||
// which is handled as a special case below.
|
||||
// We also don't try to prevent redundant copies if both leaf and compress bits are set.
|
||||
for(size_t r=1, N=proto->getNextFieldOffset(); r<N; r++) {
|
||||
PVField::const_shared_pointer fld_req(proto->getSubFieldT(r)),
|
||||
fld_base(base.getSubFieldT(fld_req->getFullName()));
|
||||
const size_t b = fld_base->getFieldOffset();
|
||||
|
||||
if(!temp.requestedMask().get(b))
|
||||
continue;
|
||||
|
||||
bool leaf = fld_base->getField()->getType()!=structure;
|
||||
|
||||
// initialize mapping when our bit is set
|
||||
temp.base2req[b] = Mapping(r, leaf);
|
||||
temp.req2base[r] = Mapping(b, leaf);
|
||||
|
||||
// add ourself to all "compress" bit mappings of enclosing structures
|
||||
for(const PVStructure *parent = fld_req->getParent(); parent; parent = parent->getParent()) {
|
||||
temp.req2base[parent->getFieldOffset()].tomask .set(b);
|
||||
temp.req2base[parent->getFieldOffset()].frommask.set(r);
|
||||
}
|
||||
|
||||
for(const PVStructure *parent = fld_base->getParent(); parent; parent = parent->getParent()) {
|
||||
temp.base2req[parent->getFieldOffset()].tomask .set(r);
|
||||
temp.base2req[parent->getFieldOffset()].frommask.set(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
temp.maskRequested.set(0);
|
||||
|
||||
if(temp.maskRequested.nextSetBit(1)==-1) {
|
||||
ok = false;
|
||||
temp.messages+="Empty field selection";
|
||||
}
|
||||
|
||||
if(!ok)
|
||||
throw std::runtime_error(temp.messages);
|
||||
|
||||
swap(temp);
|
||||
}
|
||||
|
||||
bool PVRequestMapper::_compute(const PVStructure& base, const PVStructure& pvReq,
|
||||
FieldBuilderPtr& builder, bool keepids, unsigned depth)
|
||||
{
|
||||
bool ok = true;
|
||||
const StringArray& reqNames = pvReq.getStructure()->getFieldNames();
|
||||
|
||||
for(size_t i=0, N=reqNames.size(); i<N; i++) {
|
||||
// iterate through requested fields
|
||||
|
||||
PVField::const_shared_pointer subtype(base.getSubField(reqNames[i]));
|
||||
const FieldConstPtr& subReq = pvReq.getStructure()->getFields()[i];
|
||||
|
||||
if(subReq->getType()!=structure) {
|
||||
// pvRequest .field was not properly composed
|
||||
std::ostringstream msg;
|
||||
// not a great warning message as it doesn't distinguish 'a.value' from 'b.value',
|
||||
// but getFullName() whould prefix with 'field.', which would probably cause
|
||||
// more frequent confusion...
|
||||
msg<<"request invalid '"<<pvReq.getStructure()->getFieldNames()[i]<<"' ";
|
||||
messages+=msg.str();
|
||||
ok = false;
|
||||
|
||||
} else if(!subtype) {
|
||||
// requested field does not actually exist in base
|
||||
std::ostringstream msg;
|
||||
msg<<"No field '"<<pvReq.getStructure()->getFieldNames()[i]<<"' ";
|
||||
messages+=msg.str();
|
||||
|
||||
} else if(depth>=maxDepth // exceeds max recursion depth
|
||||
|| subtype->getField()->getType()!=structure // requested field is a leaf
|
||||
|| static_cast<const Structure&>(*subReq).getFieldNames().empty() // requests all sub-fields
|
||||
)
|
||||
{
|
||||
// just add the whole thing
|
||||
builder = builder->add(reqNames[i], subtype->getField());
|
||||
for(size_t j=subtype->getFieldOffset(), N=subtype->getNextFieldOffset(); j<N; j++)
|
||||
maskRequested.set(j);
|
||||
|
||||
if(subtype->getField()->getType()!=structure
|
||||
&& !static_cast<const Structure&>(*subReq).getFieldNames().empty())
|
||||
{
|
||||
// attempt to select below a leaf field
|
||||
std::ostringstream msg;
|
||||
msg<<"Leaf field '"<<pvReq.getFullName()<<"' ";
|
||||
messages+=msg.str();
|
||||
} else if(depth>=maxDepth) {
|
||||
std::ostringstream msg;
|
||||
msg<<"selection truncated at '"<<pvReq.getFullName()<<"' ";
|
||||
messages+=msg.str();
|
||||
}
|
||||
|
||||
} else {
|
||||
// recurse into sub-structure
|
||||
const PVStructure& substruct = static_cast<const PVStructure&>(*subtype);
|
||||
|
||||
builder = builder->addNestedStructure(reqNames[i]);
|
||||
maskRequested.set(substruct.getFieldOffset());
|
||||
|
||||
if(keepids)
|
||||
builder = builder->setId(substruct.getStructure()->getID());
|
||||
|
||||
_compute(substruct,
|
||||
static_cast<const PVStructure&>(*pvReq.getPVFields()[i]),
|
||||
builder, keepids, depth+1u);
|
||||
|
||||
builder = builder->endNested();
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
void PVRequestMapper::copyBaseToRequested(
|
||||
const PVStructure& base,
|
||||
const BitSet& baseMask,
|
||||
PVStructure& request,
|
||||
BitSet& requestMask
|
||||
) const {
|
||||
assert(base.getStructure()==typeBase);
|
||||
assert(request.getStructure()==typeRequested);
|
||||
_map(base, baseMask, request, requestMask, false);
|
||||
}
|
||||
|
||||
void PVRequestMapper::copyBaseFromRequested(
|
||||
PVStructure& base,
|
||||
BitSet& baseMask,
|
||||
const PVStructure& request,
|
||||
const BitSet& requestMask
|
||||
) const {
|
||||
assert(base.getStructure()==typeBase);
|
||||
assert(request.getStructure()==typeRequested);
|
||||
_map(request, requestMask, base, baseMask, true);
|
||||
}
|
||||
|
||||
void PVRequestMapper::_map(const PVStructure& src, const BitSet& maskSrc,
|
||||
PVStructure& dest, BitSet& maskDest,
|
||||
bool dir_r2b) const
|
||||
{
|
||||
{
|
||||
scratch = maskSrc;
|
||||
const mapping_t& map = dir_r2b ? req2base : base2req;
|
||||
|
||||
assert(map.size()==src.getNumberFields());
|
||||
|
||||
for(int32 i=scratch.nextSetBit(0), N=map.size(); i>=0 && i<N; i=scratch.nextSetBit(i+1)) {
|
||||
const Mapping& M = map[i];
|
||||
if(!M.valid) {
|
||||
assert(!dir_r2b); // only base -> requested mapping can have holes
|
||||
|
||||
} else if(M.leaf) {
|
||||
// just copy
|
||||
dest.getSubFieldT(M.to)->copy(*src.getSubFieldT(i));
|
||||
maskDest.set(M.to);
|
||||
|
||||
} else {
|
||||
// set bits of all sub-fields (in requested structure)
|
||||
// these indicies are always >i
|
||||
scratch |= M.frommask;
|
||||
|
||||
// we will also set the individual bits, but if a compress bit is set in the input,
|
||||
// then set the corresponding bit in the output.
|
||||
maskDest.set(M.to);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PVRequestMapper::_mapMask(const BitSet& maskSrc,
|
||||
BitSet& maskDest,
|
||||
bool dir_r2b) const
|
||||
{
|
||||
if(maskSrc.isEmpty()) {
|
||||
// no-op
|
||||
|
||||
} else {
|
||||
const mapping_t& map = dir_r2b ? req2base : base2req;
|
||||
|
||||
for(int32 i=maskSrc.nextSetBit(0), N=map.size(); i>=0 && i<N; i=maskSrc.nextSetBit(i+1)) {
|
||||
const Mapping& M = map[i];
|
||||
if(!M.valid) {
|
||||
assert(!dir_r2b); // only base -> requested mapping can have holes
|
||||
|
||||
} else {
|
||||
maskDest.set(M.to);
|
||||
|
||||
if(!M.leaf) {
|
||||
maskDest |= M.tomask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void PVRequestMapper::swap(PVRequestMapper& other)
|
||||
{
|
||||
typeBase.swap(other.typeBase);
|
||||
typeRequested.swap(other.typeRequested);
|
||||
maskRequested.swap(other.maskRequested);
|
||||
base2req.swap(other.base2req);
|
||||
req2base.swap(other.req2base);
|
||||
messages.swap(other.messages);
|
||||
scratch.swap(other.scratch); // paranoia
|
||||
}
|
||||
|
||||
void PVRequestMapper::reset()
|
||||
{
|
||||
typeBase.reset();
|
||||
typeRequested.reset();
|
||||
maskRequested.clear();
|
||||
base2req.clear();
|
||||
req2base.clear();
|
||||
messages.clear();
|
||||
scratch.clear(); // paranoia
|
||||
}
|
||||
|
||||
}} //namespace epics::pvData
|
||||
@@ -23,7 +23,7 @@ namespace epics { namespace pvData {
|
||||
* 1) same instance
|
||||
* 2) same type (field and scalar/element), same name, same subfields (if any)
|
||||
*/
|
||||
bool compare(const Field& a, const Field& b)
|
||||
bool operator==(const Field& a, const Field& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
@@ -33,53 +33,53 @@ bool compare(const Field& a, const Field& b)
|
||||
case scalar: {
|
||||
const Scalar &A=static_cast<const Scalar&>(a);
|
||||
const Scalar &B=static_cast<const Scalar&>(b);
|
||||
return compare(A, B);
|
||||
return A==B;
|
||||
}
|
||||
case scalarArray: {
|
||||
const ScalarArray &A=static_cast<const ScalarArray&>(a);
|
||||
const ScalarArray &B=static_cast<const ScalarArray&>(b);
|
||||
return compare(A, B);
|
||||
return A==B;
|
||||
}
|
||||
case structure: {
|
||||
const Structure &A=static_cast<const Structure&>(a);
|
||||
const Structure &B=static_cast<const Structure&>(b);
|
||||
return compare(A, B);
|
||||
return A==B;
|
||||
}
|
||||
case structureArray: {
|
||||
const StructureArray &A=static_cast<const StructureArray&>(a);
|
||||
const StructureArray &B=static_cast<const StructureArray&>(b);
|
||||
return compare(A, B);
|
||||
return A==B;
|
||||
}
|
||||
case union_: {
|
||||
const Union &A=static_cast<const Union&>(a);
|
||||
const Union &B=static_cast<const Union&>(b);
|
||||
return compare(A, B);
|
||||
return A==B;
|
||||
}
|
||||
case unionArray: {
|
||||
const UnionArray &A=static_cast<const UnionArray&>(a);
|
||||
const UnionArray &B=static_cast<const UnionArray&>(b);
|
||||
return compare(A, B);
|
||||
return A==B;
|
||||
}
|
||||
default:
|
||||
throw std::logic_error("Invalid Field type in comparison");
|
||||
}
|
||||
}
|
||||
|
||||
bool compare(const Scalar& a, const Scalar& b)
|
||||
bool operator==(const Scalar& a, const Scalar& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
return a.getScalarType()==b.getScalarType();
|
||||
}
|
||||
|
||||
bool compare(const ScalarArray& a, const ScalarArray& b)
|
||||
bool operator==(const ScalarArray& a, const ScalarArray& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
return a.getElementType()==b.getElementType();
|
||||
}
|
||||
|
||||
bool compare(const Structure& a, const Structure& b)
|
||||
bool operator==(const Structure& a, const Structure& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
@@ -101,12 +101,12 @@ bool compare(const Structure& a, const Structure& b)
|
||||
return std::equal( an.begin(), an.end(), bn.begin() );
|
||||
}
|
||||
|
||||
bool compare(const StructureArray& a, const StructureArray& b)
|
||||
bool operator==(const StructureArray& a, const StructureArray& b)
|
||||
{
|
||||
return *(a.getStructure().get())==*(b.getStructure().get());
|
||||
}
|
||||
|
||||
bool compare(const Union& a, const Union& b)
|
||||
bool operator==(const Union& a, const Union& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
@@ -128,12 +128,12 @@ bool compare(const Union& a, const Union& b)
|
||||
return std::equal( an.begin(), an.end(), bn.begin() );
|
||||
}
|
||||
|
||||
bool compare(const UnionArray& a, const UnionArray& b)
|
||||
bool operator==(const UnionArray& a, const UnionArray& b)
|
||||
{
|
||||
return *(a.getUnion().get())==*(b.getUnion().get());
|
||||
}
|
||||
|
||||
bool compare(const BoundedString& a, const BoundedString& b)
|
||||
bool operator==(const BoundedString& a, const BoundedString& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
@@ -271,7 +271,7 @@ bool compareField(const PVUnion* left, const PVUnion* right)
|
||||
|
||||
if (ls->isVariant())
|
||||
{
|
||||
const PVField::const_shared_pointer& lval = left->get();
|
||||
PVFieldPtr lval = left->get();
|
||||
if (lval.get() == 0)
|
||||
return right->get().get() == 0;
|
||||
else
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
* @author mrk
|
||||
*/
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
@@ -14,18 +18,13 @@
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
#include <epicsString.h>
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/reftrack.h>
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvIntrospect.h>
|
||||
#include <pv/factory.h>
|
||||
#include <pv/serializeHelper.h>
|
||||
#include <pv/thread.h>
|
||||
#include <pv/pvData.h>
|
||||
|
||||
using std::tr1::static_pointer_cast;
|
||||
using std::size_t;
|
||||
@@ -33,79 +32,16 @@ using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
size_t Field::num_instances;
|
||||
|
||||
|
||||
struct Field::Helper {
|
||||
static unsigned hash(Field *fld) {
|
||||
std::ostringstream key;
|
||||
// hash the output of operator<<()
|
||||
// not efficient, but stable within this process.
|
||||
key<<(*fld);
|
||||
unsigned H = epicsStrHash(key.str().c_str(), 0xbadc0de1);
|
||||
fld->m_hash = H;
|
||||
return H;
|
||||
}
|
||||
};
|
||||
|
||||
struct FieldCreate::Helper {
|
||||
template<typename FLD>
|
||||
static void cache(const FieldCreate *create, std::tr1::shared_ptr<FLD>& ent) {
|
||||
unsigned hash = Field::Helper::hash(ent.get());
|
||||
|
||||
Lock G(create->mutex);
|
||||
// we examine raw pointers stored in create->cache, which is safe under create->mutex
|
||||
|
||||
std::pair<cache_t::iterator, cache_t::iterator> itp(create->cache.equal_range(hash));
|
||||
for(; itp.first!=itp.second; ++itp.first) {
|
||||
Field* cent(itp.first->second);
|
||||
FLD* centx(dynamic_cast<FLD*>(cent));
|
||||
if(centx && compare(*centx, *ent)) {
|
||||
try{
|
||||
ent = std::tr1::static_pointer_cast<FLD>(cent->shared_from_this());
|
||||
return;
|
||||
}catch(std::tr1::bad_weak_ptr&){
|
||||
// we're racing destruction.
|
||||
// add a new entry.
|
||||
// Field::~Field is in the process of removing this old one.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
create->cache.insert(std::make_pair(hash, ent.get()));
|
||||
// cache cleaned from Field::~Field
|
||||
}
|
||||
};
|
||||
static DebugLevel debugLevel = lowDebug;
|
||||
|
||||
Field::Field(Type type)
|
||||
: m_fieldType(type)
|
||||
, m_hash(0)
|
||||
{
|
||||
REFTRACE_INCREMENT(num_instances);
|
||||
}
|
||||
|
||||
Field::~Field() {
|
||||
REFTRACE_DECREMENT(num_instances);
|
||||
FieldCreatePtr create(getFieldCreate());
|
||||
|
||||
Lock G(create->mutex);
|
||||
|
||||
std::pair<FieldCreate::cache_t::iterator, FieldCreate::cache_t::iterator> itp(create->cache.equal_range(m_hash));
|
||||
for(; itp.first!=itp.second; ++itp.first) {
|
||||
Field* cent(itp.first->second);
|
||||
if(cent==this) {
|
||||
create->cache.erase(itp.first);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<PVField> Field::build() const
|
||||
{
|
||||
FieldConstPtr self(shared_from_this());
|
||||
return getPVDataCreate()->createPVField(self);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& o, const Field& f)
|
||||
{
|
||||
@@ -176,11 +112,6 @@ void Scalar::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*contro
|
||||
}
|
||||
|
||||
|
||||
std::tr1::shared_ptr<PVScalar> Scalar::build() const
|
||||
{
|
||||
return getPVDataCreate()->createPVScalar(std::tr1::static_pointer_cast<const Scalar>(shared_from_this()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string BoundedString::getID() const
|
||||
@@ -347,11 +278,6 @@ void ScalarArray::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*c
|
||||
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<PVScalarArray> ScalarArray::build() const
|
||||
{
|
||||
return getPVDataCreate()->createPVScalarArray(std::tr1::static_pointer_cast<const ScalarArray>(shared_from_this()));
|
||||
}
|
||||
|
||||
|
||||
BoundedScalarArray::~BoundedScalarArray() {}
|
||||
|
||||
@@ -403,7 +329,9 @@ StructureArray::StructureArray(StructureConstPtr const & structure)
|
||||
{
|
||||
}
|
||||
|
||||
StructureArray::~StructureArray() {}
|
||||
StructureArray::~StructureArray() {
|
||||
if(debugLevel==highDebug) printf("~StructureArray\n");
|
||||
}
|
||||
|
||||
string StructureArray::getID() const
|
||||
{
|
||||
@@ -430,17 +358,14 @@ void StructureArray::deserialize(ByteBuffer* /*buffer*/, DeserializableControl*
|
||||
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<PVValueArray<std::tr1::shared_ptr<PVStructure> > > StructureArray::build() const
|
||||
{
|
||||
return getPVDataCreate()->createPVStructureArray(std::tr1::static_pointer_cast<const StructureArray>(shared_from_this()));
|
||||
}
|
||||
|
||||
UnionArray::UnionArray(UnionConstPtr const & _punion)
|
||||
: Array(unionArray),punion(_punion)
|
||||
{
|
||||
}
|
||||
|
||||
UnionArray::~UnionArray() {}
|
||||
UnionArray::~UnionArray() {
|
||||
if(debugLevel==highDebug) printf("~UnionArray\n");
|
||||
}
|
||||
|
||||
string UnionArray::getID() const
|
||||
{
|
||||
@@ -475,11 +400,6 @@ void UnionArray::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*co
|
||||
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<PVValueArray<std::tr1::shared_ptr<PVUnion> > > UnionArray::build() const
|
||||
{
|
||||
return getPVDataCreate()->createPVUnionArray(std::tr1::static_pointer_cast<const UnionArray>(shared_from_this()));
|
||||
}
|
||||
|
||||
const string Structure::DEFAULT_ID = Structure::defaultId();
|
||||
|
||||
const string & Structure::defaultId()
|
||||
@@ -533,10 +453,11 @@ string Structure::getID() const
|
||||
}
|
||||
|
||||
FieldConstPtr Structure::getField(string const & fieldName) const {
|
||||
for(size_t i=0, N=fields.size(); i<N; i++) {
|
||||
if(fieldName==fieldNames[i]) {
|
||||
return fields[i];
|
||||
}
|
||||
size_t numberFields = fields.size();
|
||||
for(size_t i=0; i<numberFields; i++) {
|
||||
FieldConstPtr pfield = fields[i];
|
||||
int result = fieldName.compare(fieldNames[i]);
|
||||
if(result==0) return pfield;
|
||||
}
|
||||
return FieldConstPtr();
|
||||
}
|
||||
@@ -551,21 +472,6 @@ size_t Structure::getFieldIndex(string const &fieldName) const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
FieldConstPtr Structure::getFieldImpl(string const & fieldName, bool throws) const {
|
||||
for(size_t i=0, N=fields.size(); i<N; i++)
|
||||
if(fieldName==fieldNames[i])
|
||||
return fields[i];
|
||||
|
||||
if (throws) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get field: "
|
||||
<< fieldName << " (not found)";
|
||||
throw std::runtime_error(ss.str());
|
||||
} else {
|
||||
return FieldConstPtr();
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& Structure::dump(std::ostream& o) const
|
||||
{
|
||||
o << format::indent() << getID() << std::endl;
|
||||
@@ -632,11 +538,6 @@ void Structure::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*con
|
||||
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<PVStructure> Structure::build() const
|
||||
{
|
||||
return getPVDataCreate()->createPVStructure(std::tr1::static_pointer_cast<const Structure>(shared_from_this()));
|
||||
}
|
||||
|
||||
const string Union::DEFAULT_ID = Union::defaultId();
|
||||
|
||||
const string & Union::defaultId()
|
||||
@@ -704,41 +605,6 @@ Union::Union (
|
||||
|
||||
Union::~Union() { }
|
||||
|
||||
int32 Union::guess(Type t, ScalarType s) const
|
||||
{
|
||||
if(t!=scalar && t!=scalarArray)
|
||||
THROW_EXCEPTION2(std::logic_error, "PVUnion::guess() only support scalar and scalarArray");
|
||||
|
||||
int32 ret = -1;
|
||||
for(size_t i=0, N=fields.size(); i<N; i++)
|
||||
{
|
||||
if(fields[i]->getType()!=t)
|
||||
continue;
|
||||
|
||||
ScalarType type;
|
||||
switch(fields[i]->getType()) {
|
||||
case scalar:
|
||||
type = static_cast<const Scalar*>(fields[i].get())->getScalarType();
|
||||
break;
|
||||
case scalarArray:
|
||||
type = static_cast<const ScalarArray*>(fields[i].get())->getElementType();
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
if(type==s) {
|
||||
// exact match
|
||||
ret = i;
|
||||
break; // we're done
|
||||
|
||||
} else if(ret==-1) {
|
||||
// first partial match
|
||||
ret = i;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
string Union::getID() const
|
||||
{
|
||||
@@ -765,21 +631,6 @@ size_t Union::getFieldIndex(string const &fieldName) const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
FieldConstPtr Union::getFieldImpl(string const & fieldName, bool throws) const {
|
||||
for(size_t i=0, N=fields.size(); i<N; i++)
|
||||
if(fieldName==fieldNames[i])
|
||||
return fields[i];
|
||||
|
||||
if (throws) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get field: "
|
||||
<< fieldName << " (not found)";
|
||||
throw std::runtime_error(ss.str());
|
||||
} else {
|
||||
return FieldConstPtr();
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& Union::dump(std::ostream& o) const
|
||||
{
|
||||
o << format::indent() << getID() << std::endl;
|
||||
@@ -850,102 +701,18 @@ void Union::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*control
|
||||
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<PVUnion> Union::build() const
|
||||
{
|
||||
return getPVDataCreate()->createPVUnion(std::tr1::static_pointer_cast<const Union>(shared_from_this()));
|
||||
}
|
||||
|
||||
FieldBuilder::FieldBuilder()
|
||||
:fieldCreate(getFieldCreate())
|
||||
,idSet(false)
|
||||
,nestedClassToBuild(structure)
|
||||
,nestedArray(false)
|
||||
,createNested(true)
|
||||
{}
|
||||
|
||||
FieldBuilder::FieldBuilder(const Structure* S)
|
||||
:fieldCreate(getFieldCreate())
|
||||
,id(S->getID())
|
||||
,idSet(!id.empty())
|
||||
,fieldNames(S->getFieldNames())
|
||||
,fields(S->getFields())
|
||||
,parentBuilder()
|
||||
,nestedClassToBuild(structure)
|
||||
,nestedName()
|
||||
,nestedArray(false)
|
||||
,createNested(false)
|
||||
{}
|
||||
|
||||
FieldBuilder::FieldBuilder(const FieldBuilderPtr & _parentBuilder,
|
||||
const std::string& name,
|
||||
const Structure* S)
|
||||
:fieldCreate(_parentBuilder->fieldCreate)
|
||||
,id(S->getID())
|
||||
,idSet(!id.empty())
|
||||
,fieldNames(S->getFieldNames())
|
||||
,fields(S->getFields())
|
||||
,parentBuilder(_parentBuilder)
|
||||
,nestedClassToBuild(structure)
|
||||
,nestedName(name)
|
||||
,nestedArray(false)
|
||||
,createNested(false)
|
||||
{}
|
||||
|
||||
FieldBuilder::FieldBuilder(const FieldBuilderPtr & _parentBuilder,
|
||||
const std::string& name,
|
||||
const StructureArray* S)
|
||||
:fieldCreate(getFieldCreate())
|
||||
,id(S->getStructure()->getID())
|
||||
,idSet(!id.empty())
|
||||
,fieldNames(S->getStructure()->getFieldNames())
|
||||
,fields(S->getStructure()->getFields())
|
||||
,parentBuilder(_parentBuilder)
|
||||
,nestedClassToBuild(structure)
|
||||
,nestedName(name)
|
||||
,nestedArray(true)
|
||||
,createNested(false)
|
||||
{}
|
||||
|
||||
FieldBuilder::FieldBuilder(const FieldBuilderPtr & _parentBuilder,
|
||||
const std::string& name,
|
||||
const Union* S)
|
||||
:fieldCreate(getFieldCreate())
|
||||
,id(S->getID())
|
||||
,idSet(!id.empty())
|
||||
,fieldNames(S->getFieldNames())
|
||||
,fields(S->getFields())
|
||||
,parentBuilder(_parentBuilder)
|
||||
,nestedClassToBuild(union_)
|
||||
,nestedName(name)
|
||||
,nestedArray(false)
|
||||
,createNested(false)
|
||||
{}
|
||||
|
||||
FieldBuilder::FieldBuilder(const FieldBuilderPtr & _parentBuilder,
|
||||
const std::string& name,
|
||||
const UnionArray* S)
|
||||
:fieldCreate(getFieldCreate())
|
||||
,id(S->getUnion()->getID())
|
||||
,idSet(!id.empty())
|
||||
,fieldNames(S->getUnion()->getFieldNames())
|
||||
,fields(S->getUnion()->getFields())
|
||||
,parentBuilder(_parentBuilder)
|
||||
,nestedClassToBuild(union_)
|
||||
,nestedName(name)
|
||||
,nestedArray(true)
|
||||
,createNested(false)
|
||||
{}
|
||||
FieldBuilder::FieldBuilder() : fieldCreate(getFieldCreate()), idSet(false) {}
|
||||
|
||||
FieldBuilder::FieldBuilder(FieldBuilderPtr const & _parentBuilder,
|
||||
string const & _nestedName,
|
||||
Type _nestedClassToBuild, bool _nestedArray)
|
||||
:fieldCreate(_parentBuilder->fieldCreate)
|
||||
,idSet(false)
|
||||
,parentBuilder(_parentBuilder)
|
||||
,nestedClassToBuild(_nestedClassToBuild)
|
||||
,nestedName(_nestedName)
|
||||
,nestedArray(_nestedArray)
|
||||
,createNested(true)
|
||||
Type _nestedClassToBuild, bool _nestedArray) :
|
||||
fieldCreate(getFieldCreate()),
|
||||
idSet(false),
|
||||
parentBuilder(_parentBuilder),
|
||||
nestedClassToBuild(_nestedClassToBuild),
|
||||
nestedName(_nestedName),
|
||||
nestedArray(_nestedArray)
|
||||
{}
|
||||
|
||||
void FieldBuilder::reset()
|
||||
@@ -956,19 +723,6 @@ void FieldBuilder::reset()
|
||||
fields.clear();
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::begin()
|
||||
{
|
||||
FieldBuilderPtr ret(new FieldBuilder);
|
||||
return ret;
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::begin(StructureConstPtr S)
|
||||
{
|
||||
FieldBuilderPtr ret(new FieldBuilder(S.get()));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
FieldBuilderPtr FieldBuilder::setId(string const & id)
|
||||
{
|
||||
this->id = id;
|
||||
@@ -978,57 +732,56 @@ FieldBuilderPtr FieldBuilder::setId(string const & id)
|
||||
|
||||
FieldBuilderPtr FieldBuilder::add(string const & name, ScalarType scalarType)
|
||||
{
|
||||
return add(name, fieldCreate->createScalar(scalarType));
|
||||
fields.push_back(fieldCreate->createScalar(scalarType)); fieldNames.push_back(name);
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::addBoundedString(std::string const & name, std::size_t maxLength)
|
||||
{
|
||||
return add(name, fieldCreate->createBoundedString(maxLength));
|
||||
fields.push_back(fieldCreate->createBoundedString(maxLength)); fieldNames.push_back(name);
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::add(string const & name, FieldConstPtr const & field)
|
||||
{
|
||||
const Field* cur = findField(name, field->getType());
|
||||
if(!cur) {
|
||||
fields.push_back(field); fieldNames.push_back(name);
|
||||
} else if(*cur!=*field) {
|
||||
THROW_EXCEPTION2(std::runtime_error, "duplicate field name w/ different type : "+name);
|
||||
} // else exact duplicate is silently ignored
|
||||
fields.push_back(field); fieldNames.push_back(name);
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::addArray(string const & name, ScalarType scalarType)
|
||||
{
|
||||
return add(name, fieldCreate->createScalarArray(scalarType));
|
||||
fields.push_back(fieldCreate->createScalarArray(scalarType)); fieldNames.push_back(name);
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::addFixedArray(string const & name, ScalarType scalarType, size_t size)
|
||||
{
|
||||
return add(name, fieldCreate->createFixedScalarArray(scalarType, size));
|
||||
fields.push_back(fieldCreate->createFixedScalarArray(scalarType, size)); fieldNames.push_back(name);
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::addBoundedArray(string const & name, ScalarType scalarType, size_t size)
|
||||
{
|
||||
return add(name, fieldCreate->createBoundedScalarArray(scalarType, size));
|
||||
fields.push_back(fieldCreate->createBoundedScalarArray(scalarType, size)); fieldNames.push_back(name);
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::addArray(string const & name, FieldConstPtr const & element)
|
||||
{
|
||||
FieldConstPtr fld;
|
||||
switch (element->getType())
|
||||
{
|
||||
case structure:
|
||||
fld = fieldCreate->createStructureArray(static_pointer_cast<const Structure>(element));
|
||||
fields.push_back(fieldCreate->createStructureArray(static_pointer_cast<const Structure>(element)));
|
||||
break;
|
||||
case union_:
|
||||
fld = fieldCreate->createUnionArray(static_pointer_cast<const Union>(element));
|
||||
fields.push_back(fieldCreate->createUnionArray(static_pointer_cast<const Union>(element)));
|
||||
break;
|
||||
case scalar:
|
||||
|
||||
if (std::tr1::dynamic_pointer_cast<const BoundedString>(element).get())
|
||||
THROW_EXCEPTION2(std::invalid_argument, "bounded string arrays are not supported");
|
||||
|
||||
fld = fieldCreate->createScalarArray(static_pointer_cast<const Scalar>(element)->getScalarType());
|
||||
fields.push_back(fieldCreate->createScalarArray(static_pointer_cast<const Scalar>(element)->getScalarType()));
|
||||
break;
|
||||
// scalarArray?
|
||||
default:
|
||||
@@ -1037,7 +790,8 @@ FieldBuilderPtr FieldBuilder::addArray(string const & name, FieldConstPtr const
|
||||
THROW_EXCEPTION2(std::invalid_argument, msg.str());
|
||||
}
|
||||
|
||||
return add(name, fld);
|
||||
fieldNames.push_back(name);
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
FieldConstPtr FieldBuilder::createFieldInternal(Type type)
|
||||
@@ -1087,111 +841,48 @@ UnionConstPtr FieldBuilder::createUnion()
|
||||
return field;
|
||||
}
|
||||
|
||||
const Field* FieldBuilder::findField(const std::string& name, Type ftype)
|
||||
{
|
||||
// linear search on the theory that the number of fields is small
|
||||
for(size_t i=0; i<fieldNames.size(); i++)
|
||||
{
|
||||
if(name!=fieldNames[i])
|
||||
continue;
|
||||
|
||||
if(fields[i]->getType()!=ftype)
|
||||
THROW_EXCEPTION2(std::invalid_argument, "nested field not required type: "+name);
|
||||
|
||||
return fields[i].get();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::addNestedStructure(string const & name)
|
||||
{
|
||||
const Field *cur = findField(name, structure);
|
||||
if(cur) {
|
||||
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name,
|
||||
static_cast<const Structure*>(cur)));
|
||||
}
|
||||
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, structure, false));
|
||||
}
|
||||
|
||||
|
||||
FieldBuilderPtr FieldBuilder::addNestedUnion(string const & name)
|
||||
{
|
||||
const Field *cur = findField(name, union_);
|
||||
if(cur) {
|
||||
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name,
|
||||
static_cast<const Union*>(cur)));
|
||||
}
|
||||
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, union_, false));
|
||||
}
|
||||
|
||||
|
||||
FieldBuilderPtr FieldBuilder::addNestedStructureArray(string const & name)
|
||||
{
|
||||
const Field *cur = findField(name, structureArray);
|
||||
if(cur) {
|
||||
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name,
|
||||
static_cast<const StructureArray*>(cur)));
|
||||
}
|
||||
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, structure, true));
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::addNestedUnionArray(string const & name)
|
||||
{
|
||||
const Field *cur = findField(name, unionArray);
|
||||
if(cur) {
|
||||
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name,
|
||||
static_cast<const UnionArray*>(cur)));
|
||||
}
|
||||
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, union_, true));
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::endNested()
|
||||
{
|
||||
if (!parentBuilder)
|
||||
THROW_EXCEPTION2(std::runtime_error, "FieldBuilder::endNested() can only be called to create nested fields");
|
||||
if (!parentBuilder.get())
|
||||
THROW_EXCEPTION2(std::runtime_error, "this method can only be called to create nested fields");
|
||||
|
||||
FieldConstPtr nestedField = createFieldInternal(nestedClassToBuild);
|
||||
|
||||
if(createNested) {
|
||||
if (nestedArray)
|
||||
parentBuilder->addArray(nestedName, nestedField);
|
||||
else
|
||||
parentBuilder->add(nestedName, nestedField);
|
||||
return parentBuilder;
|
||||
} else {
|
||||
for(size_t i=0, N = parentBuilder->fieldNames.size(); i<N; i++)
|
||||
{
|
||||
if(nestedName!=parentBuilder->fieldNames[i])
|
||||
continue;
|
||||
|
||||
if(nestedArray) {
|
||||
if(nestedClassToBuild==structure)
|
||||
parentBuilder->fields[i] = fieldCreate->createStructureArray(std::tr1::static_pointer_cast<const Structure>(nestedField));
|
||||
else if(nestedClassToBuild==union_)
|
||||
parentBuilder->fields[i] = fieldCreate->createUnionArray(std::tr1::static_pointer_cast<const Union>(nestedField));
|
||||
else
|
||||
throw std::logic_error("bad nested class");
|
||||
} else {
|
||||
parentBuilder->fields[i] = nestedField;
|
||||
}
|
||||
return parentBuilder;
|
||||
}
|
||||
// this only reached if bug in ctor
|
||||
THROW_EXCEPTION2(std::logic_error, "no nested field field?");
|
||||
}
|
||||
if (nestedArray)
|
||||
parentBuilder->addArray(nestedName, nestedField);
|
||||
else
|
||||
parentBuilder->add(nestedName, nestedField);
|
||||
|
||||
return parentBuilder;
|
||||
}
|
||||
|
||||
|
||||
FieldBuilderPtr FieldCreate::createFieldBuilder() const
|
||||
{
|
||||
return FieldBuilderPtr(new FieldBuilder());
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldCreate::createFieldBuilder(StructureConstPtr S) const
|
||||
{
|
||||
FieldBuilderPtr ret(new FieldBuilder(S.get()));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ScalarConstPtr FieldCreate::createScalar(ScalarType scalarType) const
|
||||
{
|
||||
if(scalarType<0 || scalarType>MAX_SCALAR_TYPE) {
|
||||
@@ -1205,9 +896,10 @@ ScalarConstPtr FieldCreate::createScalar(ScalarType scalarType) const
|
||||
|
||||
BoundedStringConstPtr FieldCreate::createBoundedString(std::size_t maxLength) const
|
||||
{
|
||||
std::tr1::shared_ptr<BoundedString> s(new BoundedString(maxLength));
|
||||
Helper::cache(this, s);
|
||||
return s;
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<BoundedString> s(new BoundedString(maxLength), Field::Deleter());
|
||||
BoundedStringConstPtr sa = s;
|
||||
return sa;
|
||||
}
|
||||
|
||||
ScalarArrayConstPtr FieldCreate::createScalarArray(ScalarType elementType) const
|
||||
@@ -1229,9 +921,10 @@ ScalarArrayConstPtr FieldCreate::createFixedScalarArray(ScalarType elementType,
|
||||
THROW_EXCEPTION2(std::invalid_argument, strm.str());
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<ScalarArray> s(new FixedScalarArray(elementType, size));
|
||||
Helper::cache(this, s);
|
||||
return s;
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<ScalarArray> s(new FixedScalarArray(elementType, size), Field::Deleter());
|
||||
ScalarArrayConstPtr sa = s;
|
||||
return sa;
|
||||
}
|
||||
|
||||
ScalarArrayConstPtr FieldCreate::createBoundedScalarArray(ScalarType elementType, size_t size) const
|
||||
@@ -1242,9 +935,10 @@ ScalarArrayConstPtr FieldCreate::createBoundedScalarArray(ScalarType elementType
|
||||
THROW_EXCEPTION2(std::invalid_argument, strm.str());
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<ScalarArray> s(new BoundedScalarArray(elementType, size));
|
||||
Helper::cache(this, s);
|
||||
return s;
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<ScalarArray> s(new BoundedScalarArray(elementType, size), Field::Deleter());
|
||||
ScalarArrayConstPtr sa = s;
|
||||
return sa;
|
||||
}
|
||||
|
||||
StructureConstPtr FieldCreate::createStructure () const
|
||||
@@ -1301,9 +995,10 @@ StructureConstPtr FieldCreate::createStructure (
|
||||
StringArray const & fieldNames,FieldConstPtrArray const & fields) const
|
||||
{
|
||||
validateFieldNames(fieldNames);
|
||||
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields), Field::Deleter());
|
||||
StructureConstPtr structure = sp;
|
||||
return structure;
|
||||
}
|
||||
|
||||
StructureConstPtr FieldCreate::createStructure (
|
||||
@@ -1312,26 +1007,29 @@ StructureConstPtr FieldCreate::createStructure (
|
||||
FieldConstPtrArray const & fields) const
|
||||
{
|
||||
validateFieldNames(fieldNames);
|
||||
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields,id));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields,id), Field::Deleter());
|
||||
StructureConstPtr structure = sp;
|
||||
return structure;
|
||||
}
|
||||
|
||||
StructureArrayConstPtr FieldCreate::createStructureArray(
|
||||
StructureConstPtr const & structure) const
|
||||
{
|
||||
std::tr1::shared_ptr<StructureArray> sp(new StructureArray(structure));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<StructureArray> sp(new StructureArray(structure), Field::Deleter());
|
||||
StructureArrayConstPtr structureArray = sp;
|
||||
return structureArray;
|
||||
}
|
||||
|
||||
UnionConstPtr FieldCreate::createUnion (
|
||||
StringArray const & fieldNames,FieldConstPtrArray const & fields) const
|
||||
{
|
||||
validateFieldNames(fieldNames);
|
||||
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields), Field::Deleter());
|
||||
UnionConstPtr punion = sp;
|
||||
return punion;
|
||||
}
|
||||
|
||||
UnionConstPtr FieldCreate::createUnion (
|
||||
@@ -1340,9 +1038,10 @@ UnionConstPtr FieldCreate::createUnion (
|
||||
FieldConstPtrArray const & fields) const
|
||||
{
|
||||
validateFieldNames(fieldNames);
|
||||
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields,id));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields,id), Field::Deleter());
|
||||
UnionConstPtr punion = sp;
|
||||
return punion;
|
||||
}
|
||||
|
||||
UnionConstPtr FieldCreate::createVariantUnion () const
|
||||
@@ -1353,9 +1052,10 @@ UnionConstPtr FieldCreate::createVariantUnion () const
|
||||
UnionArrayConstPtr FieldCreate::createUnionArray(
|
||||
UnionConstPtr const & punion) const
|
||||
{
|
||||
std::tr1::shared_ptr<UnionArray> sp(new UnionArray(punion));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<UnionArray> sp(new UnionArray(punion), Field::Deleter());
|
||||
UnionArrayConstPtr unionArray = sp;
|
||||
return unionArray;
|
||||
}
|
||||
|
||||
UnionArrayConstPtr FieldCreate::createVariantUnionArray () const
|
||||
@@ -1484,10 +1184,12 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
|
||||
|
||||
size_t size = SerializeHelper::readSize(buffer, control);
|
||||
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Field> sp(
|
||||
new BoundedString(size));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
new BoundedString(size),
|
||||
Field::Deleter());
|
||||
FieldConstPtr p = sp;
|
||||
return p;
|
||||
}
|
||||
else
|
||||
throw std::invalid_argument("invalid type encoding");
|
||||
@@ -1512,17 +1214,19 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
|
||||
{
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Field> sp(
|
||||
new FixedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
new FixedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size),
|
||||
Field::Deleter());
|
||||
FieldConstPtr p = sp;
|
||||
return p;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Field> sp(
|
||||
new BoundedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
new BoundedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size),
|
||||
Field::Deleter());
|
||||
FieldConstPtr p = sp;
|
||||
return p;
|
||||
}
|
||||
}
|
||||
else if (typeCode == 0x80)
|
||||
@@ -1534,9 +1238,9 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
|
||||
// Type type = Type.structureArray;
|
||||
StructureConstPtr elementStructure = std::tr1::static_pointer_cast<const Structure>(control->cachedDeserialize(buffer));
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Field> sp(new StructureArray(elementStructure));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
std::tr1::shared_ptr<Field> sp(new StructureArray(elementStructure), Field::Deleter());
|
||||
FieldConstPtr p = sp;
|
||||
return p;
|
||||
}
|
||||
else if (typeCode == 0x81)
|
||||
{
|
||||
@@ -1547,9 +1251,9 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
|
||||
// Type type = Type.unionArray;
|
||||
UnionConstPtr elementUnion = std::tr1::static_pointer_cast<const Union>(control->cachedDeserialize(buffer));
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Field> sp(new UnionArray(elementUnion));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
std::tr1::shared_ptr<Field> sp(new UnionArray(elementUnion), Field::Deleter());
|
||||
FieldConstPtr p = sp;
|
||||
return p;
|
||||
}
|
||||
else if (typeCode == 0x82)
|
||||
{
|
||||
@@ -1565,58 +1269,46 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
|
||||
}
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
struct field_factory {
|
||||
FieldCreatePtr fieldCreate;
|
||||
field_factory() :fieldCreate(new FieldCreate()) {
|
||||
registerRefCounter("Field", &Field::num_instances);
|
||||
registerRefCounter("Thread", &Thread::num_instances);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static detail::field_factory* field_factory_s;
|
||||
static epicsThreadOnceId field_factory_once = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
static void field_factory_init(void*)
|
||||
// TODO replace with non-locking singleton pattern
|
||||
FieldCreatePtr FieldCreate::getFieldCreate()
|
||||
{
|
||||
try {
|
||||
field_factory_s = new detail::field_factory;
|
||||
}catch(std::exception& e){
|
||||
std::cerr<<"Error initializing getFieldCreate() : "<<e.what()<<"\n";
|
||||
}
|
||||
}
|
||||
LOCAL_STATIC_LOCK;
|
||||
static FieldCreatePtr fieldCreate;
|
||||
static Mutex mutex;
|
||||
|
||||
const FieldCreatePtr& FieldCreate::getFieldCreate()
|
||||
{
|
||||
epicsThreadOnce(&field_factory_once, &field_factory_init, 0);
|
||||
if(!field_factory_s->fieldCreate)
|
||||
throw std::logic_error("getFieldCreate() not initialized");
|
||||
return field_factory_s->fieldCreate;
|
||||
Lock xx(mutex);
|
||||
if(fieldCreate.get()==0) fieldCreate = FieldCreatePtr(new FieldCreate());
|
||||
return fieldCreate;
|
||||
}
|
||||
|
||||
FieldCreate::FieldCreate()
|
||||
{
|
||||
for (int i = 0; i <= MAX_SCALAR_TYPE; i++)
|
||||
{
|
||||
std::tr1::shared_ptr<Scalar> sp(new Scalar(static_cast<ScalarType>(i)));
|
||||
Helper::cache(this, sp);
|
||||
scalars.push_back(sp);
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Scalar> sp(new Scalar(static_cast<ScalarType>(i)), Field::Deleter());
|
||||
ScalarConstPtr p = sp;
|
||||
scalars.push_back(p);
|
||||
|
||||
std::tr1::shared_ptr<ScalarArray> spa(new ScalarArray(static_cast<ScalarType>(i)));
|
||||
Helper::cache(this, spa);
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<ScalarArray> spa(new ScalarArray(static_cast<ScalarType>(i)), Field::Deleter());
|
||||
ScalarArrayConstPtr pa = spa;
|
||||
scalarArrays.push_back(spa);
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<Union> su(new Union());
|
||||
Helper::cache(this, su);
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Union> su(new Union(), Field::Deleter());
|
||||
variantUnion = su;
|
||||
|
||||
std::tr1::shared_ptr<UnionArray> sua(new UnionArray(variantUnion));
|
||||
Helper::cache(this, sua);
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<UnionArray> sua(new UnionArray(variantUnion), Field::Deleter());
|
||||
variantUnionArray = sua;
|
||||
}
|
||||
|
||||
FieldCreatePtr getFieldCreate() {
|
||||
return FieldCreate::getFieldCreate();
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
namespace std{
|
||||
|
||||
@@ -7,13 +7,16 @@
|
||||
* @author mrk
|
||||
*/
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/lock.h>
|
||||
@@ -21,7 +24,6 @@
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/factory.h>
|
||||
#include <pv/serializeHelper.h>
|
||||
#include <pv/reftrack.h>
|
||||
|
||||
using std::tr1::static_pointer_cast;
|
||||
using std::size_t;
|
||||
@@ -57,111 +59,135 @@ template<> const ScalarType PVFloatArray::typeCode = pvFloat;
|
||||
template<> const ScalarType PVDoubleArray::typeCode = pvDouble;
|
||||
template<> const ScalarType PVStringArray::typeCode = pvString;
|
||||
|
||||
/** Default storage for scalar values
|
||||
*/
|
||||
template<typename T>
|
||||
class BasePVScalar : public PVScalarValue<T> {
|
||||
public:
|
||||
typedef T value_type;
|
||||
typedef T* pointer;
|
||||
typedef const T* const_pointer;
|
||||
|
||||
BasePVScalar(ScalarConstPtr const & scalar);
|
||||
virtual ~BasePVScalar();
|
||||
virtual T get() const ;
|
||||
virtual void put(T val);
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const;
|
||||
virtual void deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pflusher);
|
||||
private:
|
||||
T value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
PVScalarValue<T>::~PVScalarValue() {}
|
||||
BasePVScalar<T>::BasePVScalar(ScalarConstPtr const & scalar)
|
||||
: PVScalarValue<T>(scalar),value(0)
|
||||
{}
|
||||
//Note: '0' is a suitable default for all POD types (not string)
|
||||
|
||||
template<typename T>
|
||||
std::ostream& PVScalarValue<T>::dumpValue(std::ostream& o) const
|
||||
BasePVScalar<T>::~BasePVScalar() {}
|
||||
|
||||
template<typename T>
|
||||
T BasePVScalar<T>::get() const { return value;}
|
||||
|
||||
template<typename T>
|
||||
void BasePVScalar<T>::put(T val)
|
||||
{
|
||||
return o << get();
|
||||
value = val;
|
||||
PVField::postPut();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::operator>>=(T& value) const
|
||||
{
|
||||
value = get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::operator<<=(typename storage_t::arg_type value)
|
||||
{
|
||||
put(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::assign(const PVScalar& scalar)
|
||||
{
|
||||
if(isImmutable())
|
||||
throw std::invalid_argument("destination is immutable");
|
||||
copyUnchecked(scalar);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::copy(const PVScalar& from)
|
||||
{
|
||||
assign(from);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::copyUnchecked(const PVScalar& from)
|
||||
{
|
||||
if(this==&from)
|
||||
return;
|
||||
T result;
|
||||
from.getAs((void*)&result, typeCode);
|
||||
put(result);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::serialize(ByteBuffer *pbuffer,
|
||||
void BasePVScalar<T>::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const {
|
||||
pflusher->ensureBuffer(sizeof(T));
|
||||
pbuffer->put(storage.value);
|
||||
}
|
||||
|
||||
template<>
|
||||
void PVScalarValue<std::string>::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const {
|
||||
SerializeHelper::serializeString(storage.value, pbuffer, pflusher);
|
||||
pbuffer->put(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::deserialize(ByteBuffer *pbuffer,
|
||||
void BasePVScalar<T>::deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pflusher)
|
||||
{
|
||||
pflusher->ensureData(sizeof(T));
|
||||
storage.value = pbuffer->GET(T);
|
||||
value = pbuffer->GET(T);
|
||||
}
|
||||
|
||||
template<>
|
||||
void PVScalarValue<std::string>::deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pflusher)
|
||||
{
|
||||
storage.value = SerializeHelper::deserializeString(pbuffer, pflusher);
|
||||
// TODO: check for violations of maxLength?
|
||||
}
|
||||
typedef BasePVScalar<boolean> BasePVBoolean;
|
||||
typedef BasePVScalar<int8> BasePVByte;
|
||||
typedef BasePVScalar<int16> BasePVShort;
|
||||
typedef BasePVScalar<int32> BasePVInt;
|
||||
typedef BasePVScalar<int64> BasePVLong;
|
||||
typedef BasePVScalar<uint8> BasePVUByte;
|
||||
typedef BasePVScalar<uint16> BasePVUShort;
|
||||
typedef BasePVScalar<uint32> BasePVUInt;
|
||||
typedef BasePVScalar<uint64> BasePVULong;
|
||||
typedef BasePVScalar<float> BasePVFloat;
|
||||
typedef BasePVScalar<double> BasePVDouble;
|
||||
|
||||
PVString::PVString(ScalarConstPtr const & scalar)
|
||||
: PVScalarValue<std::string>(scalar)
|
||||
// BasePVString is special case, since it implements SerializableArray
|
||||
class BasePVString : public PVString {
|
||||
public:
|
||||
typedef string value_type;
|
||||
typedef string* pointer;
|
||||
typedef const string* const_pointer;
|
||||
|
||||
BasePVString(ScalarConstPtr const & scalar);
|
||||
virtual ~BasePVString();
|
||||
virtual string get() const ;
|
||||
virtual void put(string val);
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const;
|
||||
virtual void deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pflusher);
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const;
|
||||
private:
|
||||
string value;
|
||||
std::size_t maxLength;
|
||||
};
|
||||
|
||||
BasePVString::BasePVString(ScalarConstPtr const & scalar)
|
||||
: PVString(scalar),value()
|
||||
{
|
||||
BoundedStringConstPtr boundedString = std::tr1::dynamic_pointer_cast<const BoundedString>(scalar);
|
||||
if (boundedString.get())
|
||||
storage.maxLength = boundedString->getMaximumLength();
|
||||
maxLength = boundedString->getMaximumLength();
|
||||
else
|
||||
storage.maxLength = 0;
|
||||
maxLength = 0;
|
||||
}
|
||||
|
||||
std::ostream& PVString::dumpValue(std::ostream& o) const
|
||||
BasePVString::~BasePVString() {}
|
||||
|
||||
string BasePVString::get() const { return value;}
|
||||
|
||||
void BasePVString::put(string val)
|
||||
{
|
||||
// we escape, but do not quote, for scalar string
|
||||
o<<escape(get());
|
||||
return o;
|
||||
if (maxLength > 0 && val.length() > maxLength)
|
||||
throw std::overflow_error("string too long");
|
||||
|
||||
value = val;
|
||||
postPut();
|
||||
}
|
||||
|
||||
/* mixing overrides (virtual functions) and overloads (different argument lists) is fun...
|
||||
* we override all overloads to avoid the "hides overloaded virtual function" warning from clang.
|
||||
* In this case we don't need/want to, so just delegate to the base class.
|
||||
*/
|
||||
void PVString::serialize(ByteBuffer *pbuffer,
|
||||
void BasePVString::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const
|
||||
{PVScalarValue<std::string>::serialize(pbuffer, pflusher);}
|
||||
{
|
||||
SerializeHelper::serializeString(value, pbuffer, pflusher);
|
||||
}
|
||||
|
||||
void PVString::serialize(ByteBuffer *pbuffer,
|
||||
void BasePVString::deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pflusher)
|
||||
{
|
||||
value = SerializeHelper::deserializeString(pbuffer, pflusher);
|
||||
}
|
||||
|
||||
void BasePVString::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const
|
||||
{
|
||||
// check bounds
|
||||
const size_t length = storage.value.length();
|
||||
const size_t length = /*(value == null) ? 0 :*/ value.length();
|
||||
/*if (offset < 0) offset = 0;
|
||||
else*/ if (offset > length) offset = length;
|
||||
//if (count < 0) count = length;
|
||||
@@ -171,10 +197,10 @@ void PVString::serialize(ByteBuffer *pbuffer,
|
||||
count = maxCount;
|
||||
|
||||
// write
|
||||
SerializeHelper::serializeSubstring(storage.value, offset, count, pbuffer, pflusher);
|
||||
SerializeHelper::serializeSubstring(value, offset, count, pbuffer, pflusher);
|
||||
}
|
||||
|
||||
void PVArray::checkLength(size_t len) const
|
||||
void PVArray::checkLength(size_t len)
|
||||
{
|
||||
Array::ArraySizeType type = getArray()->getArraySizeType();
|
||||
if (type != Array::variable)
|
||||
@@ -187,79 +213,61 @@ void PVArray::checkLength(size_t len) const
|
||||
}
|
||||
}
|
||||
|
||||
/** Default storage for arrays
|
||||
*/
|
||||
template<typename T>
|
||||
PVValueArray<T>::PVValueArray(ScalarArrayConstPtr const & scalarArray)
|
||||
:base_t(scalarArray)
|
||||
,value()
|
||||
class DefaultPVArray : public PVValueArray<T> {
|
||||
public:
|
||||
typedef T* pointer;
|
||||
typedef const T* const_pointer;
|
||||
typedef std::vector<T> vector;
|
||||
typedef const std::vector<T> const_vector;
|
||||
typedef std::tr1::shared_ptr<vector> shared_vector;
|
||||
|
||||
typedef ::epics::pvData::shared_vector<T> svector;
|
||||
typedef ::epics::pvData::shared_vector<const T> const_svector;
|
||||
|
||||
DefaultPVArray(ScalarArrayConstPtr const & scalarArray);
|
||||
virtual ~DefaultPVArray();
|
||||
|
||||
virtual size_t getLength() const {return value.size();}
|
||||
virtual size_t getCapacity() const {return value.capacity();}
|
||||
|
||||
virtual void setCapacity(size_t capacity);
|
||||
virtual void setLength(size_t length);
|
||||
|
||||
virtual const_svector view() const {return value;}
|
||||
virtual void swap(const_svector &other);
|
||||
virtual void replace(const const_svector& next);
|
||||
|
||||
// from Serializable
|
||||
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) const;
|
||||
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher);
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const;
|
||||
private:
|
||||
const_svector value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
DefaultPVArray<T>::DefaultPVArray(ScalarArrayConstPtr const & scalarArray)
|
||||
: PVValueArray<T>(scalarArray),
|
||||
value()
|
||||
|
||||
{}
|
||||
|
||||
PVValueArray<PVStructurePtr>::PVValueArray(StructureArrayConstPtr const & structureArray)
|
||||
:base_t(structureArray)
|
||||
,structureArray(structureArray)
|
||||
|
||||
{}
|
||||
|
||||
PVValueArray<PVUnionPtr>::PVValueArray(UnionArrayConstPtr const & unionArray)
|
||||
:base_t(unionArray)
|
||||
,unionArray(unionArray)
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
PVValueArray<T>::~PVValueArray() {}
|
||||
|
||||
template<typename T>
|
||||
ArrayConstPtr PVValueArray<T>::getArray() const
|
||||
{
|
||||
return std::tr1::static_pointer_cast<const Array>(this->getField());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::ostream& PVValueArray<T>::dumpValue(std::ostream& o) const
|
||||
{
|
||||
const_svector v(this->view());
|
||||
typename const_svector::const_iterator it(v.begin()),
|
||||
end(v.end());
|
||||
o << '[';
|
||||
if(it!=end) {
|
||||
o << print_cast(*it++);
|
||||
for(; it!=end; ++it)
|
||||
o << ',' << print_cast(*it);
|
||||
|
||||
ArrayConstPtr array = this->getArray();
|
||||
if (array->getArraySizeType() == Array::fixed)
|
||||
{
|
||||
// this->setLength(array->getMaximumCapacity());
|
||||
this->setCapacityMutable(false);
|
||||
}
|
||||
return o << ']';
|
||||
}
|
||||
|
||||
template<>
|
||||
std::ostream& PVValueArray<std::string>::dumpValue(std::ostream& o, size_t index) const
|
||||
{
|
||||
return o << '"' << escape(this->view().at(index)) << '"';
|
||||
}
|
||||
|
||||
template<>
|
||||
std::ostream& PVValueArray<std::string>::dumpValue(std::ostream& o) const
|
||||
{
|
||||
const_svector v(this->view());
|
||||
const_svector::const_iterator it(v.begin()),
|
||||
end(v.end());
|
||||
o << '[';
|
||||
if(it!=end) {
|
||||
o << '"' << escape(*it++) << '"';
|
||||
for(; it!=end; ++it)
|
||||
o << ", \"" << escape(*it) << '"';
|
||||
|
||||
}
|
||||
return o << ']';
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::ostream& PVValueArray<T>::dumpValue(std::ostream& o, size_t index) const
|
||||
{
|
||||
return o << print_cast(this->view().at(index));
|
||||
}
|
||||
|
||||
DefaultPVArray<T>::~DefaultPVArray()
|
||||
{ }
|
||||
template<typename T>
|
||||
void PVValueArray<T>::setCapacity(size_t capacity)
|
||||
void DefaultPVArray<T>::setCapacity(size_t capacity)
|
||||
{
|
||||
if(this->isCapacityMutable()) {
|
||||
this->checkLength(capacity);
|
||||
@@ -270,7 +278,7 @@ void PVValueArray<T>::setCapacity(size_t capacity)
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::setLength(size_t length)
|
||||
void DefaultPVArray<T>::setLength(size_t length)
|
||||
{
|
||||
if(this->isImmutable())
|
||||
THROW_EXCEPTION2(std::logic_error, "immutable");
|
||||
@@ -287,7 +295,7 @@ void PVValueArray<T>::setLength(size_t length)
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::replace(const const_svector& next)
|
||||
void DefaultPVArray<T>::replace(const const_svector& next)
|
||||
{
|
||||
this->checkLength(next.size());
|
||||
|
||||
@@ -296,7 +304,7 @@ void PVValueArray<T>::replace(const const_svector& next)
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::swap(const_svector &other)
|
||||
void DefaultPVArray<T>::swap(const_svector &other)
|
||||
{
|
||||
if (this->isImmutable())
|
||||
THROW_EXCEPTION2(std::logic_error, "immutable");
|
||||
@@ -308,13 +316,13 @@ void PVValueArray<T>::swap(const_svector &other)
|
||||
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::serialize(ByteBuffer *pbuffer,
|
||||
void DefaultPVArray<T>::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const {
|
||||
serialize(pbuffer, pflusher, 0, this->getLength());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::deserialize(ByteBuffer *pbuffer,
|
||||
void DefaultPVArray<T>::deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pcontrol) {
|
||||
|
||||
size_t size = this->getArray()->getArraySizeType() == Array::fixed ?
|
||||
@@ -363,7 +371,7 @@ void PVValueArray<T>::deserialize(ByteBuffer *pbuffer,
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::serialize(ByteBuffer *pbuffer,
|
||||
void DefaultPVArray<T>::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const
|
||||
{
|
||||
//TODO: avoid incrementing the ref counter...
|
||||
@@ -407,7 +415,7 @@ void PVValueArray<T>::serialize(ByteBuffer *pbuffer,
|
||||
// specializations for string
|
||||
|
||||
template<>
|
||||
void PVValueArray<string>::deserialize(ByteBuffer *pbuffer,
|
||||
void DefaultPVArray<string>::deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pcontrol) {
|
||||
|
||||
size_t size = this->getArray()->getArraySizeType() == Array::fixed ?
|
||||
@@ -434,7 +442,7 @@ void PVValueArray<string>::deserialize(ByteBuffer *pbuffer,
|
||||
}
|
||||
|
||||
template<>
|
||||
void PVValueArray<string>::serialize(ByteBuffer *pbuffer,
|
||||
void DefaultPVArray<string>::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const {
|
||||
|
||||
const_svector temp(value);
|
||||
@@ -450,17 +458,18 @@ void PVValueArray<string>::serialize(ByteBuffer *pbuffer,
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::_getAsVoid(epics::pvData::shared_vector<const void>& out) const
|
||||
{
|
||||
out = static_shared_vector_cast<const void>(this->view());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::_putFromVoid(const epics::pvData::shared_vector<const void>& in)
|
||||
{
|
||||
this->replace(shared_vector_convert<const T>(in));
|
||||
}
|
||||
typedef DefaultPVArray<boolean> DefaultPVBooleanArray;
|
||||
typedef DefaultPVArray<int8> BasePVByteArray;
|
||||
typedef DefaultPVArray<int16> BasePVShortArray;
|
||||
typedef DefaultPVArray<int32> BasePVIntArray;
|
||||
typedef DefaultPVArray<int64> BasePVLongArray;
|
||||
typedef DefaultPVArray<uint8> BasePVUByteArray;
|
||||
typedef DefaultPVArray<uint16> BasePVUShortArray;
|
||||
typedef DefaultPVArray<uint32> BasePVUIntArray;
|
||||
typedef DefaultPVArray<uint64> BasePVULongArray;
|
||||
typedef DefaultPVArray<float> BasePVFloatArray;
|
||||
typedef DefaultPVArray<double> BasePVDoubleArray;
|
||||
typedef DefaultPVArray<string> BasePVStringArray;
|
||||
|
||||
// Factory
|
||||
|
||||
@@ -555,29 +564,29 @@ PVScalarPtr PVDataCreate::createPVScalar(ScalarConstPtr const & scalar)
|
||||
ScalarType scalarType = scalar->getScalarType();
|
||||
switch(scalarType) {
|
||||
case pvBoolean:
|
||||
return PVScalarPtr(new PVBoolean(scalar));
|
||||
return PVScalarPtr(new BasePVBoolean(scalar));
|
||||
case pvByte:
|
||||
return PVScalarPtr(new PVByte(scalar));
|
||||
return PVScalarPtr(new BasePVByte(scalar));
|
||||
case pvShort:
|
||||
return PVScalarPtr(new PVShort(scalar));
|
||||
return PVScalarPtr(new BasePVShort(scalar));
|
||||
case pvInt:
|
||||
return PVScalarPtr(new PVInt(scalar));
|
||||
return PVScalarPtr(new BasePVInt(scalar));
|
||||
case pvLong:
|
||||
return PVScalarPtr(new PVLong(scalar));
|
||||
return PVScalarPtr(new BasePVLong(scalar));
|
||||
case pvUByte:
|
||||
return PVScalarPtr(new PVUByte(scalar));
|
||||
return PVScalarPtr(new BasePVUByte(scalar));
|
||||
case pvUShort:
|
||||
return PVScalarPtr(new PVUShort(scalar));
|
||||
return PVScalarPtr(new BasePVUShort(scalar));
|
||||
case pvUInt:
|
||||
return PVScalarPtr(new PVUInt(scalar));
|
||||
return PVScalarPtr(new BasePVUInt(scalar));
|
||||
case pvULong:
|
||||
return PVScalarPtr(new PVULong(scalar));
|
||||
return PVScalarPtr(new BasePVULong(scalar));
|
||||
case pvFloat:
|
||||
return PVScalarPtr(new PVFloat(scalar));
|
||||
return PVScalarPtr(new BasePVFloat(scalar));
|
||||
case pvDouble:
|
||||
return PVScalarPtr(new PVDouble(scalar));
|
||||
return PVScalarPtr(new BasePVDouble(scalar));
|
||||
case pvString:
|
||||
return PVScalarPtr(new PVString(scalar));
|
||||
return PVScalarPtr(new BasePVString(scalar));
|
||||
}
|
||||
throw std::logic_error("PVDataCreate::createPVScalar should never get here");
|
||||
}
|
||||
@@ -602,29 +611,29 @@ PVScalarArrayPtr PVDataCreate::createPVScalarArray(
|
||||
{
|
||||
switch(scalarArray->getElementType()) {
|
||||
case pvBoolean:
|
||||
return PVScalarArrayPtr(new PVBooleanArray(scalarArray));
|
||||
return PVScalarArrayPtr(new DefaultPVBooleanArray(scalarArray));
|
||||
case pvByte:
|
||||
return PVScalarArrayPtr(new PVByteArray(scalarArray));
|
||||
return PVScalarArrayPtr(new BasePVByteArray(scalarArray));
|
||||
case pvShort:
|
||||
return PVScalarArrayPtr(new PVShortArray(scalarArray));
|
||||
return PVScalarArrayPtr(new BasePVShortArray(scalarArray));
|
||||
case pvInt:
|
||||
return PVScalarArrayPtr(new PVIntArray(scalarArray));
|
||||
return PVScalarArrayPtr(new BasePVIntArray(scalarArray));
|
||||
case pvLong:
|
||||
return PVScalarArrayPtr(new PVLongArray(scalarArray));
|
||||
return PVScalarArrayPtr(new BasePVLongArray(scalarArray));
|
||||
case pvUByte:
|
||||
return PVScalarArrayPtr(new PVUByteArray(scalarArray));
|
||||
return PVScalarArrayPtr(new BasePVUByteArray(scalarArray));
|
||||
case pvUShort:
|
||||
return PVScalarArrayPtr(new PVUShortArray(scalarArray));
|
||||
return PVScalarArrayPtr(new BasePVUShortArray(scalarArray));
|
||||
case pvUInt:
|
||||
return PVScalarArrayPtr(new PVUIntArray(scalarArray));
|
||||
return PVScalarArrayPtr(new BasePVUIntArray(scalarArray));
|
||||
case pvULong:
|
||||
return PVScalarArrayPtr(new PVULongArray(scalarArray));
|
||||
return PVScalarArrayPtr(new BasePVULongArray(scalarArray));
|
||||
case pvFloat:
|
||||
return PVScalarArrayPtr(new PVFloatArray(scalarArray));
|
||||
return PVScalarArrayPtr(new BasePVFloatArray(scalarArray));
|
||||
case pvDouble:
|
||||
return PVScalarArrayPtr(new PVDoubleArray(scalarArray));
|
||||
return PVScalarArrayPtr(new BasePVDoubleArray(scalarArray));
|
||||
case pvString:
|
||||
return PVScalarArrayPtr(new PVStringArray(scalarArray));
|
||||
return PVScalarArrayPtr(new BasePVStringArray(scalarArray));
|
||||
}
|
||||
throw std::logic_error("PVDataCreate::createPVScalarArray should never get here");
|
||||
|
||||
@@ -715,63 +724,23 @@ PVUnionPtr PVDataCreate::createPVUnion(PVUnionPtr const & unionToClone)
|
||||
return punion;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
struct pvfield_factory {
|
||||
PVDataCreatePtr pvDataCreate;
|
||||
pvfield_factory() :pvDataCreate(new PVDataCreate()) {
|
||||
registerRefCounter("PVField", &PVField::num_instances);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static detail::pvfield_factory* pvfield_factory_s;
|
||||
static epicsThreadOnceId pvfield_factory_once = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
static void pvfield_factory_init(void*)
|
||||
// TODO not thread-safe (local static initializers)
|
||||
// TODO replace with non-locking singleton pattern
|
||||
PVDataCreatePtr PVDataCreate::getPVDataCreate()
|
||||
{
|
||||
try {
|
||||
pvfield_factory_s = new detail::pvfield_factory;
|
||||
}catch(std::exception& e){
|
||||
std::cerr<<"Error initializing getFieldCreate() : "<<e.what()<<"\n";
|
||||
}
|
||||
static PVDataCreatePtr pvDataCreate;
|
||||
static Mutex mutex;
|
||||
Lock xx(mutex);
|
||||
|
||||
if(pvDataCreate.get()==0) pvDataCreate = PVDataCreatePtr(new PVDataCreate());
|
||||
return pvDataCreate;
|
||||
}
|
||||
|
||||
const PVDataCreatePtr& PVDataCreate::getPVDataCreate()
|
||||
{
|
||||
epicsThreadOnce(&pvfield_factory_once, &pvfield_factory_init, 0);
|
||||
if(!pvfield_factory_s->pvDataCreate)
|
||||
throw std::logic_error("getPVDataCreate() not initialized");
|
||||
return pvfield_factory_s->pvDataCreate;
|
||||
PVDataCreatePtr getPVDataCreate() {
|
||||
return PVDataCreate::getPVDataCreate();
|
||||
}
|
||||
|
||||
// explicitly instanciate to ensure that windows
|
||||
// builds emit exported symbols for inline'd methods
|
||||
template class PVScalarValue<boolean>;
|
||||
template class PVScalarValue<int8>;
|
||||
template class PVScalarValue<uint8>;
|
||||
template class PVScalarValue<int16>;
|
||||
template class PVScalarValue<uint16>;
|
||||
template class PVScalarValue<int32>;
|
||||
template class PVScalarValue<uint32>;
|
||||
template class PVScalarValue<int64>;
|
||||
template class PVScalarValue<uint64>;
|
||||
template class PVScalarValue<float>;
|
||||
template class PVScalarValue<double>;
|
||||
template class PVScalarValue<std::string>;
|
||||
template class PVValueArray<boolean>;
|
||||
template class PVValueArray<int8>;
|
||||
template class PVValueArray<uint8>;
|
||||
template class PVValueArray<int16>;
|
||||
template class PVValueArray<uint16>;
|
||||
template class PVValueArray<int32>;
|
||||
template class PVValueArray<uint32>;
|
||||
template class PVValueArray<int64>;
|
||||
template class PVValueArray<uint64>;
|
||||
template class PVValueArray<float>;
|
||||
template class PVValueArray<double>;
|
||||
template class PVValueArray<std::string>;
|
||||
|
||||
}} // namespace epics::pvData
|
||||
}}
|
||||
|
||||
namespace std{
|
||||
std::ostream& operator<<(std::ostream& o, const epics::pvData::PVField *ptr)
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/factory.h>
|
||||
#include <pv/reftrack.h>
|
||||
|
||||
using std::tr1::const_pointer_cast;
|
||||
using std::size_t;
|
||||
@@ -25,20 +24,15 @@ using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
size_t PVField::num_instances;
|
||||
|
||||
PVField::PVField(FieldConstPtr field)
|
||||
: parent(NULL),field(field),
|
||||
fieldOffset(0), nextFieldOffset(0),
|
||||
immutable(false)
|
||||
{
|
||||
REFTRACE_INCREMENT(num_instances);
|
||||
}
|
||||
|
||||
PVField::~PVField()
|
||||
{
|
||||
REFTRACE_DECREMENT(num_instances);
|
||||
}
|
||||
{ }
|
||||
|
||||
|
||||
size_t PVField::getFieldOffset() const
|
||||
@@ -60,16 +54,22 @@ size_t PVField::getNumberFields() const
|
||||
}
|
||||
|
||||
|
||||
bool PVField::isImmutable() const {return immutable;}
|
||||
|
||||
void PVField::setImmutable() {immutable = true;}
|
||||
|
||||
const FieldConstPtr & PVField::getField() const {return field;}
|
||||
|
||||
PVStructure *PVField::getParent() const {return parent;}
|
||||
|
||||
void PVField::postPut()
|
||||
{
|
||||
if(postHandler) postHandler->postPut();
|
||||
if(postHandler.get()!=NULL) postHandler->postPut();
|
||||
}
|
||||
|
||||
void PVField::setPostHandler(PostHandlerPtr const &handler)
|
||||
{
|
||||
if(postHandler) {
|
||||
if(postHandler.get()!=NULL) {
|
||||
if(postHandler.get()==handler.get()) return;
|
||||
throw std::logic_error(
|
||||
"PVField::setPostHandler a postHandler is already registered");
|
||||
@@ -97,7 +97,7 @@ std::ostream& operator<<(std::ostream& o, const PVField& f)
|
||||
string PVField::getFullName() const
|
||||
{
|
||||
string ret(fieldName);
|
||||
for(const PVField *fld=getParent(); fld; fld=fld->getParent())
|
||||
for(PVField *fld=getParent(); fld; fld=fld->getParent())
|
||||
{
|
||||
if(fld->getFieldName().size()==0) break;
|
||||
ret = fld->getFieldName() + '.' + ret;
|
||||
@@ -184,16 +184,62 @@ void PVField::copy(const PVField& from)
|
||||
if(isImmutable())
|
||||
throw std::invalid_argument("destination is immutable");
|
||||
|
||||
if (getField() != from.getField())
|
||||
if (getField()->getType() != from.getField()->getType())
|
||||
throw std::invalid_argument("field types do not match");
|
||||
|
||||
copyUnchecked(from);
|
||||
switch(getField()->getType())
|
||||
{
|
||||
case scalar:
|
||||
{
|
||||
const PVScalar* fromS = static_cast<const PVScalar*>(&from);
|
||||
PVScalar* toS = static_cast<PVScalar*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
case scalarArray:
|
||||
{
|
||||
const PVScalarArray* fromS = static_cast<const PVScalarArray*>(&from);
|
||||
PVScalarArray* toS = static_cast<PVScalarArray*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
case structure:
|
||||
{
|
||||
const PVStructure* fromS = static_cast<const PVStructure*>(&from);
|
||||
PVStructure* toS = static_cast<PVStructure*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
case structureArray:
|
||||
{
|
||||
const PVStructureArray* fromS = static_cast<const PVStructureArray*>(&from);
|
||||
PVStructureArray* toS = static_cast<PVStructureArray*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
case union_:
|
||||
{
|
||||
const PVUnion* fromS = static_cast<const PVUnion*>(&from);
|
||||
PVUnion* toS = static_cast<PVUnion*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
case unionArray:
|
||||
{
|
||||
const PVUnionArray* fromS = static_cast<const PVUnionArray*>(&from);
|
||||
PVUnionArray* toS = static_cast<PVUnionArray*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw std::logic_error("PVField::copy unknown type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PVField::copyUnchecked(const PVField& from)
|
||||
{
|
||||
assert(getField()==from.getField());
|
||||
|
||||
switch(getField()->getType())
|
||||
{
|
||||
case scalar:
|
||||
|
||||
@@ -24,6 +24,25 @@ using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
PVFieldPtr PVStructure::nullPVField;
|
||||
PVBooleanPtr PVStructure::nullPVBoolean;
|
||||
PVBytePtr PVStructure::nullPVByte;
|
||||
PVShortPtr PVStructure::nullPVShort;
|
||||
PVIntPtr PVStructure::nullPVInt;
|
||||
PVLongPtr PVStructure::nullPVLong;
|
||||
PVUBytePtr PVStructure::nullPVUByte;
|
||||
PVUShortPtr PVStructure::nullPVUShort;
|
||||
PVUIntPtr PVStructure::nullPVUInt;
|
||||
PVULongPtr PVStructure::nullPVULong;
|
||||
PVFloatPtr PVStructure::nullPVFloat;
|
||||
PVDoublePtr PVStructure::nullPVDouble;
|
||||
PVStringPtr PVStructure::nullPVString;
|
||||
PVStructurePtr PVStructure::nullPVStructure;
|
||||
PVStructureArrayPtr PVStructure::nullPVStructureArray;
|
||||
PVUnionPtr PVStructure::nullPVUnion;
|
||||
PVUnionArrayPtr PVStructure::nullPVUnionArray;
|
||||
PVScalarArrayPtr PVStructure::nullPVScalarArray;
|
||||
|
||||
PVStructure::PVStructure(StructureConstPtr const & structurePtr)
|
||||
: PVField(structurePtr),
|
||||
structurePtr(structurePtr),
|
||||
@@ -60,7 +79,9 @@ PVStructure::PVStructure(StructureConstPtr const & structurePtr,
|
||||
}
|
||||
}
|
||||
|
||||
PVStructure::~PVStructure() {}
|
||||
PVStructure::~PVStructure()
|
||||
{
|
||||
}
|
||||
|
||||
void PVStructure::setImmutable()
|
||||
{
|
||||
@@ -72,42 +93,60 @@ void PVStructure::setImmutable()
|
||||
PVField::setImmutable();
|
||||
}
|
||||
|
||||
PVFieldPtr PVStructure::getSubFieldImpl(size_t fieldOffset, bool throws) const
|
||||
StructureConstPtr PVStructure::getStructure() const
|
||||
{
|
||||
const PVStructure *current = this;
|
||||
return structurePtr;
|
||||
}
|
||||
|
||||
recurse:
|
||||
// we don't permit self lookup
|
||||
if(fieldOffset<=current->getFieldOffset() || fieldOffset>=current->getNextFieldOffset()) {
|
||||
if(throws) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get field with offset "
|
||||
<< fieldOffset << " (Invalid offset)" ;
|
||||
throw std::runtime_error(ss.str());
|
||||
} else {
|
||||
return PVFieldPtr();
|
||||
const PVFieldPtrArray & PVStructure::getPVFields() const
|
||||
{
|
||||
return pvFields;
|
||||
}
|
||||
|
||||
PVFieldPtr PVStructure::getSubField(const char * fieldName) const
|
||||
{
|
||||
PVField * field = getSubFieldImpl(fieldName, false);
|
||||
if (field)
|
||||
return field->shared_from_this();
|
||||
else
|
||||
return PVFieldPtr();
|
||||
}
|
||||
|
||||
|
||||
PVFieldPtr PVStructure::getSubField(size_t fieldOffset) const
|
||||
{
|
||||
if(fieldOffset<=getFieldOffset()) {
|
||||
return nullPVField;
|
||||
}
|
||||
if(fieldOffset>getNextFieldOffset()) return nullPVField;
|
||||
size_t numFields = pvFields.size();
|
||||
for(size_t i=0; i<numFields; i++) {
|
||||
PVFieldPtr pvField = pvFields[i];
|
||||
if(pvField->getFieldOffset()==fieldOffset) return pvFields[i];
|
||||
if(pvField->getNextFieldOffset()<=fieldOffset) continue;
|
||||
if(pvField->getField()->getType()==structure) {
|
||||
PVStructure *pvStructure = static_cast<PVStructure *>(pvField.get());
|
||||
return pvStructure->getSubField(fieldOffset);
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t i=0, numFields = current->pvFields.size(); i<numFields; i++) {
|
||||
const PVFieldPtr& pvField = current->pvFields[i];
|
||||
|
||||
if(pvField->getFieldOffset()==fieldOffset) {
|
||||
return pvField;
|
||||
|
||||
} else if(pvField->getNextFieldOffset()<=fieldOffset) {
|
||||
continue;
|
||||
|
||||
} else if(pvField->getField()->getType()==structure) {
|
||||
current = static_cast<PVStructure *>(pvField.get());
|
||||
goto recurse;
|
||||
}
|
||||
}
|
||||
// the first test against current->getNextFieldOffset() would avoid this
|
||||
throw std::logic_error("PVStructure.getSubField: Logic error");
|
||||
}
|
||||
|
||||
PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
|
||||
PVFieldPtr PVStructure::getSubFieldT(std::size_t fieldOffset) const
|
||||
{
|
||||
PVFieldPtr pvField = getSubField(fieldOffset);
|
||||
if (pvField.get())
|
||||
return pvField;
|
||||
else
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get field with offset "
|
||||
<< fieldOffset << "(Invalid offset)" ;
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
PVField* PVStructure::getSubFieldImpl(const char *name, bool throws) const
|
||||
{
|
||||
const PVStructure *parent = this;
|
||||
if(!name)
|
||||
@@ -115,7 +154,7 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
|
||||
if (throws)
|
||||
throw std::invalid_argument("Failed to get field: (Field name is NULL string)");
|
||||
else
|
||||
return PVFieldPtr();
|
||||
return NULL;
|
||||
}
|
||||
const char *fullName = name;
|
||||
while(true) {
|
||||
@@ -131,7 +170,7 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
else
|
||||
return PVFieldPtr();
|
||||
return NULL;
|
||||
}
|
||||
size_t N = sep-name;
|
||||
if(N==0)
|
||||
@@ -144,7 +183,7 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
else
|
||||
return PVFieldPtr();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const PVFieldPtrArray& pvFields = parent->getPVFields();
|
||||
@@ -171,7 +210,7 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
else
|
||||
return PVFieldPtr();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(*sep) {
|
||||
@@ -188,32 +227,18 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
else
|
||||
return PVFieldPtr();
|
||||
return NULL;
|
||||
}
|
||||
child = NULL;
|
||||
name = sep+1; // skip past '.'
|
||||
// loop around to new parent
|
||||
|
||||
} else {
|
||||
return child->shared_from_this();
|
||||
return child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PVStructure::throwBadFieldType(const char *name)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << "Failed to get field: " << name << " (Field has wrong type)";
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
|
||||
void PVStructure::throwBadFieldType(std::size_t fieldOffset)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get field with offset "
|
||||
<< fieldOffset << " (Field has wrong type)";
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
|
||||
void PVStructure::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const {
|
||||
@@ -247,7 +272,7 @@ void PVStructure::serialize(ByteBuffer *pbuffer,
|
||||
|
||||
size_t fieldsSize = pvFields.size();
|
||||
for(size_t i = 0; i<fieldsSize; i++) {
|
||||
PVField* pvField = pvFields[i].get();
|
||||
PVFieldPtr pvField = pvFields[i];
|
||||
offset = pvField->getFieldOffset();
|
||||
int32 inumberFields = static_cast<int32>(pvField->getNumberFields());
|
||||
next = pbitSet->nextSetBit(static_cast<uint32>(offset));
|
||||
@@ -261,7 +286,8 @@ void PVStructure::serialize(ByteBuffer *pbuffer,
|
||||
if(inumberFields==1) {
|
||||
pvField->serialize(pbuffer, pflusher);
|
||||
} else {
|
||||
static_cast<PVStructure*>(pvField)->serialize(pbuffer, pflusher, pbitSet);
|
||||
PVStructurePtr pvStructure = std::tr1::static_pointer_cast<PVStructure>(pvField);
|
||||
pvStructure->serialize(pbuffer, pflusher, pbitSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,14 +76,14 @@ void PVStructureArray::compress() {
|
||||
size_t newLength = 0;
|
||||
|
||||
for(size_t i=0; i<length; i++) {
|
||||
if(vec[i]) {
|
||||
if(vec[i].get()!=NULL) {
|
||||
newLength++;
|
||||
continue;
|
||||
}
|
||||
// find first non 0
|
||||
size_t notNull = 0;
|
||||
for(size_t j=i+1;j<length;j++) {
|
||||
if(vec[j]) {
|
||||
if(vec[j].get()!=NULL) {
|
||||
notNull = j;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ using std::string;
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
#define PVUNION_UNDEFINED_INDEX -1
|
||||
const int32 PVUnion::UNDEFINED_INDEX = PVUNION_UNDEFINED_INDEX;
|
||||
int32 PVUnion::UNDEFINED_INDEX = PVUNION_UNDEFINED_INDEX;
|
||||
|
||||
PVDataCreatePtr PVUnion::pvDataCreate(getPVDataCreate());
|
||||
|
||||
@@ -40,7 +40,24 @@ PVUnion::PVUnion(UnionConstPtr const & unionPtr)
|
||||
|
||||
#undef PVUNION_UNDEFINED_INDEX
|
||||
|
||||
PVUnion::~PVUnion() {}
|
||||
PVUnion::~PVUnion()
|
||||
{
|
||||
}
|
||||
|
||||
UnionConstPtr PVUnion::getUnion() const
|
||||
{
|
||||
return unionPtr;
|
||||
}
|
||||
|
||||
PVFieldPtr PVUnion::get() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
int32 PVUnion::getSelectedIndex() const
|
||||
{
|
||||
return selector;
|
||||
}
|
||||
|
||||
string PVUnion::getSelectedFieldName() const
|
||||
{
|
||||
@@ -53,11 +70,8 @@ string PVUnion::getSelectedFieldName() const
|
||||
|
||||
PVFieldPtr PVUnion::select(int32 index)
|
||||
{
|
||||
if (variant && index != UNDEFINED_INDEX)
|
||||
throw std::invalid_argument("index out of bounds");
|
||||
|
||||
// no change
|
||||
if (selector == index && !variant)
|
||||
if (selector == index)
|
||||
return value;
|
||||
|
||||
if (index == UNDEFINED_INDEX)
|
||||
@@ -66,7 +80,9 @@ PVFieldPtr PVUnion::select(int32 index)
|
||||
value.reset();
|
||||
return value;
|
||||
}
|
||||
else if (index < 0 || size_t(index) >= unionPtr->getFields().size())
|
||||
else if (variant)
|
||||
throw std::invalid_argument("index out of bounds");
|
||||
else if (index < 0 || index > static_cast<int32>(unionPtr->getFields().size()))
|
||||
throw std::invalid_argument("index out of bounds");
|
||||
|
||||
FieldConstPtr field = unionPtr->getField(index);
|
||||
@@ -83,6 +99,11 @@ PVFieldPtr PVUnion::select(string const & fieldName)
|
||||
throw std::invalid_argument("no such fieldName");
|
||||
return select(index);
|
||||
}
|
||||
|
||||
void PVUnion::set(PVFieldPtr const & value)
|
||||
{
|
||||
set(selector, value);
|
||||
}
|
||||
|
||||
void PVUnion::set(int32 index, PVFieldPtr const & value)
|
||||
{
|
||||
@@ -93,14 +114,14 @@ void PVUnion::set(int32 index, PVFieldPtr const & value)
|
||||
if (index == UNDEFINED_INDEX)
|
||||
{
|
||||
// for undefined index we accept only null values
|
||||
if (value)
|
||||
if (value.get())
|
||||
throw std::invalid_argument("non-null value for index == UNDEFINED_INDEX");
|
||||
}
|
||||
else if (index < 0 || size_t(index) >= unionPtr->getFields().size())
|
||||
else if (index < 0 || index > static_cast<int32>(unionPtr->getFields().size()))
|
||||
throw std::invalid_argument("index out of bounds");
|
||||
else if (!value)
|
||||
throw std::invalid_argument("Can't set defined index w/ NULL");
|
||||
else if (value->getField() != unionPtr->getField(index))
|
||||
|
||||
// value type must match
|
||||
if (value->getField() != unionPtr->getField(index))
|
||||
throw std::invalid_argument("selected field and its introspection data do not match");
|
||||
}
|
||||
|
||||
@@ -115,7 +136,7 @@ void PVUnion::set(string const & fieldName, PVFieldPtr const & value)
|
||||
if (index == -1)
|
||||
throw std::invalid_argument("no such fieldName");
|
||||
|
||||
set(index, value);
|
||||
set(index, value);
|
||||
}
|
||||
|
||||
void PVUnion::serialize(ByteBuffer *pbuffer, SerializableControl *pflusher) const
|
||||
@@ -181,7 +202,7 @@ std::ostream& PVUnion::dumpValue(std::ostream& o) const
|
||||
o << format::indent() << getUnion()->getID() << ' ' << getFieldName() << std::endl;
|
||||
{
|
||||
format::indent_scope s(o);
|
||||
const PVField::const_shared_pointer& fieldField = get();
|
||||
PVFieldPtr fieldField = get();
|
||||
if (fieldField.get() == NULL)
|
||||
o << format::indent() << "(none)" << std::endl;
|
||||
else
|
||||
@@ -210,7 +231,7 @@ void PVUnion::copy(const PVUnion& from)
|
||||
void PVUnion::copyUnchecked(const PVUnion& from)
|
||||
{
|
||||
|
||||
const PVField::const_shared_pointer& fromValue = from.get();
|
||||
PVFieldPtr fromValue = from.get();
|
||||
if (from.getUnion()->isVariant())
|
||||
{
|
||||
if (fromValue.get() == 0)
|
||||
|
||||
@@ -76,14 +76,14 @@ void PVUnionArray::compress() {
|
||||
size_t newLength = 0;
|
||||
|
||||
for(size_t i=0; i<length; i++) {
|
||||
if(vec[i]) {
|
||||
if(vec[i].get()!=NULL) {
|
||||
newLength++;
|
||||
continue;
|
||||
}
|
||||
// find first non 0
|
||||
size_t notNull = 0;
|
||||
for(size_t j=i+1;j<length;j++) {
|
||||
if(vec[j]) {
|
||||
if(vec[j].get()!=NULL) {
|
||||
notNull = j;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include <stdexcept>
|
||||
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/lock.h>
|
||||
@@ -23,86 +22,33 @@ using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
static
|
||||
StructureConstPtr buildValueAlarm(ScalarType vtype)
|
||||
{
|
||||
return FieldBuilder::begin()
|
||||
->setId("valueAlarm_t")
|
||||
->add("active", pvBoolean)
|
||||
->add("lowAlarmLimit", vtype)
|
||||
->add("lowWarningLimit", vtype)
|
||||
->add("highWarningLimit", vtype)
|
||||
->add("highAlarmLimit", vtype)
|
||||
->add("lowAlarmSeverity", pvInt)
|
||||
->add("lowWarningSeverity", pvInt)
|
||||
->add("highWarningSeverity", pvInt)
|
||||
->add("highAlarmSeverity", pvInt)
|
||||
->add("hysteresis", pvByte)
|
||||
->createStructure();
|
||||
}
|
||||
|
||||
StandardField::StandardField()
|
||||
:fieldCreate(getFieldCreate())
|
||||
,notImplemented("not implemented")
|
||||
,valueFieldName("value")
|
||||
|
||||
,alarmField(FieldBuilder::begin()
|
||||
->setId("alarm_t")
|
||||
->add("severity", pvInt)
|
||||
->add("status", pvInt)
|
||||
->add("message", pvString)
|
||||
->createStructure())
|
||||
|
||||
,timeStampField(FieldBuilder::begin()
|
||||
->setId("time_t")
|
||||
->add("secondsPastEpoch", pvLong)
|
||||
->add("nanoseconds", pvInt)
|
||||
->add("userTag", pvInt)
|
||||
->createStructure())
|
||||
|
||||
,displayField(FieldBuilder::begin()
|
||||
->setId("display_t")
|
||||
->add("limitLow", pvDouble)
|
||||
->add("limitHigh", pvDouble)
|
||||
->add("description", pvString)
|
||||
->add("format", pvString)
|
||||
->add("units", pvString)
|
||||
->createStructure())
|
||||
|
||||
,controlField(FieldBuilder::begin()
|
||||
->setId("control_t")
|
||||
->add("limitLow", pvDouble)
|
||||
->add("limitHigh", pvDouble)
|
||||
->add("minStep", pvDouble)
|
||||
->createStructure())
|
||||
|
||||
,booleanAlarmField(FieldBuilder::begin()
|
||||
->setId("valueAlarm_t")
|
||||
->add("active", pvBoolean)
|
||||
->add("falseSeverity", pvInt)
|
||||
->add("trueSeverity", pvInt)
|
||||
->add("changeStateSeverity", pvInt)
|
||||
->createStructure())
|
||||
|
||||
,byteAlarmField(buildValueAlarm(pvByte))
|
||||
,shortAlarmField(buildValueAlarm(pvShort))
|
||||
,intAlarmField(buildValueAlarm(pvInt))
|
||||
,longAlarmField(buildValueAlarm(pvLong))
|
||||
,ubyteAlarmField(buildValueAlarm(pvUByte))
|
||||
,ushortAlarmField(buildValueAlarm(pvUShort))
|
||||
,uintAlarmField(buildValueAlarm(pvUInt))
|
||||
,ulongAlarmField(buildValueAlarm(pvULong))
|
||||
,floatAlarmField(buildValueAlarm(pvFloat))
|
||||
,doubleAlarmField(buildValueAlarm(pvDouble))
|
||||
|
||||
,enumeratedAlarmField(FieldBuilder::begin()
|
||||
->setId("valueAlarm_t")
|
||||
->add("active", pvBoolean)
|
||||
->add("stateSeverity", pvInt)
|
||||
->add("changeStateSeverity", pvInt)
|
||||
->createStructure())
|
||||
: fieldCreate(getFieldCreate()),
|
||||
notImplemented("not implemented"),
|
||||
valueFieldName("value")
|
||||
{}
|
||||
|
||||
void StandardField::init()
|
||||
{
|
||||
createAlarm();
|
||||
createTimeStamp();
|
||||
createDisplay();
|
||||
createControl();
|
||||
createBooleanAlarm();
|
||||
createByteAlarm();
|
||||
createShortAlarm();
|
||||
createIntAlarm();
|
||||
createLongAlarm();
|
||||
createUByteAlarm();
|
||||
createUShortAlarm();
|
||||
createUIntAlarm();
|
||||
createULongAlarm();
|
||||
createFloatAlarm();
|
||||
createDoubleAlarm();
|
||||
createEnumeratedAlarm();
|
||||
}
|
||||
|
||||
StandardField::~StandardField(){}
|
||||
|
||||
StructureConstPtr StandardField::createProperties(string id,FieldConstPtr field,string properties)
|
||||
@@ -199,6 +145,361 @@ StructureConstPtr StandardField::createProperties(string id,FieldConstPtr field,
|
||||
return fieldCreate->createStructure(id,names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createAlarm() {
|
||||
size_t num = 3;
|
||||
FieldConstPtrArray fields(num);
|
||||
StringArray names(num);
|
||||
names[0] = "severity";
|
||||
names[1] = "status";
|
||||
names[2] = "message";
|
||||
fields[0] = fieldCreate->createScalar(pvInt);
|
||||
fields[1] = fieldCreate->createScalar(pvInt);
|
||||
fields[2] = fieldCreate->createScalar(pvString);
|
||||
alarmField = fieldCreate->createStructure("alarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createTimeStamp() {
|
||||
size_t num = 3;
|
||||
FieldConstPtrArray fields(num);
|
||||
StringArray names(num);
|
||||
names[0] = "secondsPastEpoch";
|
||||
names[1] = "nanoseconds";
|
||||
names[2] = "userTag";
|
||||
fields[0] = fieldCreate->createScalar(pvLong);
|
||||
fields[1] = fieldCreate->createScalar(pvInt);
|
||||
fields[2] = fieldCreate->createScalar(pvInt);
|
||||
timeStampField = fieldCreate->createStructure("time_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createDisplay() {
|
||||
size_t num = 5;
|
||||
FieldConstPtrArray fields(num);
|
||||
StringArray names(num);
|
||||
names[0] = "limitLow";
|
||||
names[1] = "limitHigh";
|
||||
names[2] = "description";
|
||||
names[3] = "format";
|
||||
names[4] = "units";
|
||||
fields[0] = fieldCreate->createScalar(pvDouble);
|
||||
fields[1] = fieldCreate->createScalar(pvDouble);
|
||||
fields[2] = fieldCreate->createScalar(pvString);
|
||||
fields[3] = fieldCreate->createScalar(pvString);
|
||||
fields[4] = fieldCreate->createScalar(pvString);
|
||||
displayField = fieldCreate->createStructure("display_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createControl() {
|
||||
size_t num = 3;
|
||||
FieldConstPtrArray fields(num);
|
||||
StringArray names(num);
|
||||
names[0] = "limitLow";
|
||||
names[1] = "limitHigh";
|
||||
names[2] = "minStep";
|
||||
fields[0] = fieldCreate->createScalar(pvDouble);
|
||||
fields[1] = fieldCreate->createScalar(pvDouble);
|
||||
fields[2] = fieldCreate->createScalar(pvDouble);
|
||||
controlField = fieldCreate->createStructure("control_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createBooleanAlarm() {
|
||||
size_t numFields = 4;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "falseSeverity";
|
||||
names[2] = "trueSeverity";
|
||||
names[3] = "changeStateSeverity";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvInt);
|
||||
fields[2] = fieldCreate->createScalar(pvInt);
|
||||
fields[3] = fieldCreate->createScalar(pvInt);
|
||||
booleanAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createByteAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvByte);
|
||||
fields[2] = fieldCreate->createScalar(pvByte);
|
||||
fields[3] = fieldCreate->createScalar(pvByte);
|
||||
fields[4] = fieldCreate->createScalar(pvByte);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvByte);
|
||||
byteAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createShortAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvShort);
|
||||
fields[2] = fieldCreate->createScalar(pvShort);
|
||||
fields[3] = fieldCreate->createScalar(pvShort);
|
||||
fields[4] = fieldCreate->createScalar(pvShort);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvShort);
|
||||
shortAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createIntAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvInt);
|
||||
fields[2] = fieldCreate->createScalar(pvInt);
|
||||
fields[3] = fieldCreate->createScalar(pvInt);
|
||||
fields[4] = fieldCreate->createScalar(pvInt);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvInt);
|
||||
intAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createLongAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvLong);
|
||||
fields[2] = fieldCreate->createScalar(pvLong);
|
||||
fields[3] = fieldCreate->createScalar(pvLong);
|
||||
fields[4] = fieldCreate->createScalar(pvLong);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvLong);
|
||||
longAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createUByteAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvUByte);
|
||||
fields[2] = fieldCreate->createScalar(pvUByte);
|
||||
fields[3] = fieldCreate->createScalar(pvUByte);
|
||||
fields[4] = fieldCreate->createScalar(pvUByte);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvUByte);
|
||||
ubyteAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createUShortAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvUShort);
|
||||
fields[2] = fieldCreate->createScalar(pvUShort);
|
||||
fields[3] = fieldCreate->createScalar(pvUShort);
|
||||
fields[4] = fieldCreate->createScalar(pvUShort);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvUShort);
|
||||
ushortAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createUIntAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvUInt);
|
||||
fields[2] = fieldCreate->createScalar(pvUInt);
|
||||
fields[3] = fieldCreate->createScalar(pvUInt);
|
||||
fields[4] = fieldCreate->createScalar(pvUInt);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvUInt);
|
||||
uintAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createULongAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvULong);
|
||||
fields[2] = fieldCreate->createScalar(pvULong);
|
||||
fields[3] = fieldCreate->createScalar(pvULong);
|
||||
fields[4] = fieldCreate->createScalar(pvULong);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvULong);
|
||||
ulongAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createFloatAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvFloat);
|
||||
fields[2] = fieldCreate->createScalar(pvFloat);
|
||||
fields[3] = fieldCreate->createScalar(pvFloat);
|
||||
fields[4] = fieldCreate->createScalar(pvFloat);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvFloat);
|
||||
floatAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createDoubleAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvDouble);
|
||||
fields[2] = fieldCreate->createScalar(pvDouble);
|
||||
fields[3] = fieldCreate->createScalar(pvDouble);
|
||||
fields[4] = fieldCreate->createScalar(pvDouble);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvDouble);
|
||||
doubleAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createEnumeratedAlarm() {
|
||||
size_t numFields = 3;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "stateSeverity";
|
||||
names[2] = "changeStateSeverity";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalarArray(pvInt);
|
||||
fields[2] = fieldCreate->createScalar(pvInt);
|
||||
enumeratedAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
|
||||
StructureConstPtr StandardField::scalar(
|
||||
ScalarType type,string const &properties)
|
||||
{
|
||||
@@ -263,21 +564,104 @@ StructureConstPtr StandardField::enumerated(string const &properties)
|
||||
return createProperties("epics:nt/NTEnum:1.0",field,properties);
|
||||
}
|
||||
|
||||
static StandardFieldPtr *stdFieldGbl;
|
||||
|
||||
static epicsThreadOnceId stdFieldGblOnce = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
void StandardField::once(void*)
|
||||
StructureConstPtr StandardField::alarm()
|
||||
{
|
||||
stdFieldGbl = new StandardFieldPtr;
|
||||
stdFieldGbl->reset(new StandardField);
|
||||
return alarmField;
|
||||
}
|
||||
|
||||
const StandardFieldPtr &StandardField::getStandardField()
|
||||
StructureConstPtr StandardField::timeStamp()
|
||||
{
|
||||
epicsThreadOnce(&stdFieldGblOnce, &StandardField::once, 0);
|
||||
return timeStampField;
|
||||
}
|
||||
|
||||
return *stdFieldGbl;
|
||||
StructureConstPtr StandardField::display()
|
||||
{
|
||||
return displayField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::control()
|
||||
{
|
||||
return controlField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::booleanAlarm()
|
||||
{
|
||||
return booleanAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::byteAlarm()
|
||||
{
|
||||
return byteAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::ubyteAlarm()
|
||||
{
|
||||
return ubyteAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::shortAlarm()
|
||||
{
|
||||
return shortAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::ushortAlarm()
|
||||
{
|
||||
return ushortAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::intAlarm()
|
||||
{
|
||||
return intAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::uintAlarm()
|
||||
{
|
||||
return uintAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::longAlarm()
|
||||
{
|
||||
return longAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::ulongAlarm()
|
||||
{
|
||||
return ulongAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::floatAlarm()
|
||||
{
|
||||
return floatAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::doubleAlarm()
|
||||
{
|
||||
return doubleAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::enumeratedAlarm()
|
||||
{
|
||||
return enumeratedAlarmField;
|
||||
}
|
||||
|
||||
StandardFieldPtr StandardField::getStandardField()
|
||||
{
|
||||
static StandardFieldPtr standardFieldCreate;
|
||||
static Mutex mutex;
|
||||
Lock xx(mutex);
|
||||
|
||||
if(standardFieldCreate.get()==0)
|
||||
{
|
||||
standardFieldCreate = StandardFieldPtr(new StandardField());
|
||||
standardFieldCreate->init();
|
||||
}
|
||||
return standardFieldCreate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
StandardFieldPtr getStandardField() {
|
||||
return StandardField::getStandardField();
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
@@ -3,521 +3,42 @@
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#if defined(__linux__) || defined(__APPLE__)
|
||||
#include <unistd.h>
|
||||
#define HAVE_ISATTY
|
||||
#endif
|
||||
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include <epicsTime.h>
|
||||
#include <epicsString.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/bitSet.h>
|
||||
#include <pv/pvData.h>
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
# include <pv/json.h>
|
||||
#endif
|
||||
#include <pv/pvIntrospect.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
namespace format
|
||||
{
|
||||
static int indent_index = std::ios_base::xalloc();
|
||||
|
||||
long& indent_value(std::ios_base& ios)
|
||||
{
|
||||
return ios.iword(indent_index);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, indent_level const& indent)
|
||||
{
|
||||
indent_value(os) = indent.level;
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, indent const&)
|
||||
{
|
||||
long il = indent_value(os);
|
||||
for(long i=0, spaces = il * 4; i<spaces; i++)
|
||||
os.put(' ');
|
||||
return os;
|
||||
}
|
||||
|
||||
array_at_internal operator<<(std::ostream& str, array_at const& manip)
|
||||
{
|
||||
return array_at_internal(manip.index, str);
|
||||
}
|
||||
}
|
||||
|
||||
}} //epics::pvData
|
||||
namespace {
|
||||
using namespace epics::pvData;
|
||||
|
||||
bool useEscapes(std::ostream& strm) {
|
||||
FILE *fp = 0;
|
||||
// TODO: a better way to do this...
|
||||
if(&std::cout==&strm)
|
||||
fp = stdout;
|
||||
if(&std::cerr==&strm)
|
||||
fp = stderr;
|
||||
if(!fp) return false;
|
||||
#ifdef HAVE_ISATTY
|
||||
// check $TERM as well?
|
||||
return isatty(fileno(fp))==1;
|
||||
#else
|
||||
// TODO: in theory windows 10 can be made to understand escapes as well
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void printAlarmTx(std::ostream& strm, const PVStructure& sub)
|
||||
{
|
||||
PVScalar::const_shared_pointer pvSeverity(sub.getSubField<PVInt>("severity"));
|
||||
PVScalar::const_shared_pointer pvStatus(sub.getSubField<PVInt>("status"));
|
||||
PVString::const_shared_pointer pvMessage(sub.getSubField<PVString>("message"));
|
||||
|
||||
switch(pvSeverity ? pvSeverity->getAs<int32>() : 0) {
|
||||
case 0: return; // nothing more to do here...
|
||||
case 1: strm<<"MINOR "; break;
|
||||
case 2: strm<<"MAJOR "; break;
|
||||
case 3: strm<<"INVALID "; break;
|
||||
case 4: strm<<"UNDEFINED "; break;
|
||||
default: strm<<pvSeverity->getAs<int32>()<<' ';
|
||||
}
|
||||
|
||||
switch(pvStatus ? pvStatus->getAs<int32>() : 0) {
|
||||
case 0: break;
|
||||
case 1: strm<<"DEVICE "; break;
|
||||
case 2: strm<<"DRIVER "; break;
|
||||
case 3: strm<<"RECORD "; break;
|
||||
case 4: strm<<"DB "; break;
|
||||
case 5: strm<<"CONF "; break;
|
||||
case 6: strm<<"UNDEFINED "; break;
|
||||
case 7: strm<<"CLIENT "; break;
|
||||
default: strm<<pvStatus->getAs<int32>()<<' ';
|
||||
}
|
||||
|
||||
if(pvMessage && !pvMessage->get().empty())
|
||||
strm<<pvMessage->get()<<' ';
|
||||
}
|
||||
|
||||
|
||||
void printAlarmT(std::ostream& strm, const PVStructure& top)
|
||||
{
|
||||
PVStructure::const_shared_pointer sub(top.getSubField<PVStructure>("alarm"));
|
||||
if(sub)
|
||||
printAlarmTx(strm, *sub);
|
||||
}
|
||||
|
||||
void printTimeTx(std::ostream& strm, const PVStructure& tsubop)
|
||||
{
|
||||
char timeText[32];
|
||||
epicsTimeStamp epicsTS;
|
||||
|
||||
PVScalar::const_shared_pointer secf(tsubop.getSubField<PVScalar>("secondsPastEpoch")),
|
||||
nsecf(tsubop.getSubField<PVScalar>("nanoseconds")),
|
||||
tagf(tsubop.getSubField<PVScalar>("userTag"));
|
||||
|
||||
epicsTS.secPastEpoch = secf ? secf->getAs<int64>() : 0;
|
||||
epicsTS.nsec = nsecf ? nsecf->getAs<int32>() : 0;
|
||||
|
||||
if(epicsTS.secPastEpoch > POSIX_TIME_AT_EPICS_EPOCH)
|
||||
epicsTS.secPastEpoch -= POSIX_TIME_AT_EPICS_EPOCH;
|
||||
else
|
||||
epicsTS.secPastEpoch = 0;
|
||||
|
||||
epicsTimeToStrftime(timeText, sizeof(timeText), "%Y-%m-%d %H:%M:%S.%03f", &epicsTS);
|
||||
strm <<std::setw(24) <<std::left <<timeText <<' ';
|
||||
if (tagf) {
|
||||
int64 tagv = tagf->getAs<int64>();
|
||||
if(tagv)
|
||||
strm << tagv << ' ';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void printTimeT(std::ostream& strm, const PVStructure& top)
|
||||
{
|
||||
PVStructure::const_shared_pointer sub(top.getSubField<PVStructure>("timeStamp"));
|
||||
if(sub)
|
||||
printTimeTx(strm, *sub);
|
||||
}
|
||||
|
||||
bool printEnumT(std::ostream& strm, const PVStructure& top, bool fromtop)
|
||||
{
|
||||
PVStructure::const_shared_pointer value;
|
||||
if(fromtop) {
|
||||
value = top.getSubField<PVStructure>("value");
|
||||
} else {
|
||||
value = std::tr1::static_pointer_cast<const PVStructure>(top.shared_from_this());
|
||||
}
|
||||
PVScalar::const_shared_pointer idx(value->getSubField<PVScalar>("index"));
|
||||
PVStringArray::const_shared_pointer choices(value->getSubField<PVStringArray>("choices"));
|
||||
if(!idx || !choices) return false;
|
||||
|
||||
if(fromtop) {
|
||||
strm<<format::indent();
|
||||
printTimeT(strm, top);
|
||||
printAlarmT(strm, top);
|
||||
}
|
||||
|
||||
PVStringArray::const_svector ch(choices->view());
|
||||
uint32 I = idx->getAs<uint32>();
|
||||
strm<<'('<<I<<')';
|
||||
if(I>=ch.size()) {
|
||||
strm<<" <undefined>";
|
||||
} else {
|
||||
strm<<' '<<escape(ch[I]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void csvEscape(std::string& S)
|
||||
{
|
||||
// concise, not particularly efficient...
|
||||
std::string temp(escape(S).style(escape::CSV).str());
|
||||
if(S.find_first_of(" ,\\")!=S.npos) {// only quote if necessary (stupid Excel)
|
||||
std::string temp2;
|
||||
temp2.reserve(temp.size()+2);
|
||||
temp2.push_back('\"');
|
||||
temp2 += temp;
|
||||
temp2.push_back('\"');
|
||||
temp2.swap(temp);
|
||||
}
|
||||
S = temp;
|
||||
}
|
||||
|
||||
bool printTable(std::ostream& strm, const PVStructure& top)
|
||||
{
|
||||
PVStructure::const_shared_pointer cf(top.getSubField<PVStructure>("value"));
|
||||
if(!cf) return false;
|
||||
static int indent_index = std::ios_base::xalloc();
|
||||
|
||||
long& indent_value(std::ios_base& ios)
|
||||
{
|
||||
const FieldConstPtrArray& fields = cf->getStructure()->getFields();
|
||||
if(fields.empty()) return false;
|
||||
for(size_t i=0, N=fields.size(); i<N; i++) {
|
||||
if(fields[i]->getType()!=scalarArray)
|
||||
return false; // cowardly refusing to handle anything else...
|
||||
}
|
||||
return ios.iword(indent_index);
|
||||
}
|
||||
|
||||
// maybe output a line with meta-data
|
||||
{
|
||||
bool havets = !!top.getSubField("timeStamp");
|
||||
bool haveal = !!top.getSubField("alarm");
|
||||
if(havets || haveal)
|
||||
strm<<format::indent();
|
||||
if(havets) {
|
||||
printTimeT(strm, top);
|
||||
strm<<' ';
|
||||
}
|
||||
if(haveal) {
|
||||
printAlarmT(strm, top);
|
||||
strm<<' ';
|
||||
}
|
||||
strm<<'\n';
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, indent_level const& indent)
|
||||
{
|
||||
indent_value(os) = indent.level;
|
||||
return os;
|
||||
}
|
||||
|
||||
PVStringArray::svector labels;
|
||||
{
|
||||
PVStringArray::const_shared_pointer lf(top.getSubField<PVStringArray>("labels"));
|
||||
if(lf) {
|
||||
PVStringArray::const_svector L(lf->view());
|
||||
labels = thaw(L);
|
||||
}
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, indent const&)
|
||||
{
|
||||
long il = indent_value(os);
|
||||
std::size_t spaces = static_cast<std::size_t>(il) * 4;
|
||||
return os << string(spaces, ' ');
|
||||
}
|
||||
|
||||
const PVFieldPtrArray& columns = cf->getPVFields();
|
||||
std::vector<shared_vector<std::string> > coldat(columns.size());
|
||||
array_at_internal operator<<(std::ostream& str, array_at const& manip)
|
||||
{
|
||||
return array_at_internal(manip.index, str);
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<size_t> widths(columns.size());
|
||||
labels.reserve(columns.size());
|
||||
|
||||
size_t nrows = size_t(-1);
|
||||
|
||||
for(size_t i=0, N=columns.size(); i<N; i++) {
|
||||
if(i>=labels.size()) {
|
||||
labels.push_back(cf->getStructure()->getFieldName(i));
|
||||
} else {
|
||||
csvEscape(labels[i]);
|
||||
}
|
||||
widths[i] = labels[i].size();
|
||||
|
||||
{
|
||||
shared_vector<const std::string> ro;
|
||||
static_cast<const PVScalarArray*>(columns[i].get())->getAs(ro);
|
||||
coldat[i] = thaw(ro);
|
||||
}
|
||||
|
||||
// truncate if some columns are longer than others
|
||||
nrows = std::min(nrows, coldat[i].size());
|
||||
|
||||
for(size_t j=0, M=coldat[i].size(); j<M; j++) {
|
||||
csvEscape(coldat[i][j]);
|
||||
widths[i] = std::max(widths[i], coldat[i][j].size());
|
||||
}
|
||||
}
|
||||
|
||||
// output header line
|
||||
strm<<format::indent();
|
||||
for(size_t c=0, N=coldat.size(); c<N; c++) {
|
||||
strm<<std::setw(widths[c])<<std::right<<labels[c];
|
||||
if(c+1!=N) {
|
||||
strm<<' ';
|
||||
}
|
||||
}
|
||||
strm<<'\n';
|
||||
|
||||
for(size_t r=0; r<nrows; r++) {
|
||||
strm<<format::indent();
|
||||
for(size_t c=0, N=coldat.size(); c<N; c++) {
|
||||
strm<<std::setw(widths[c])<<std::right<<coldat[c][r];
|
||||
if(c+1!=N)
|
||||
strm<<' ';
|
||||
}
|
||||
strm<<'\n';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void expandBS(const PVStructure& top, BitSet& mask, bool parents) {
|
||||
if(mask.get(0)) { // special handling because getSubField(0) not allowed
|
||||
// wildcard
|
||||
for(size_t idx=1, N=top.getNumberFields(); idx<N; idx++) {
|
||||
mask.set(idx);
|
||||
}
|
||||
|
||||
} else {
|
||||
for(int32 idx = mask.nextSetBit(0), N=top.getNumberFields(); idx>=0 && idx<N; idx=mask.nextSetBit(idx+1)) {
|
||||
PVField::const_shared_pointer fld = top.getSubFieldT(idx);
|
||||
|
||||
// look forward and mark all children
|
||||
for(size_t i=idx+1, N=fld->getNextFieldOffset(); i<N; i++)
|
||||
mask.set(i);
|
||||
|
||||
if(parents) {
|
||||
// look back and mark all parents
|
||||
// we've already stepped past all parents so siblings will not be automatically marked
|
||||
for(const PVStructure *parent = fld->getParent(); parent; parent = parent->getParent()) {
|
||||
mask.set(parent->getFieldOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
void printRaw(std::ostream& strm, const PVStructure::Formatter& format, const PVStructure& cur)
|
||||
{
|
||||
typedef std::deque<std::pair<const PVField*, size_t> > todo_t;
|
||||
todo_t todo;
|
||||
BitSet show, highlight;
|
||||
|
||||
const long orig_indent = format::indent_value(strm);
|
||||
|
||||
{
|
||||
if(format.xshow)
|
||||
show = *format.xshow;
|
||||
else
|
||||
show.set(0);
|
||||
|
||||
if(format.xhighlight)
|
||||
highlight = *format.xhighlight;
|
||||
|
||||
expandBS(format.xtop, show, true);
|
||||
expandBS(format.xtop, highlight, false);
|
||||
highlight &= show; // can't highlight hidden fields (paranoia)
|
||||
}
|
||||
|
||||
if(!show.get(0)) return; // nothing to do here...
|
||||
|
||||
todo.push_front(std::make_pair(&format.xtop, orig_indent));
|
||||
|
||||
while(!todo.empty()) {
|
||||
todo_t::value_type cur(todo.front());
|
||||
todo.pop_front();
|
||||
|
||||
format::indent_value(strm) = cur.second;
|
||||
|
||||
bool hl = highlight.get(cur.first->getFieldOffset()) && format.xmode==PVStructure::Formatter::ANSI;
|
||||
if(hl)
|
||||
strm<<"\x1b[1m"; // Bold
|
||||
|
||||
switch(cur.first->getField()->getType()) {
|
||||
case structure: {
|
||||
const PVStructure* str = static_cast<const PVStructure*>(cur.first);
|
||||
|
||||
const PVFieldPtrArray& flds = str->getPVFields();
|
||||
|
||||
for(PVFieldPtrArray::const_reverse_iterator it(flds.rbegin()), end(flds.rend()); it!=end; ++it) {
|
||||
const PVField *fld = (*it).get();
|
||||
if(!show.get(fld->getFieldOffset())) continue;
|
||||
|
||||
todo.push_front(std::make_pair(fld, cur.second+1));
|
||||
}
|
||||
|
||||
std::string id(cur.first->getField()->getID());
|
||||
|
||||
strm<<format::indent()<<id<<' '<<cur.first->getFieldName();
|
||||
if(id=="alarm_t") {
|
||||
strm.put(' ');
|
||||
printAlarmTx(strm, *str);
|
||||
} else if(id=="time_t") {
|
||||
strm.put(' ');
|
||||
printTimeTx(strm, *str);
|
||||
} else if(id=="enum_t") {
|
||||
strm.put(' ');
|
||||
printEnumT(strm, *str, false);
|
||||
}
|
||||
strm.put('\n');
|
||||
}
|
||||
break;
|
||||
case scalar:
|
||||
case scalarArray:
|
||||
strm<<format::indent()<<cur.first->getField()->getID()<<' '<<cur.first->getFieldName()
|
||||
<<' '<<*cur.first
|
||||
<<'\n';
|
||||
break;
|
||||
case structureArray:
|
||||
case union_:
|
||||
case unionArray:
|
||||
strm<<*cur.first;
|
||||
break;
|
||||
}
|
||||
|
||||
if(hl)
|
||||
strm<<"\x1b[0m"; // reset
|
||||
}
|
||||
|
||||
format::indent_value(strm) = orig_indent;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& strm, const PVStructure::Formatter& format)
|
||||
{
|
||||
if(format.xfmt==PVStructure::Formatter::JSON) {
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
JSONPrintOptions opts;
|
||||
opts.multiLine = false;
|
||||
printJSON(strm, format.xtop, format.xshow ? *format.xshow : BitSet().set(0), opts);
|
||||
strm<<'\n';
|
||||
return strm;
|
||||
#else
|
||||
// fall through to Raw
|
||||
#endif
|
||||
|
||||
} else if(format.xfmt==PVStructure::Formatter::NT) {
|
||||
std::string id(format.xtop.getStructure()->getID()),
|
||||
idprefix(id.substr(0, id.find_first_of('.')));
|
||||
|
||||
// NTTable
|
||||
if(idprefix=="epics:nt/NTTable:1") {
|
||||
if(printTable(strm, format.xtop))
|
||||
return strm;
|
||||
} else {
|
||||
//NTScalar, NTScalarArray, NTEnum, or anything with '.value'
|
||||
|
||||
PVField::const_shared_pointer value(format.xtop.getSubField("value"));
|
||||
if(value) {
|
||||
switch(value->getField()->getType()) {
|
||||
case scalar:
|
||||
strm<<format::indent();
|
||||
printTimeT(strm, format.xtop);
|
||||
strm<<std::setprecision(6)<<*static_cast<const PVScalar*>(value.get())<<' ';
|
||||
printAlarmT(strm, format.xtop);
|
||||
strm<<'\n';
|
||||
return strm;
|
||||
|
||||
case scalarArray:
|
||||
strm<<format::indent();
|
||||
printTimeT(strm, format.xtop);
|
||||
printAlarmT(strm, format.xtop);
|
||||
strm<<std::setprecision(6)<<*static_cast<const PVScalarArray*>(value.get())<<'\n';
|
||||
return strm;
|
||||
|
||||
case structure:
|
||||
if(printEnumT(strm, format.xtop, true)) {
|
||||
strm<<'\n';
|
||||
return strm;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// fall through unhandled as Raw
|
||||
|
||||
PVStructure::Formatter format2(format);
|
||||
|
||||
if(format2.xmode==PVStructure::Formatter::Auto)
|
||||
format2.xmode = useEscapes(strm) ? PVStructure::Formatter::ANSI : PVStructure::Formatter::Plain;
|
||||
|
||||
printRaw(strm, format2, format.xtop);
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
static char hexdigit(char c) {
|
||||
c &= 0xf;
|
||||
if(c<9)
|
||||
return '0'+c;
|
||||
else
|
||||
return 'A'+c-10;
|
||||
}
|
||||
|
||||
escape::~escape() {}
|
||||
|
||||
std::string escape::str() const
|
||||
{
|
||||
std::ostringstream strm;
|
||||
strm<<(*this);
|
||||
return strm.str();
|
||||
}
|
||||
|
||||
epicsShareFunc
|
||||
std::ostream& operator<<(std::ostream& strm, const escape& Q)
|
||||
{
|
||||
for(size_t pos = 0, len = Q.orig.size(); pos < len; pos++) {
|
||||
const char C = Q.orig[pos];
|
||||
char quote = '\\', next;
|
||||
// compare me with epicsStrnEscapedFromRaw()
|
||||
switch(C) {
|
||||
case '\a': next = 'a'; break;
|
||||
case '\b': next = 'b'; break;
|
||||
case '\f': next = 'f'; break;
|
||||
case '\n': next = 'n'; break;
|
||||
case '\r': next = 'r'; break;
|
||||
case '\t': next = 't'; break;
|
||||
case '\v': next = 'v'; break;
|
||||
case '\\': next = '\\'; break;
|
||||
case '\'': next = '\''; break;
|
||||
case '\"': next = '\"'; if(Q.S==escape::CSV) quote = '"'; break;
|
||||
default:
|
||||
if(!isprint(C)) {
|
||||
// print three charator escape
|
||||
strm<<"\\x"<<hexdigit(C>>4)<<hexdigit(C);
|
||||
} else {
|
||||
// literal
|
||||
strm.put(C);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// print two charactor escape
|
||||
strm.put(quote);
|
||||
strm.put(next);
|
||||
}
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
}} //epics::pvData
|
||||
}}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
# This is a Makefile fragment, see ../Makefile
|
||||
|
||||
SRC_DIRS += $(PVDATA_SRC)/json
|
||||
|
||||
INC += pv/json.h
|
||||
|
||||
LIBSRCS += parsehelper.cpp
|
||||
LIBSRCS += parseany.cpp
|
||||
LIBSRCS += parseinto.cpp
|
||||
LIBSRCS += print.cpp
|
||||
@@ -1,284 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvdVersion.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/valueBuilder.h>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
|
||||
#include "pv/json.h"
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
using pvd::yajl::integer_arg;
|
||||
using pvd::yajl::size_arg;
|
||||
|
||||
namespace {
|
||||
|
||||
struct context {
|
||||
|
||||
unsigned depth;
|
||||
|
||||
enum state_t {
|
||||
Undefined,
|
||||
Key,
|
||||
Array,
|
||||
} state;
|
||||
|
||||
pvd::shared_vector<void> arr;
|
||||
|
||||
pvd::ValueBuilder root,
|
||||
*cur;
|
||||
|
||||
std::string msg,
|
||||
key;
|
||||
|
||||
context() :depth(0u), state(Undefined), cur(&root) {}
|
||||
};
|
||||
|
||||
#define TRY context *self = (context*)ctx; try
|
||||
|
||||
#define CATCH() catch(std::exception& e) { self->msg = e.what(); return 0; }
|
||||
|
||||
int jtree_null(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
self->msg = "NULL value not permitted";
|
||||
return 0;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_boolean(void * ctx, int boolVal)
|
||||
{
|
||||
TRY {
|
||||
if(self->depth==0) throw std::runtime_error("Bare value not supported");
|
||||
switch(self->state) {
|
||||
case context::Key:
|
||||
self->cur = &self->cur->add<pvd::pvBoolean>(self->key, boolVal);
|
||||
self->key.clear();
|
||||
self->state = context::Undefined;
|
||||
break;
|
||||
case context::Array:
|
||||
{
|
||||
if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvBoolean)
|
||||
throw std::runtime_error("Mixed type array not supported");
|
||||
pvd::shared_vector<pvd::boolean> arr(pvd::static_shared_vector_cast<pvd::boolean>(self->arr));
|
||||
arr.push_back(boolVal);
|
||||
self->arr = pvd::static_shared_vector_cast<void>(arr);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::logic_error("boolean in bad state");
|
||||
}
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_integer(void * ctx, integer_arg integerVal)
|
||||
{
|
||||
TRY {
|
||||
if(self->depth==0) throw std::runtime_error("Bare value not supported");
|
||||
switch(self->state) {
|
||||
case context::Key:
|
||||
self->cur = &self->cur->add<pvd::pvLong>(self->key, integerVal);
|
||||
self->key.clear();
|
||||
self->state = context::Undefined;
|
||||
break;
|
||||
case context::Array:
|
||||
{
|
||||
if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvLong)
|
||||
throw std::runtime_error("Mixed type array not supported");
|
||||
pvd::shared_vector<pvd::int64> arr(pvd::static_shared_vector_cast<pvd::int64>(self->arr));
|
||||
arr.push_back(integerVal);
|
||||
self->arr = pvd::static_shared_vector_cast<void>(arr);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::logic_error("int64 in bad state");
|
||||
}
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_double(void * ctx, double doubleVal)
|
||||
{
|
||||
TRY {
|
||||
if(self->depth==0) throw std::runtime_error("Bare value not supported");
|
||||
switch(self->state) {
|
||||
case context::Key:
|
||||
self->cur = &self->cur->add<pvd::pvDouble>(self->key, doubleVal);
|
||||
self->key.clear();
|
||||
self->state = context::Undefined;
|
||||
break;
|
||||
case context::Array:
|
||||
{
|
||||
if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvDouble)
|
||||
throw std::runtime_error("Mixed type array not supported");
|
||||
pvd::shared_vector<double> arr(pvd::static_shared_vector_cast<double>(self->arr));
|
||||
arr.push_back(doubleVal);
|
||||
self->arr = pvd::static_shared_vector_cast<void>(arr);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::logic_error("double in bad state");
|
||||
}
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_string(void * ctx, const unsigned char * stringVal,
|
||||
size_arg stringLen)
|
||||
{
|
||||
TRY {
|
||||
if(self->depth==0) throw std::runtime_error("Bare value not supported");
|
||||
std::string sval((const char*)stringVal, stringLen);
|
||||
switch(self->state) {
|
||||
case context::Key:
|
||||
self->cur = &self->cur->add<pvd::pvString>(self->key, sval);
|
||||
self->key.clear();
|
||||
self->state = context::Undefined;
|
||||
break;
|
||||
case context::Array:
|
||||
{
|
||||
if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvString)
|
||||
throw std::runtime_error("Mixed type array not supported");
|
||||
pvd::shared_vector<std::string> arr(pvd::static_shared_vector_cast<std::string>(self->arr));
|
||||
arr.push_back(sval);
|
||||
self->arr = pvd::static_shared_vector_cast<void>(arr);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::logic_error("double in bad state");
|
||||
}
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_start_map(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
if(self->depth>0) {
|
||||
if(self->key.empty())
|
||||
throw std::logic_error("anonymous dict not top level?");
|
||||
self->cur = &self->cur->addNested(self->key);
|
||||
self->key.clear();
|
||||
}
|
||||
self->depth++;
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_map_key(void * ctx, const unsigned char * key,
|
||||
size_arg stringLen)
|
||||
{
|
||||
TRY {
|
||||
if(!self->key.empty())
|
||||
throw std::logic_error("double key?");
|
||||
if(stringLen==0)
|
||||
throw std::runtime_error("empty key not allowed");
|
||||
self->key = std::string((const char*)key, stringLen);
|
||||
self->state = context::Key;
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_end_map(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
if(self->depth>1)
|
||||
self->cur = &self->cur->endNested();
|
||||
else if(self->depth==0)
|
||||
throw std::logic_error("Unbalenced dict");
|
||||
self->depth--;
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_start_array(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
if(self->depth==0) throw std::runtime_error("Bare array not supported");
|
||||
if(self->state!=context::Key)
|
||||
throw std::logic_error("bare array not supported");
|
||||
self->state = context::Array;
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
int jtree_end_array(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
if(self->state!=context::Array)
|
||||
throw std::logic_error("Bad array parse");
|
||||
self->cur = &self->cur->add(self->key, pvd::freeze(self->arr));
|
||||
self->key.clear();
|
||||
self->state = context::Undefined;
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
|
||||
yajl_callbacks jtree_cbs = {
|
||||
&jtree_null,
|
||||
&jtree_boolean,
|
||||
&jtree_integer,
|
||||
&jtree_double,
|
||||
NULL, // number
|
||||
&jtree_string,
|
||||
&jtree_start_map,
|
||||
&jtree_map_key,
|
||||
&jtree_end_map,
|
||||
&jtree_start_array,
|
||||
&jtree_end_array,
|
||||
};
|
||||
|
||||
struct handler {
|
||||
yajl_handle handle;
|
||||
handler(yajl_handle handle) :handle(handle)
|
||||
{
|
||||
if(!handle)
|
||||
throw std::runtime_error("Failed to allocate yajl handle");
|
||||
}
|
||||
~handler() {
|
||||
yajl_free(handle);
|
||||
}
|
||||
operator yajl_handle() { return handle; }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
|
||||
epics::pvData::PVStructure::shared_pointer
|
||||
parseJSON(std::istream& strm)
|
||||
{
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
yajl_parser_config conf;
|
||||
memset(&conf, 0, sizeof(conf));
|
||||
conf.allowComments = 1;
|
||||
conf.checkUTF8 = 1;
|
||||
#endif
|
||||
|
||||
context ctxt;
|
||||
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
handler handle(yajl_alloc(&jtree_cbs, &conf, NULL, &ctxt));
|
||||
#else
|
||||
handler handle(yajl_alloc(&jtree_cbs, NULL, &ctxt));
|
||||
|
||||
yajl_config(handle, yajl_allow_comments, 1);
|
||||
#endif
|
||||
|
||||
if(!yajl_parse_helper(strm, handle))
|
||||
throw std::runtime_error(ctxt.msg);
|
||||
|
||||
return ctxt.cur->buildPVStructure();
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#endif // EPICS_VERSION_INT
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvdVersion.h>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
|
||||
#include "pv/json.h"
|
||||
|
||||
namespace {
|
||||
|
||||
void check_trailing(const std::string& line)
|
||||
{
|
||||
size_t idx = line.find_first_not_of(" \t\n\r");
|
||||
if(idx==line.npos) return;
|
||||
// TODO: detect the end of potentially multi-line comments...
|
||||
// for now trailing comments not allowed
|
||||
throw std::runtime_error("Trailing junk");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
|
||||
bool yajl_parse_helper(std::istream& src,
|
||||
yajl_handle handle)
|
||||
{
|
||||
unsigned linenum=0;
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
bool done = false;
|
||||
#endif
|
||||
|
||||
std::string line;
|
||||
while(std::getline(src, line)) {
|
||||
linenum++;
|
||||
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
if(done) {
|
||||
check_trailing(line);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
yajl_status sts = yajl_parse(handle, (const unsigned char*)line.c_str(), line.size());
|
||||
|
||||
switch(sts) {
|
||||
case yajl_status_ok: {
|
||||
size_t consumed = yajl_get_bytes_consumed(handle);
|
||||
|
||||
if(consumed<line.size()) {
|
||||
check_trailing(line.substr(consumed));
|
||||
}
|
||||
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
done = true;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case yajl_status_client_canceled:
|
||||
return false;
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
case yajl_status_insufficient_data:
|
||||
// continue with next line
|
||||
break;
|
||||
#endif
|
||||
case yajl_status_error:
|
||||
{
|
||||
std::ostringstream msg;
|
||||
unsigned char *raw = yajl_get_error(handle, 1, (const unsigned char*)line.c_str(), line.size());
|
||||
if(!raw) {
|
||||
msg<<"Unknown error on line "<<linenum;
|
||||
} else {
|
||||
try {
|
||||
msg<<"Error on line "<<linenum<<" : "<<(const char*)raw;
|
||||
}catch(...){
|
||||
yajl_free_error(handle, raw);
|
||||
throw;
|
||||
}
|
||||
yajl_free_error(handle, raw);
|
||||
}
|
||||
throw std::runtime_error(msg.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!src.eof() || src.bad()) {
|
||||
std::ostringstream msg;
|
||||
msg<<"I/O error after line "<<linenum;
|
||||
throw std::runtime_error(msg.str());
|
||||
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
} else if(!done) {
|
||||
switch(yajl_parse_complete(handle)) {
|
||||
#else
|
||||
} else {
|
||||
switch(yajl_complete_parse(handle)) {
|
||||
#endif
|
||||
case yajl_status_ok:
|
||||
break;
|
||||
case yajl_status_client_canceled:
|
||||
return false;
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
case yajl_status_insufficient_data:
|
||||
throw std::runtime_error("unexpected end of input");
|
||||
#endif
|
||||
case yajl_status_error:
|
||||
throw std::runtime_error("Error while completing parsing");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#endif // EPICS_VERSION_INT
|
||||
@@ -1,352 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvdVersion.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/valueBuilder.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
|
||||
#include "pv/json.h"
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
using pvd::yajl::integer_arg;
|
||||
using pvd::yajl::size_arg;
|
||||
|
||||
namespace {
|
||||
struct context {
|
||||
|
||||
std::string msg;
|
||||
|
||||
struct frame {
|
||||
pvd::PVFieldPtr fld;
|
||||
pvd::BitSet *assigned;
|
||||
frame(const pvd::PVFieldPtr& fld, pvd::BitSet *assigned)
|
||||
:fld(fld), assigned(assigned)
|
||||
{}
|
||||
};
|
||||
|
||||
typedef std::vector<frame> stack_t;
|
||||
stack_t stack;
|
||||
|
||||
context(const pvd::PVFieldPtr& root, pvd::BitSet *assigned)
|
||||
{
|
||||
stack.push_back(frame(root, assigned));
|
||||
}
|
||||
};
|
||||
|
||||
#define TRY context *self = (context*)ctx; assert(!self->stack.empty()); try
|
||||
|
||||
#define CATCH() catch(std::exception& e) { if(self->msg.empty()) self->msg = e.what(); return 0; }
|
||||
|
||||
int jtree_null(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
self->msg = "NULL value not permitted";
|
||||
return 0;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
template<typename PVScalarT, typename PVArrayT>
|
||||
void valueAssign(context *self, typename PVScalarT::value_type val)
|
||||
{
|
||||
assert(!self->stack.empty());
|
||||
context::frame& back = self->stack.back();
|
||||
pvd::Type type(back.fld->getField()->getType());
|
||||
if(type==pvd::scalar) {
|
||||
pvd::PVScalar* fld(static_cast<pvd::PVScalar*>(back.fld.get()));
|
||||
|
||||
fld->putFrom(val);
|
||||
if(back.assigned)
|
||||
back.assigned->set(fld->getFieldOffset());
|
||||
self->stack.pop_back();
|
||||
// structure at the top of the stack
|
||||
|
||||
} else if(type==pvd::scalarArray) {
|
||||
pvd::PVScalarArray *fld(static_cast<pvd::PVScalarArray*>(back.fld.get()));
|
||||
|
||||
pvd::shared_vector<const void> carr;
|
||||
fld->getAs(carr);
|
||||
|
||||
switch(carr.original_type())
|
||||
{
|
||||
#define CASE_STRING
|
||||
#define CASE_REAL_INT64
|
||||
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case epics::pvData::pv##PVACODE: { \
|
||||
pvd::shared_vector<const PVATYPE> arr(pvd::static_shared_vector_cast<const PVATYPE>(carr)); \
|
||||
pvd::shared_vector<PVATYPE> tarr(pvd::thaw(arr)); \
|
||||
tarr.push_back(pvd::castUnsafe<PVATYPE>(val)); \
|
||||
carr = pvd::static_shared_vector_cast<const void>(pvd::freeze(tarr)); \
|
||||
} break;
|
||||
#include <pv/typemap.h>
|
||||
#undef CASE
|
||||
#undef CASE_REAL_INT64
|
||||
#undef CASE_STRING
|
||||
}
|
||||
|
||||
fld->putFrom(carr);
|
||||
|
||||
// leave array field at top of stack
|
||||
|
||||
} else if(type==pvd::union_) {
|
||||
pvd::PVUnion* fld(static_cast<pvd::PVUnion*>(back.fld.get()));
|
||||
pvd::UnionConstPtr utype(fld->getUnion());
|
||||
|
||||
if(utype->isVariant()) {
|
||||
typename PVScalarT::shared_pointer elem(pvd::getPVDataCreate()->createPVScalar<PVScalarT>());
|
||||
|
||||
elem->put(val);
|
||||
|
||||
fld->set(elem);
|
||||
|
||||
} else {
|
||||
// attempt automagic assignment
|
||||
|
||||
const pvd::StringArray& names = utype->getFieldNames();
|
||||
const pvd::FieldConstPtrArray types = utype->getFields();
|
||||
assert(names.size()==types.size());
|
||||
|
||||
bool assigned = false;
|
||||
for(size_t i=0, N=names.size(); i<N; i++) {
|
||||
if(types[i]->getType()!=pvd::scalar) continue;
|
||||
|
||||
pvd::PVScalarPtr ufld(fld->select<pvd::PVScalar>(i));
|
||||
try{
|
||||
ufld->putFrom(val);
|
||||
assigned = true;
|
||||
}catch(std::runtime_error&){
|
||||
if(i==N-1)
|
||||
throw;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
if(!assigned)
|
||||
throw std::runtime_error("Unable to select union member");
|
||||
}
|
||||
if(back.assigned)
|
||||
back.assigned->set(fld->getFieldOffset());
|
||||
self->stack.pop_back();
|
||||
// structure back at the top of the stack
|
||||
|
||||
} else {
|
||||
throw std::invalid_argument("Can't assign value");
|
||||
}
|
||||
}
|
||||
|
||||
int jtree_boolean(void * ctx, int boolVal)
|
||||
{
|
||||
TRY {
|
||||
valueAssign<pvd::PVBoolean, pvd::PVBooleanArray>(self, !!boolVal);
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_integer(void * ctx, integer_arg integerVal)
|
||||
{
|
||||
TRY {
|
||||
valueAssign<pvd::PVLong, pvd::PVLongArray>(self, integerVal);
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_double(void * ctx, double doubleVal)
|
||||
{
|
||||
TRY {
|
||||
valueAssign<pvd::PVDouble, pvd::PVDoubleArray>(self, doubleVal);
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_string(void * ctx, const unsigned char * stringVal,
|
||||
size_arg stringLen)
|
||||
{
|
||||
TRY {
|
||||
std::string val((const char*)stringVal, stringLen);
|
||||
valueAssign<pvd::PVString, pvd::PVStringArray>(self, val);
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_start_map(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
assert(!self->stack.empty());
|
||||
|
||||
context::frame& back = self->stack.back();
|
||||
pvd::Type type = back.fld->getField()->getType();
|
||||
if(type==pvd::structure) {
|
||||
// will fill in
|
||||
} else if(type==pvd::structureArray) {
|
||||
// starting new element in structure array
|
||||
pvd::PVStructureArray* sarr(static_cast<pvd::PVStructureArray*>(back.fld.get()));
|
||||
|
||||
pvd::PVStructurePtr elem(pvd::getPVDataCreate()->createPVStructure(sarr->getStructureArray()->getStructure()));
|
||||
|
||||
self->stack.push_back(context::frame(elem, 0));
|
||||
} else {
|
||||
throw std::runtime_error("Can't map (sub)structure");
|
||||
}
|
||||
|
||||
assert(self->stack.back().fld->getField()->getType()==pvd::structure);
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_map_key(void * ctx, const unsigned char * key,
|
||||
size_arg stringLen)
|
||||
{
|
||||
TRY {
|
||||
assert(!self->stack.empty());
|
||||
std::string name((const char*)key, stringLen);
|
||||
|
||||
// start_map() ensures we have a structure at the top of the stack
|
||||
pvd::PVStructure *fld = static_cast<pvd::PVStructure*>(self->stack.back().fld.get());
|
||||
|
||||
try {
|
||||
self->stack.push_back(context::frame(fld->getSubFieldT(name), self->stack.back().assigned));
|
||||
}catch(std::runtime_error& e){
|
||||
std::ostringstream strm;
|
||||
strm<<"At "<<fld->getFullName()<<" : "<<e.what()<<"\n";
|
||||
throw std::runtime_error(strm.str());
|
||||
}
|
||||
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_end_map(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
assert(!self->stack.empty());
|
||||
assert(self->stack.back().fld->getField()->getType()==pvd::structure);
|
||||
|
||||
context::frame elem(self->stack.back());
|
||||
self->stack.pop_back();
|
||||
|
||||
if(!self->stack.empty() && self->stack.back().fld->getField()->getType()==pvd::structureArray) {
|
||||
// append element to struct array
|
||||
pvd::PVStructureArray *sarr = static_cast<pvd::PVStructureArray*>(self->stack.back().fld.get());
|
||||
|
||||
pvd::PVStructureArray::const_svector cval;
|
||||
sarr->swap(cval);
|
||||
|
||||
pvd::PVStructureArray::svector val(pvd::thaw(cval));
|
||||
|
||||
val.push_back(std::tr1::static_pointer_cast<pvd::PVStructure>(elem.fld));
|
||||
|
||||
sarr->replace(pvd::freeze(val));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_start_array(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
assert(!self->stack.empty());
|
||||
pvd::PVFieldPtr& back(self->stack.back().fld);
|
||||
pvd::Type type = back->getField()->getType();
|
||||
if(type!=pvd::structureArray && type!=pvd::scalarArray)
|
||||
throw std::runtime_error("Can't assign array");
|
||||
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
int jtree_end_array(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
assert(!self->stack.empty());
|
||||
|
||||
if(self->stack.back().assigned)
|
||||
self->stack.back().assigned->set(self->stack.back().fld->getFieldOffset());
|
||||
self->stack.pop_back();
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
|
||||
yajl_callbacks jtree_cbs = {
|
||||
&jtree_null,
|
||||
&jtree_boolean,
|
||||
&jtree_integer,
|
||||
&jtree_double,
|
||||
NULL, // number
|
||||
&jtree_string,
|
||||
&jtree_start_map,
|
||||
&jtree_map_key,
|
||||
&jtree_end_map,
|
||||
&jtree_start_array,
|
||||
&jtree_end_array,
|
||||
};
|
||||
|
||||
struct handler {
|
||||
yajl_handle handle;
|
||||
handler(yajl_handle handle) :handle(handle)
|
||||
{
|
||||
if(!handle)
|
||||
throw std::runtime_error("Failed to allocate yajl handle");
|
||||
}
|
||||
~handler() {
|
||||
yajl_free(handle);
|
||||
}
|
||||
operator yajl_handle() { return handle; }
|
||||
};
|
||||
|
||||
struct noop {
|
||||
void operator()(pvd::PVField*) {}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
|
||||
epicsShareFunc
|
||||
void parseJSON(std::istream& strm,
|
||||
PVField& dest,
|
||||
BitSet *assigned)
|
||||
{
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
yajl_parser_config conf;
|
||||
memset(&conf, 0, sizeof(conf));
|
||||
conf.allowComments = 1;
|
||||
conf.checkUTF8 = 1;
|
||||
#endif
|
||||
|
||||
// we won't create refs to 'dest' which presist beyond this call.
|
||||
// however, it is convienent to treat 'dest' in the same manner as
|
||||
// any union/structureArray memebers it may contain.
|
||||
PVFieldPtr fakedest(&dest, noop());
|
||||
|
||||
context ctxt(fakedest, assigned);
|
||||
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
handler handle(yajl_alloc(&jtree_cbs, &conf, NULL, &ctxt));
|
||||
#else
|
||||
handler handle(yajl_alloc(&jtree_cbs, NULL, &ctxt));
|
||||
|
||||
yajl_config(handle, yajl_allow_comments, 1);
|
||||
#endif
|
||||
|
||||
|
||||
if(!yajl_parse_helper(strm, handle))
|
||||
throw std::runtime_error(ctxt.msg);
|
||||
|
||||
if(!ctxt.stack.empty())
|
||||
throw std::logic_error("field stack not empty");
|
||||
assert(fakedest.use_count()==1);
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#endif // EPICS_VERSION_INT
|
||||
|
||||
@@ -1,236 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvdVersion.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/valueBuilder.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
|
||||
#include "pv/json.h"
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
|
||||
namespace {
|
||||
|
||||
struct args {
|
||||
std::ostream& strm;
|
||||
const pvd::JSONPrintOptions& opts;
|
||||
|
||||
unsigned indent;
|
||||
|
||||
args(std::ostream& strm,
|
||||
const pvd::JSONPrintOptions& opts)
|
||||
:strm(strm)
|
||||
,opts(opts)
|
||||
,indent(opts.indent)
|
||||
{}
|
||||
|
||||
void doIntent() {
|
||||
if(!opts.multiLine) return;
|
||||
strm.put('\n');
|
||||
unsigned i=indent;
|
||||
while(i--) strm.put(' ');
|
||||
}
|
||||
};
|
||||
|
||||
void show_field(args& A, const pvd::PVField* fld, const pvd::BitSet *mask);
|
||||
|
||||
void show_struct(args& A, const pvd::PVStructure* fld, const pvd::BitSet *mask)
|
||||
{
|
||||
const pvd::StructureConstPtr& type = fld->getStructure();
|
||||
const pvd::PVFieldPtrArray& children = fld->getPVFields();
|
||||
|
||||
const pvd::StringArray& names = type->getFieldNames();
|
||||
|
||||
A.strm.put('{');
|
||||
A.indent++;
|
||||
|
||||
bool first = true;
|
||||
for(size_t i=0, N=names.size(); i<N; i++)
|
||||
{
|
||||
if(mask && !mask->get(children[i]->getFieldOffset())) continue;
|
||||
|
||||
if(first)
|
||||
first = false;
|
||||
else
|
||||
A.strm.put(',');
|
||||
A.doIntent();
|
||||
A.strm<<'\"'<<names[i]<<"\": ";
|
||||
show_field(A, children[i].get(), mask);
|
||||
}
|
||||
|
||||
A.indent--;
|
||||
A.doIntent();
|
||||
A.strm.put('}');
|
||||
}
|
||||
|
||||
void show_field(args& A, const pvd::PVField* fld, const pvd::BitSet *mask)
|
||||
{
|
||||
switch(fld->getField()->getType())
|
||||
{
|
||||
case pvd::scalar:
|
||||
{
|
||||
const pvd::PVScalar *scalar=static_cast<const pvd::PVScalar*>(fld);
|
||||
if(scalar->getScalar()->getScalarType()==pvd::pvString) {
|
||||
A.strm<<'\"'<<scalar->getAs<std::string>()<<'\"';
|
||||
} else {
|
||||
A.strm<<scalar->getAs<std::string>();
|
||||
}
|
||||
}
|
||||
return;
|
||||
case pvd::scalarArray:
|
||||
{
|
||||
const pvd::PVScalarArray *scalar=static_cast<const pvd::PVScalarArray*>(fld);
|
||||
const bool isstring = scalar->getScalarArray()->getElementType()==pvd::pvString;
|
||||
|
||||
pvd::shared_vector<const void> arr;
|
||||
scalar->getAs<void>(arr);
|
||||
|
||||
pvd::shared_vector<const std::string> sarr(pvd::shared_vector_convert<const std::string>(arr));
|
||||
|
||||
A.strm.put('[');
|
||||
for(size_t i=0, N=sarr.size(); i<N; i++) {
|
||||
if(i!=0)
|
||||
A.strm.put(',');
|
||||
if(isstring)
|
||||
A.strm.put('\"');
|
||||
A.strm<<sarr[i];
|
||||
if(isstring)
|
||||
A.strm.put('\"');
|
||||
}
|
||||
A.strm.put(']');
|
||||
}
|
||||
return;
|
||||
case pvd::structure:
|
||||
show_struct(A, static_cast<const pvd::PVStructure*>(fld), mask);
|
||||
return;
|
||||
case pvd::structureArray:
|
||||
{
|
||||
pvd::PVStructureArray::const_svector arr(static_cast<const pvd::PVStructureArray*>(fld)->view());
|
||||
A.strm.put('[');
|
||||
A.indent++;
|
||||
|
||||
for(size_t i=0, N=arr.size(); i<N; i++) {
|
||||
if(i!=0)
|
||||
A.strm.put(',');
|
||||
A.doIntent();
|
||||
if(arr[i])
|
||||
show_struct(A, arr[i].get(), 0);
|
||||
else
|
||||
A.strm<<"NULL";
|
||||
}
|
||||
|
||||
A.indent--;
|
||||
A.doIntent();
|
||||
A.strm.put(']');
|
||||
}
|
||||
return;
|
||||
case pvd::union_:
|
||||
{
|
||||
const pvd::PVUnion *U=static_cast<const pvd::PVUnion*>(fld);
|
||||
const pvd::PVField::const_shared_pointer& C(U->get());
|
||||
|
||||
if(!C) {
|
||||
A.strm<<"null";
|
||||
} else {
|
||||
show_field(A, C.get(), 0);
|
||||
}
|
||||
}
|
||||
return;
|
||||
case pvd::unionArray: {
|
||||
const pvd::PVUnionArray *U=static_cast<const pvd::PVUnionArray*>(fld);
|
||||
pvd::PVUnionArray::const_svector arr(U->view());
|
||||
A.strm.put('[');
|
||||
A.indent++;
|
||||
|
||||
for(size_t i=0, N=arr.size(); i<N; i++) {
|
||||
if(i!=0)
|
||||
A.strm.put(',');
|
||||
A.doIntent();
|
||||
if(arr[i])
|
||||
show_field(A, arr[i].get(), 0);
|
||||
else
|
||||
A.strm<<"NULL";
|
||||
}
|
||||
|
||||
A.indent--;
|
||||
A.doIntent();
|
||||
A.strm.put(']');
|
||||
|
||||
}
|
||||
return;
|
||||
}
|
||||
// should not be reached
|
||||
if(A.opts.ignoreUnprintable)
|
||||
A.strm<<"// unprintable field type";
|
||||
else
|
||||
throw std::runtime_error("Encountered unprintable field type");
|
||||
}
|
||||
|
||||
void expandBS(const pvd::PVStructure& top, pvd::BitSet& mask, bool parents) {
|
||||
if(mask.get(0)) { // special handling because getSubField(0) not allowed
|
||||
// wildcard
|
||||
for(size_t idx=1, N=top.getNumberFields(); idx<N; idx++) {
|
||||
mask.set(idx);
|
||||
}
|
||||
|
||||
} else {
|
||||
for(pvd::int32 idx = mask.nextSetBit(0), N=top.getNumberFields(); idx>=0 && idx<N; idx=mask.nextSetBit(idx+1)) {
|
||||
pvd::PVField::const_shared_pointer fld = top.getSubFieldT(idx);
|
||||
|
||||
// look forward and mark all children
|
||||
for(size_t i=idx+1, N=fld->getNextFieldOffset(); i<N; i++)
|
||||
mask.set(i);
|
||||
|
||||
if(parents) {
|
||||
// look back and mark all parents
|
||||
// we've already stepped past all parents so siblings will not be automatically marked
|
||||
for(const pvd::PVStructure *parent = fld->getParent(); parent; parent = parent->getParent()) {
|
||||
mask.set(parent->getFieldOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
|
||||
JSONPrintOptions::JSONPrintOptions()
|
||||
:multiLine(true)
|
||||
,ignoreUnprintable(true)
|
||||
,indent(0)
|
||||
{}
|
||||
|
||||
void printJSON(std::ostream& strm,
|
||||
const PVStructure& val,
|
||||
const BitSet& mask,
|
||||
const JSONPrintOptions& opts)
|
||||
{
|
||||
args A(strm, opts);
|
||||
pvd::BitSet emask(mask);
|
||||
expandBS(val, emask, true);
|
||||
if(!emask.get(0)) return;
|
||||
show_struct(A, &val, &emask);
|
||||
}
|
||||
|
||||
void printJSON(std::ostream& strm,
|
||||
const PVField& val,
|
||||
const JSONPrintOptions& opts)
|
||||
{
|
||||
args A(strm, opts);
|
||||
show_field(A, &val, 0);
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#endif // EPICS_VERSION_INT
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
#ifndef PV_JSON_H
|
||||
#define PV_JSON_H
|
||||
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include <pv/pvdVersion.h>
|
||||
#include <pv/pvData.h>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define pvjson_epicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <yajl_parse.h>
|
||||
|
||||
#ifdef pvjson_epicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# include "shareLib.h"
|
||||
#endif
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
|
||||
class BitSet;
|
||||
|
||||
/** @defgroup pvjson JSON print/parse
|
||||
*
|
||||
* Printing PVField as JSON and parsing JSON into PVField.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
//! Options used during printing
|
||||
struct epicsShareClass JSONPrintOptions
|
||||
{
|
||||
bool multiLine; //!< include new lines
|
||||
bool ignoreUnprintable;//!< ignore union/union array when encountered
|
||||
unsigned indent; //!< Initial indentation (# of spaces)
|
||||
JSONPrintOptions();
|
||||
};
|
||||
|
||||
/** Print PVStructure as JSON
|
||||
*
|
||||
* 'mask' selects those fields which will be printed.
|
||||
* @version Overload added after 7.0.0
|
||||
*/
|
||||
epicsShareFunc
|
||||
void printJSON(std::ostream& strm,
|
||||
const PVStructure& val,
|
||||
const BitSet& mask,
|
||||
const JSONPrintOptions& opts = JSONPrintOptions());
|
||||
|
||||
/** Print PVField as JSON
|
||||
* @version Overload added after 7.0.0
|
||||
*/
|
||||
epicsShareFunc
|
||||
void printJSON(std::ostream& strm,
|
||||
const PVField& val,
|
||||
const JSONPrintOptions& opts = JSONPrintOptions());
|
||||
|
||||
// To be deprecated in favor of previous form
|
||||
FORCE_INLINE
|
||||
void printJSON(std::ostream& strm,
|
||||
const PVField::const_shared_pointer& val,
|
||||
const JSONPrintOptions& opts = JSONPrintOptions())
|
||||
{
|
||||
printJSON(strm, *val, opts);
|
||||
}
|
||||
|
||||
/** Parse JSON text into a PVStructure
|
||||
*
|
||||
* Restrictions:
|
||||
*
|
||||
* - Top level must be {} dict/object
|
||||
* - field values must be number, string, array, or dict/object
|
||||
* - array values must be number or string
|
||||
*/
|
||||
epicsShareFunc
|
||||
PVStructure::shared_pointer parseJSON(std::istream& strm);
|
||||
|
||||
/** Parse JSON and store into the provided PVStructure.
|
||||
*
|
||||
* Restrictions:
|
||||
*
|
||||
* - array of union not supported
|
||||
* - Only scalar value assigned to union
|
||||
*
|
||||
* @param strm Read JSON text from stream
|
||||
* @param dest Store in fields of this structure
|
||||
* @param assigned Which fields of _dest_ were assigned. (Optional)
|
||||
* @throws std::runtime_error on failure. dest and assigned may be modified.
|
||||
* @version Overload added after 7.0.0
|
||||
*/
|
||||
epicsShareFunc
|
||||
void parseJSON(std::istream& strm,
|
||||
PVField& dest,
|
||||
BitSet *assigned=0);
|
||||
|
||||
// To be deprecated in favor of previous form
|
||||
FORCE_INLINE
|
||||
void parseJSON(std::istream& strm,
|
||||
const PVField::shared_pointer& dest,
|
||||
BitSet *assigned=0)
|
||||
{
|
||||
parseJSON(strm, *dest, assigned);
|
||||
}
|
||||
|
||||
|
||||
/** Wrapper around yajl_parse()
|
||||
*
|
||||
* Parse entire input stream.
|
||||
* Errors if extranious non-whitespace found after the point were parsing completes.
|
||||
*
|
||||
* @param src The stream from which input charactors are read
|
||||
* @param handle A parser handle previously allocated with yajl_alloc(). Not free'd on success or failure.
|
||||
*
|
||||
* @returns true if parsing completes successfully. false if parsing cancelled by callback. throws other errors
|
||||
*
|
||||
* @note The form of this call depends on EPICS_YAJL_VERSION
|
||||
*/
|
||||
epicsShareFunc
|
||||
bool yajl_parse_helper(std::istream& src,
|
||||
yajl_handle handle);
|
||||
|
||||
namespace yajl {
|
||||
// undef implies API version 0
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
typedef long integer_arg;
|
||||
typedef unsigned size_arg;
|
||||
#else
|
||||
typedef long long integer_arg;
|
||||
typedef size_t size_arg;
|
||||
#endif
|
||||
} // namespace epics::pvData::yajl
|
||||
|
||||
/** @} */
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#else
|
||||
# error JSON parser requires EPICS Base >= 3.15.0.1
|
||||
#endif // EPICS_VERSION_INT
|
||||
|
||||
#endif // PV_JSON_H
|
||||
@@ -4,6 +4,7 @@ SRC_DIRS += $(PVDATA_SRC)/misc
|
||||
|
||||
INC += pv/noDefaultMethods.h
|
||||
INC += pv/lock.h
|
||||
INC += pv/requester.h
|
||||
INC += pv/serialize.h
|
||||
INC += pv/bitSet.h
|
||||
INC += pv/byteBuffer.h
|
||||
@@ -11,29 +12,32 @@ INC += pv/epicsException.h
|
||||
INC += pv/serializeHelper.h
|
||||
INC += pv/event.h
|
||||
INC += pv/thread.h
|
||||
INC += pv/executor.h
|
||||
INC += pv/timeFunction.h
|
||||
INC += pv/timer.h
|
||||
INC += pv/queue.h
|
||||
INC += pv/messageQueue.h
|
||||
INC += pv/destroyable.h
|
||||
INC += pv/status.h
|
||||
INC += pv/sharedPtr.h
|
||||
INC += pv/debugPtr.h
|
||||
INC += pv/localStaticLock.h
|
||||
INC += pv/typeCast.h
|
||||
INC += pv/sharedVector.h
|
||||
INC += pv/templateMeta.h
|
||||
INC += pv/current_function.h
|
||||
INC += pv/pvUnitTest.h
|
||||
INC += pv/reftrack.h
|
||||
INC += pv/anyscalar.h
|
||||
|
||||
LIBSRCS += byteBuffer.cpp
|
||||
LIBSRCS += bitSet.cpp
|
||||
LIBSRCS += epicsException.cpp
|
||||
LIBSRCS += requester.cpp
|
||||
LIBSRCS += serializeHelper.cpp
|
||||
LIBSRCS += event.cpp
|
||||
LIBSRCS += executor.cpp
|
||||
LIBSRCS += timeFunction.cpp
|
||||
LIBSRCS += timer.cpp
|
||||
LIBSRCS += status.cpp
|
||||
LIBSRCS += messageQueue.cpp
|
||||
LIBSRCS += localStaticLock.cpp
|
||||
LIBSRCS += typeCast.cpp
|
||||
LIBSRCS += thread.cpp
|
||||
LIBSRCS += parseToPOD.cpp
|
||||
LIBSRCS += pvUnitTest.cpp
|
||||
LIBSRCS += debugPtr.cpp
|
||||
LIBSRCS += reftrack.cpp
|
||||
LIBSRCS += anyscalar.cpp
|
||||
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
|
||||
#include <epicsAssert.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <shareLib.h>
|
||||
|
||||
#include "pv/anyscalar.h"
|
||||
|
||||
namespace epics {namespace pvData {
|
||||
|
||||
AnyScalar::AnyScalar(ScalarType type, const void *buf)
|
||||
{
|
||||
if(type==pvString) {
|
||||
new (_wrap.blob) std::string(*static_cast<const std::string*>(buf));
|
||||
|
||||
} else {
|
||||
memcpy(_wrap.blob, buf, ScalarTypeFunc::elementSize(type));
|
||||
}
|
||||
|
||||
_stype = type;
|
||||
}
|
||||
|
||||
AnyScalar::AnyScalar(const AnyScalar& o)
|
||||
:_stype(o._stype)
|
||||
{
|
||||
if(o._stype==pvString) {
|
||||
new (_wrap.blob) std::string(o._as<std::string>());
|
||||
} else if(o._stype!=(ScalarType)-1) {
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
}
|
||||
}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
AnyScalar::AnyScalar(AnyScalar&& o)
|
||||
:_stype(o._stype)
|
||||
{
|
||||
typedef std::string string;
|
||||
if(o._stype==pvString) {
|
||||
new (_wrap.blob) std::string();
|
||||
_as<std::string>() = std::move(o._as<std::string>());
|
||||
o._as<string>().~string();
|
||||
} else if(o._stype!=(ScalarType)-1) {
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
}
|
||||
o._stype = (ScalarType)-1;
|
||||
}
|
||||
#endif
|
||||
|
||||
void AnyScalar::clear() {
|
||||
if(_stype==pvString) {
|
||||
typedef std::string string;
|
||||
_as<string>().~string();
|
||||
}
|
||||
// other types need no cleanup
|
||||
_stype = (ScalarType)-1;
|
||||
}
|
||||
|
||||
void AnyScalar::swap(AnyScalar& o) {
|
||||
typedef std::string string;
|
||||
switch((int)_stype) {
|
||||
case -1:
|
||||
switch((int)o._stype) {
|
||||
case -1:
|
||||
// nil <-> nil
|
||||
break;
|
||||
case pvString:
|
||||
// nil <-> string
|
||||
new (_wrap.blob) std::string();
|
||||
_as<std::string>().swap(o._as<std::string>());
|
||||
o._as<std::string>().~string();
|
||||
break;
|
||||
default:
|
||||
// nil <-> non-string
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case pvString:
|
||||
switch((int)o._stype) {
|
||||
case -1:
|
||||
// string <-> nil
|
||||
new (o._wrap.blob) std::string();
|
||||
_as<std::string>().swap(o._as<std::string>());
|
||||
_as<std::string>().~string();
|
||||
break;
|
||||
case pvString:
|
||||
// string <-> string
|
||||
_as<std::string>().swap(o._as<std::string>());
|
||||
break;
|
||||
default: {
|
||||
// string <-> non-string
|
||||
std::string temp;
|
||||
temp.swap(_as<std::string>());
|
||||
|
||||
_as<std::string>().~string();
|
||||
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
|
||||
new (o._wrap.blob) std::string();
|
||||
temp.swap(o._as<std::string>());
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
switch((int)o._stype) {
|
||||
case -1:
|
||||
// non-string <-> nil
|
||||
memcpy(o._wrap.blob, _wrap.blob, sizeof(_largest_blob));
|
||||
break;
|
||||
case pvString: {
|
||||
// non-string <-> string
|
||||
std::string temp;
|
||||
temp.swap(o._as<std::string>());
|
||||
|
||||
o._as<std::string>().~string();
|
||||
|
||||
memcpy(o._wrap.blob, _wrap.blob, sizeof(_largest_blob));
|
||||
|
||||
new (_wrap.blob) std::string();
|
||||
temp.swap(_as<std::string>());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// non-string <-> non-string
|
||||
_largest_blob temp;
|
||||
memcpy(&temp, _wrap.blob, sizeof(_largest_blob));
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
memcpy(o._wrap.blob, &temp, sizeof(_largest_blob));
|
||||
// std::swap(o._wrap.blob, _wrap.blob); // gcc <=4.3 doesn't like this
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
std::swap(_stype, o._stype);
|
||||
}
|
||||
const void* AnyScalar::bufferUnsafe() const {
|
||||
if(_stype==pvString) {
|
||||
return as<std::string>().c_str();
|
||||
} else {
|
||||
return _wrap.blob;
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& strm, const AnyScalar& v)
|
||||
{
|
||||
switch(v.type()) {
|
||||
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case pv ## PVACODE: strm<<v._as<PVATYPE>(); break;
|
||||
#define CASE_REAL_INT64
|
||||
#define CASE_STRING
|
||||
#include "pv/typemap.h"
|
||||
#undef CASE
|
||||
#undef CASE_REAL_INT64
|
||||
#undef CASE_STRING
|
||||
default:
|
||||
strm<<"(nil)"; break;
|
||||
}
|
||||
return strm;
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
|
||||
#include <epicsMutex.h>
|
||||
|
||||
@@ -55,19 +54,6 @@ namespace epics { namespace pvData {
|
||||
words.reserve((nbits == 0) ? 1 : WORD_INDEX(nbits-1) + 1);
|
||||
}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
BitSet::BitSet(std::initializer_list<uint32> I)
|
||||
{
|
||||
// optimistically guess that highest bit is last (not required)
|
||||
words.reserve((I.size() == 0) ? 1 : WORD_INDEX(*(I.end()-1)) + 1);
|
||||
|
||||
for(uint32 idx : I)
|
||||
{
|
||||
set(idx);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
BitSet::~BitSet() {}
|
||||
|
||||
void BitSet::recalculateWordsInUse() {
|
||||
@@ -89,7 +75,7 @@ namespace epics { namespace pvData {
|
||||
ensureCapacity(wordIndex+1);
|
||||
}
|
||||
|
||||
BitSet& BitSet::flip(uint32 bitIndex) {
|
||||
void BitSet::flip(uint32 bitIndex) {
|
||||
|
||||
uint32 wordIdx = WORD_INDEX(bitIndex);
|
||||
expandTo(wordIdx);
|
||||
@@ -97,27 +83,25 @@ namespace epics { namespace pvData {
|
||||
words[wordIdx] ^= (((uint64)1) << WORD_OFFSET(bitIndex));
|
||||
|
||||
recalculateWordsInUse();
|
||||
return *this;
|
||||
}
|
||||
|
||||
BitSet& BitSet::set(uint32 bitIndex) {
|
||||
void BitSet::set(uint32 bitIndex) {
|
||||
|
||||
uint32 wordIdx = WORD_INDEX(bitIndex);
|
||||
expandTo(wordIdx);
|
||||
|
||||
words[wordIdx] |= (((uint64)1) << WORD_OFFSET(bitIndex));
|
||||
return *this;
|
||||
}
|
||||
|
||||
BitSet& BitSet::clear(uint32 bitIndex) {
|
||||
void BitSet::clear(uint32 bitIndex) {
|
||||
|
||||
uint32 wordIdx = WORD_INDEX(bitIndex);
|
||||
if (wordIdx < words.size()) {
|
||||
words[wordIdx] &= ~(((uint64)1) << WORD_OFFSET(bitIndex));
|
||||
if (wordIdx >= words.size())
|
||||
return;
|
||||
|
||||
recalculateWordsInUse();
|
||||
}
|
||||
return *this;
|
||||
words[wordIdx] &= ~(((uint64)1) << WORD_OFFSET(bitIndex));
|
||||
|
||||
recalculateWordsInUse();
|
||||
}
|
||||
|
||||
void BitSet::set(uint32 bitIndex, bool value) {
|
||||
@@ -211,20 +195,6 @@ namespace epics { namespace pvData {
|
||||
return words.size() * BITS_PER_WORD;
|
||||
}
|
||||
|
||||
bool BitSet::logical_and(const BitSet& set) const
|
||||
{
|
||||
size_t nwords = std::min(words.size(), set.words.size());
|
||||
for(size_t i=0; i<nwords; i++) {
|
||||
if(words[i] & set.words[i])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool BitSet::logical_or(const BitSet& set) const
|
||||
{
|
||||
return !words.empty() || !set.words.empty();
|
||||
}
|
||||
|
||||
BitSet& BitSet::operator&=(const BitSet& set) {
|
||||
// Check for self-assignment!
|
||||
if (this == &set) return *this;
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsGuard.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/debugPtr.h>
|
||||
|
||||
namespace {
|
||||
typedef epicsGuard<epicsMutex> Guard;
|
||||
}
|
||||
|
||||
namespace epics {
|
||||
namespace debug {
|
||||
|
||||
// joins together a group of ptr_base instances
|
||||
// which all have the same dtor
|
||||
struct tracker {
|
||||
epicsMutex mutex;
|
||||
ptr_base::ref_set_t refs;
|
||||
};
|
||||
|
||||
void shared_ptr_base::track_new()
|
||||
{
|
||||
if(track) {
|
||||
Guard G(track->mutex);
|
||||
track->refs.insert(this);
|
||||
}
|
||||
snap_stack();
|
||||
}
|
||||
|
||||
// create new tracker if ptr!=nullptr, otherwise clear
|
||||
void shared_ptr_base::track_new(void* ptr)
|
||||
{
|
||||
track_clear();
|
||||
if(ptr){
|
||||
track.reset(new tracker);
|
||||
Guard G(track->mutex);
|
||||
track->refs.insert(this);
|
||||
}
|
||||
snap_stack();
|
||||
}
|
||||
|
||||
void shared_ptr_base::track_assign(const shared_ptr_base &o)
|
||||
{
|
||||
if(track!=o.track) {
|
||||
track_clear();
|
||||
track = o.track;
|
||||
if(track) {
|
||||
Guard G(track->mutex);
|
||||
track->refs.insert(this);
|
||||
}
|
||||
snap_stack();
|
||||
}
|
||||
}
|
||||
|
||||
void shared_ptr_base::track_clear()
|
||||
{
|
||||
if(track) {
|
||||
Guard G(track->mutex);
|
||||
track->refs.erase(this);
|
||||
}
|
||||
track.reset();
|
||||
#ifndef EXCEPT_USE_NONE
|
||||
m_depth = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void shared_ptr_base::swap(shared_ptr_base &o)
|
||||
{
|
||||
// we cheat a bit here to avoid lock order, and to lock only twice
|
||||
if(track) {
|
||||
Guard G(track->mutex);
|
||||
track->refs.insert(&o);
|
||||
track->refs.erase(this);
|
||||
}
|
||||
track.swap(o.track);
|
||||
if(track) {
|
||||
Guard G(track->mutex);
|
||||
track->refs.insert(this);
|
||||
track->refs.erase(&o);
|
||||
}
|
||||
//TODO: keep original somehow???
|
||||
snap_stack();
|
||||
o.snap_stack();
|
||||
}
|
||||
|
||||
void shared_ptr_base::snap_stack()
|
||||
{
|
||||
if(!track) {
|
||||
#ifndef EXCEPT_USE_NONE
|
||||
m_depth = 0;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
#if defined(EXCEPT_USE_BACKTRACE)
|
||||
{
|
||||
m_depth=backtrace(m_stack,EXCEPT_DEPTH);
|
||||
}
|
||||
#else
|
||||
{}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void shared_ptr_base::show_stack(std::ostream& strm) const
|
||||
{
|
||||
strm<<"ptr "<<this;
|
||||
#ifndef EXCEPT_USE_NONE
|
||||
if(m_depth<=0) return;
|
||||
#endif
|
||||
#if 0 && defined(EXCEPT_USE_BACKTRACE)
|
||||
{
|
||||
|
||||
char **symbols=backtrace_symbols(m_stack, m_depth);
|
||||
|
||||
strm<<": ";
|
||||
for(int i=0; i<m_depth; i++) {
|
||||
strm<<symbols[i]<<", ";
|
||||
}
|
||||
|
||||
std::free(symbols);
|
||||
}
|
||||
#elif !defined(EXCEPT_USE_NONE)
|
||||
{
|
||||
strm<<": ";
|
||||
for(int i=0; i<m_depth; i++) {
|
||||
strm<<std::hex<<m_stack[i]<<" ";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void ptr_base::show_refs(std::ostream& strm, bool self, bool weak) const
|
||||
{
|
||||
if(!track) {
|
||||
strm<<"# No refs\n";
|
||||
} else {
|
||||
Guard G(track->mutex);
|
||||
for(auto ref : track->refs) {
|
||||
if(!self && ref==this) continue;
|
||||
strm<<'#';
|
||||
ref->show_stack(strm);
|
||||
strm<<'\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ptr_base::spy_refs(ref_set_t &refs) const
|
||||
{
|
||||
if(track) {
|
||||
Guard G(track->mutex);
|
||||
refs.insert(track->refs.begin(), track->refs.end());
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace epics::debug
|
||||
|
||||
#endif // __cplusplus>=201103L
|
||||
98
src/misc/executor.cpp
Normal file
98
src/misc/executor.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
/* executor.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
|
||||
#include <epicsEvent.h>
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/executor.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
// special instance to stop the executor thread
|
||||
class ExecutorShutdown : public Command {
|
||||
virtual void command();
|
||||
};
|
||||
|
||||
void ExecutorShutdown::command()
|
||||
{
|
||||
}
|
||||
|
||||
static
|
||||
std::tr1::shared_ptr<Command> shutdown(new ExecutorShutdown());
|
||||
|
||||
|
||||
Executor::Executor(string const & threadName,ThreadPriority priority)
|
||||
: thread(threadName,priority,this)
|
||||
{
|
||||
}
|
||||
|
||||
Executor::~Executor()
|
||||
{
|
||||
execute(shutdown);
|
||||
stopped.wait();
|
||||
// The thread signals 'stopped' while still holding
|
||||
// the lock. By taking it we wait for the run() function
|
||||
// to actually return
|
||||
Lock xx(mutex);
|
||||
head.reset();
|
||||
tail.reset();
|
||||
}
|
||||
|
||||
void Executor::run()
|
||||
{
|
||||
Lock xx(mutex);
|
||||
while(true) {
|
||||
while(!head.get()) {
|
||||
xx.unlock();
|
||||
moreWork.wait();
|
||||
xx.lock();
|
||||
}
|
||||
CommandPtr command = head;
|
||||
head = command->next;
|
||||
if(!command.get()) continue;
|
||||
if(command.get()==shutdown.get()) break;
|
||||
xx.unlock();
|
||||
try {
|
||||
command->command();
|
||||
}catch(std::exception& e){
|
||||
//TODO: feed into logging mechanism
|
||||
fprintf(stderr, "Executor: Unhandled exception: %s",e.what());
|
||||
}catch(...){
|
||||
fprintf(stderr, "Executor: Unhandled exception");
|
||||
}
|
||||
|
||||
xx.lock();
|
||||
}
|
||||
stopped.signal();
|
||||
}
|
||||
|
||||
void Executor::execute(CommandPtr const & command)
|
||||
{
|
||||
Lock xx(mutex);
|
||||
command->next.reset();
|
||||
if(!head.get()) {
|
||||
head = command;
|
||||
moreWork.signal();
|
||||
return;
|
||||
}
|
||||
CommandPtr tail = head;
|
||||
while(tail->next) tail = tail->next;
|
||||
tail->next = command;
|
||||
}
|
||||
|
||||
}}
|
||||
39
src/misc/localStaticLock.cpp
Normal file
39
src/misc/localStaticLock.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
/* localStaticLock.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mse
|
||||
*/
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/localStaticLock.h>
|
||||
|
||||
static int nifty_counter;
|
||||
static epics::pvData::Mutex* g_localStaticInitMutex;
|
||||
|
||||
epics::pvData::Mutex& getLocalStaticInitMutex()
|
||||
{
|
||||
return *g_localStaticInitMutex;
|
||||
}
|
||||
|
||||
|
||||
// The counter is initialized at load-time, i.e., before any of the static objects are initialized.
|
||||
MutexInitializer::MutexInitializer ()
|
||||
{
|
||||
if (0 == nifty_counter++)
|
||||
{
|
||||
// Initialize static members.
|
||||
g_localStaticInitMutex = new epics::pvData::Mutex();
|
||||
}
|
||||
}
|
||||
|
||||
MutexInitializer::~MutexInitializer ()
|
||||
{
|
||||
if (0 == --nifty_counter)
|
||||
{
|
||||
// Clean-up.
|
||||
delete g_localStaticInitMutex;
|
||||
}
|
||||
}
|
||||
115
src/misc/messageQueue.cpp
Normal file
115
src/misc/messageQueue.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
/* messageQueue.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
#include <string>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/messageQueue.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
MessageNode::MessageNode()
|
||||
: messageType(infoMessage)
|
||||
{}
|
||||
|
||||
string MessageNode::getMessage() const
|
||||
{
|
||||
return message;
|
||||
}
|
||||
|
||||
MessageType MessageNode::getMessageType() const
|
||||
{
|
||||
return messageType;
|
||||
}
|
||||
|
||||
MessageQueuePtr MessageQueue::create(int size)
|
||||
{
|
||||
MessageNodePtrArray nodeArray;
|
||||
nodeArray.reserve(size);
|
||||
for(int i=0; i<size; i++) {
|
||||
nodeArray.push_back(
|
||||
MessageNodePtr(new MessageNode()));
|
||||
}
|
||||
return std::tr1::shared_ptr<MessageQueue>(new MessageQueue(nodeArray));
|
||||
}
|
||||
|
||||
MessageQueue::MessageQueue(MessageNodePtrArray &data)
|
||||
: Queue<MessageNode>(data),
|
||||
overrun(0)
|
||||
{ }
|
||||
|
||||
MessageQueue::~MessageQueue()
|
||||
{
|
||||
}
|
||||
|
||||
MessageNodePtr &MessageQueue::get() {
|
||||
if(getNumberUsed()==0) return nullNode;
|
||||
lastGet = getUsed();
|
||||
return lastGet;
|
||||
}
|
||||
|
||||
void MessageQueue::release() {
|
||||
if(lastGet.get()==NULL) return;
|
||||
releaseUsed(lastGet);
|
||||
lastGet.reset();
|
||||
}
|
||||
bool MessageQueue::put(string message,MessageType messageType,bool replaceLast)
|
||||
{
|
||||
MessageNodePtr node = getFree();
|
||||
if(node.get()!= NULL) {
|
||||
node->message = message;
|
||||
node->messageType = messageType;
|
||||
lastPut = node;
|
||||
setUsed(node);
|
||||
return true;
|
||||
}
|
||||
overrun++;
|
||||
if(replaceLast) {
|
||||
node = lastPut;
|
||||
node->message = message;
|
||||
node->messageType = messageType;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MessageQueue::isEmpty()
|
||||
{
|
||||
int free = getNumberFree();
|
||||
if(free==capacity()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MessageQueue::isFull()
|
||||
{
|
||||
if(getNumberFree()==0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
int MessageQueue::getClearOverrun()
|
||||
{
|
||||
int num = overrun;
|
||||
overrun = 0;
|
||||
return num;
|
||||
}
|
||||
|
||||
MessageQueuePtr createMessageQueue(int size)
|
||||
{
|
||||
MessageNodePtrArray nodeArray;
|
||||
nodeArray.reserve(size);
|
||||
for(int i=0; i<size; i++) {
|
||||
nodeArray.push_back(
|
||||
MessageNodePtr(new MessageNode()));
|
||||
}
|
||||
return std::tr1::shared_ptr<MessageQueue>(new MessageQueue(nodeArray));
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
@@ -254,7 +254,7 @@ epicsParseFloat(const char *str, float *to, char **units)
|
||||
#endif
|
||||
|
||||
// Sometimes we have to provide our own copy of strtoll()
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1800
|
||||
#if defined(_WIN32) && !defined(_MINGW)
|
||||
// On Windows with MSVC, Base-3.15 provides strtoll()
|
||||
# define NEED_OLL_FUNCS (EPICS_VERSION_INT < VERSION_INT(3,15,0,1))
|
||||
#elif defined(vxWorks)
|
||||
@@ -507,20 +507,20 @@ void handleParseError(int err)
|
||||
|
||||
namespace epics { namespace pvData { namespace detail {
|
||||
|
||||
void parseToPOD(const char* in, boolean *out)
|
||||
void parseToPOD(const string & in, boolean *out)
|
||||
{
|
||||
if(epicsStrCaseCmp(in,"true")==0)
|
||||
if(epicsStrCaseCmp(in.c_str(),"true")==0)
|
||||
*out = 1;
|
||||
else if(epicsStrCaseCmp(in,"false")==0)
|
||||
else if(epicsStrCaseCmp(in.c_str(),"false")==0)
|
||||
*out = 0;
|
||||
else
|
||||
throw std::runtime_error("parseToPOD: string no match true/false");
|
||||
}
|
||||
|
||||
#define INTFN(T, S) \
|
||||
void parseToPOD(const char* in, T *out) { \
|
||||
void parseToPOD(const string& in, T *out) { \
|
||||
epics ## S temp; \
|
||||
int err = epicsParse ## S (in, &temp, 0, NULL); \
|
||||
int err = epicsParse ## S (in.c_str(), &temp, 0, NULL); \
|
||||
if(err) handleParseError(err); \
|
||||
else *out = temp; \
|
||||
}
|
||||
@@ -532,31 +532,31 @@ INTFN(uint16_t, UInt16);
|
||||
INTFN(int32_t, Int32);
|
||||
INTFN(uint32_t, UInt32);
|
||||
|
||||
void parseToPOD(const char* in, int64_t *out) {
|
||||
void parseToPOD(const string& in, int64_t *out) {
|
||||
#ifdef NEED_LONGLONG
|
||||
int err = epicsParseLongLong(in, out, 0, NULL);
|
||||
int err = epicsParseLongLong(in.c_str(), out, 0, NULL);
|
||||
#else
|
||||
int err = epicsParseLong(in, out, 0, NULL);
|
||||
int err = epicsParseLong(in.c_str(), out, 0, NULL);
|
||||
#endif
|
||||
if(err) handleParseError(err);
|
||||
}
|
||||
|
||||
void parseToPOD(const char* in, uint64_t *out) {
|
||||
void parseToPOD(const string& in, uint64_t *out) {
|
||||
#ifdef NEED_LONGLONG
|
||||
int err = epicsParseULongLong(in, out, 0, NULL);
|
||||
int err = epicsParseULongLong(in.c_str(), out, 0, NULL);
|
||||
#else
|
||||
int err = epicsParseULong(in, out, 0, NULL);
|
||||
int err = epicsParseULong(in.c_str(), out, 0, NULL);
|
||||
#endif
|
||||
if(err) handleParseError(err);
|
||||
}
|
||||
|
||||
void parseToPOD(const char* in, float *out) {
|
||||
int err = epicsParseFloat(in, out, NULL);
|
||||
void parseToPOD(const string& in, float *out) {
|
||||
int err = epicsParseFloat(in.c_str(), out, NULL);
|
||||
if(err) handleParseError(err);
|
||||
}
|
||||
|
||||
void parseToPOD(const char* in, double *out) {
|
||||
int err = epicsParseDouble(in, out, NULL);
|
||||
void parseToPOD(const string& in, double *out) {
|
||||
int err = epicsParseDouble(in.c_str(), out, NULL);
|
||||
if(err) handleParseError(err);
|
||||
#if defined(vxWorks)
|
||||
/* vxWorks strtod returns [-]epicsINF when it should return ERANGE error.
|
||||
@@ -564,7 +564,7 @@ void parseToPOD(const char* in, double *out) {
|
||||
* this into an ERANGE error
|
||||
*/
|
||||
else if (*out == epicsINF || *out == -epicsINF) {
|
||||
const char* s = in;
|
||||
const char* s = in.c_str();
|
||||
int c;
|
||||
|
||||
/* skip spaces and the sign */
|
||||
|
||||
@@ -1,251 +0,0 @@
|
||||
#ifndef PV_ANYSCALAR_H
|
||||
#define PV_ANYSCALAR_H
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
# include <type_traits>
|
||||
#endif
|
||||
|
||||
#include <ostream>
|
||||
#include <exception>
|
||||
#include <map>
|
||||
|
||||
#include <epicsAssert.h>
|
||||
|
||||
#include <pv/templateMeta.h>
|
||||
#include <pv/typeCast.h>
|
||||
#include <pv/pvIntrospect.h> /* for ScalarType enum */
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
namespace detail {
|
||||
|
||||
// special mangling for AnyScalar ctor to map from argument type to storage type.
|
||||
// allow construction from constants.
|
||||
template <typename T>
|
||||
struct any_storage_type { typedef T type; };
|
||||
template<> struct any_storage_type<int> { typedef int32 type; };
|
||||
template<> struct any_storage_type<unsigned> { typedef uint32 type; };
|
||||
template<> struct any_storage_type<char*> { typedef std::string type; };
|
||||
template<> struct any_storage_type<const char*> { typedef std::string type; };
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
// std::max() isn't constexpr until c++14 :(
|
||||
constexpr size_t cmax(size_t A, size_t B) {
|
||||
return A>B ? A : B;
|
||||
}
|
||||
#endif
|
||||
|
||||
}// namespace detail
|
||||
|
||||
/** A type-safe variant union capable of holding
|
||||
* any of the PVD scalar types (POD or string)
|
||||
*
|
||||
@code
|
||||
AnyScalar A(5);
|
||||
assert(A.type()==pvInt);
|
||||
assert(A.ref<int32>()==5);
|
||||
assert(A.as<int32>()==5);
|
||||
assert(A.as<double>()==5.0);
|
||||
assert(A.ref<double>()==5.0); // throws AnyScalar::bad_cast
|
||||
@endcode
|
||||
*/
|
||||
class epicsShareClass AnyScalar {
|
||||
public:
|
||||
struct bad_cast : public std::exception {
|
||||
#if __cplusplus>=201103L
|
||||
bad_cast() noexcept {}
|
||||
virtual ~bad_cast() noexcept {}
|
||||
virtual const char* what() const noexcept
|
||||
#else
|
||||
bad_cast() throw() {}
|
||||
virtual ~bad_cast() throw() {}
|
||||
virtual const char* what() const throw()
|
||||
#endif
|
||||
{ return "bad_cast() type mis-match"; }
|
||||
};
|
||||
|
||||
private:
|
||||
ScalarType _stype;
|
||||
|
||||
// always reserve enough storage for std::string or double (assumed worst case)
|
||||
#if __cplusplus>=201103L
|
||||
|
||||
struct wrap_t {
|
||||
typename std::aligned_storage<detail::cmax(sizeof(std::string), sizeof(double)),
|
||||
detail::cmax(alignof(std::string), alignof(double))
|
||||
>::type blob[1];
|
||||
} _wrap;
|
||||
#else
|
||||
struct wrap_t {
|
||||
union blob_t {
|
||||
char data[sizeof(std::string)];
|
||||
double align_f; // assume std::string alignment <= 8
|
||||
} blob[1];
|
||||
} _wrap;
|
||||
#endif
|
||||
|
||||
// assumed largest non-string type
|
||||
typedef double _largest_blob;
|
||||
|
||||
template<typename T>
|
||||
inline T& _as() {
|
||||
return *reinterpret_cast<T*>(_wrap.blob);
|
||||
}
|
||||
template<typename T>
|
||||
inline const T& _as() const {
|
||||
return *reinterpret_cast<const T*>(_wrap.blob);
|
||||
}
|
||||
public:
|
||||
//! Construct empty
|
||||
//! @post empty()==true
|
||||
AnyScalar() : _stype((ScalarType)-1) {}
|
||||
|
||||
//! Construct from provided value.
|
||||
template<typename T>
|
||||
explicit AnyScalar(T v)
|
||||
{
|
||||
typedef typename meta::strip_const<T>::type T2;
|
||||
typedef typename detail::any_storage_type<T2>::type TT;
|
||||
|
||||
STATIC_ASSERT(sizeof(TT)<=sizeof(_wrap.blob));
|
||||
|
||||
new (_wrap.blob) TT(v);
|
||||
|
||||
// this line fails to compile when type T can't be mapped to one of
|
||||
// the PVD scalar types.
|
||||
_stype = (ScalarType)ScalarTypeID<TT>::value;
|
||||
}
|
||||
|
||||
//! Construct from un-typed pointer.
|
||||
//! Caller is responsible to ensure that buf actually points to the provided type
|
||||
//! @version Added after 7.0.0
|
||||
AnyScalar(ScalarType type, const void *buf);
|
||||
|
||||
AnyScalar(const AnyScalar& o);
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
AnyScalar(AnyScalar&& o);
|
||||
#endif
|
||||
|
||||
inline ~AnyScalar() {clear();}
|
||||
|
||||
inline AnyScalar& operator=(const AnyScalar& o) {
|
||||
AnyScalar(o).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AnyScalar& operator=(T v) {
|
||||
AnyScalar(v).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
inline AnyScalar& operator=(AnyScalar&& o) {
|
||||
clear();
|
||||
swap(o);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
//! Reset internal state.
|
||||
//! @version Added after 7.0.0
|
||||
//! @post empty()==true
|
||||
void clear();
|
||||
|
||||
void swap(AnyScalar& o);
|
||||
|
||||
//! Type code of contained value. Or (ScalarType)-1 is empty.
|
||||
inline ScalarType type() const {
|
||||
return _stype;
|
||||
}
|
||||
|
||||
inline void* unsafe() { return _wrap.blob; }
|
||||
inline const void* unsafe() const { return _wrap.blob; }
|
||||
|
||||
inline bool empty() const { return _stype==(ScalarType)-1; }
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
explicit operator bool() const { return !empty(); }
|
||||
#else
|
||||
private:
|
||||
typedef void (AnyScalar::*bool_type)(AnyScalar&);
|
||||
public:
|
||||
operator bool_type() const { return !empty() ? &AnyScalar::swap : 0; }
|
||||
#endif
|
||||
|
||||
//! Provide read-only access to underlying buffer.
|
||||
//! For a string this is std::string::c_str().
|
||||
//! @version Added after 7.0.0
|
||||
const void* bufferUnsafe() const;
|
||||
|
||||
/** Return typed reference to wrapped value. Non-const reference allows value modification
|
||||
*
|
||||
* @throws bad_cast when the requested type does not match the stored type
|
||||
@code
|
||||
AnyScalar v(42);
|
||||
v.ref<uint32>() = 43;
|
||||
assert(v.ref<uint32>() == 43);
|
||||
@endcode
|
||||
*/
|
||||
template<typename T>
|
||||
inline
|
||||
// T -> strip_const -> map to storage type -> add reference
|
||||
typename detail::any_storage_type<typename meta::strip_const<T>::type>::type&
|
||||
ref() {
|
||||
typedef typename meta::strip_const<T>::type T2;
|
||||
typedef typename detail::any_storage_type<T2>::type TT;
|
||||
|
||||
if(_stype!=(ScalarType)ScalarTypeID<TT>::value)
|
||||
throw bad_cast();
|
||||
return reinterpret_cast<TT&>(_wrap.blob);
|
||||
}
|
||||
|
||||
/** Return typed reference to wrapped value. Const reference does not allow modification.
|
||||
*
|
||||
* @throws bad_cast when the requested type does not match the stored type
|
||||
@code
|
||||
const AnyScalar v(42);
|
||||
assert(v.ref<uint32>() == 42);
|
||||
@endcode
|
||||
*/
|
||||
template<typename T>
|
||||
inline
|
||||
// T -> strip_const -> map to storage type -> add const reference
|
||||
typename meta::decorate_const<typename detail::any_storage_type<typename meta::strip_const<T>::type>::type>::type&
|
||||
ref() const {
|
||||
typedef typename meta::strip_const<T>::type T2;
|
||||
typedef typename detail::any_storage_type<T2>::type TT;
|
||||
|
||||
if(_stype!=(ScalarType)ScalarTypeID<TT>::value)
|
||||
throw bad_cast();
|
||||
return reinterpret_cast<typename meta::decorate_const<TT>::type&>(_wrap.blob);
|
||||
}
|
||||
|
||||
/** copy out wrapped value, with a value conversion.
|
||||
*
|
||||
* @throws bad_cast when empty()==true
|
||||
*/
|
||||
template<typename T>
|
||||
inline
|
||||
T as() const {
|
||||
typedef typename meta::strip_const<T>::type T2;
|
||||
typedef typename detail::any_storage_type<T2>::type TT;
|
||||
|
||||
if(_stype==(ScalarType)-1)
|
||||
throw bad_cast();
|
||||
TT ret;
|
||||
castUnsafeV(1, (ScalarType)ScalarTypeID<T2>::value, &ret,
|
||||
_stype, _wrap.blob);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
friend epicsShareFunc std::ostream& operator<<(std::ostream& strm, const AnyScalar& v);
|
||||
};
|
||||
|
||||
epicsShareExtern
|
||||
std::ostream& operator<<(std::ostream& strm, const AnyScalar& v);
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#endif // PV_ANYSCALAR_H
|
||||
@@ -9,10 +9,6 @@
|
||||
#ifndef BITSET_H
|
||||
#define BITSET_H
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
# include <initializer_list>
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <pv/pvType.h>
|
||||
@@ -50,8 +46,6 @@ namespace epics { namespace pvData {
|
||||
* synchronization.
|
||||
*
|
||||
* Based on Java implementation.
|
||||
*
|
||||
* @since 7.0.0 Many methods return BitSet& to facilite method chaining.
|
||||
*/
|
||||
class epicsShareClass BitSet : public Serializable {
|
||||
public:
|
||||
@@ -71,16 +65,6 @@ namespace epics { namespace pvData {
|
||||
*/
|
||||
BitSet(uint32 nbits);
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
/** Initialize from a list of indicies
|
||||
@code
|
||||
BitSet X({1, 5});
|
||||
assert(X.get(1) && X.get(5));
|
||||
@endcode
|
||||
*/
|
||||
BitSet(std::initializer_list<uint32> I);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
@@ -92,21 +76,21 @@ namespace epics { namespace pvData {
|
||||
*
|
||||
* @param bitIndex the index of the bit to flip
|
||||
*/
|
||||
BitSet& flip(uint32 bitIndex);
|
||||
void flip(uint32 bitIndex);
|
||||
|
||||
/**
|
||||
* Sets the bit at the specified index to @c true.
|
||||
*
|
||||
* @param bitIndex a bit index
|
||||
*/
|
||||
BitSet& set(uint32 bitIndex);
|
||||
void set(uint32 bitIndex);
|
||||
|
||||
/**
|
||||
* Sets the bit specified by the index to @c false.
|
||||
*
|
||||
* @param bitIndex the index of the bit to be cleared
|
||||
*/
|
||||
BitSet& clear(uint32 bitIndex);
|
||||
void clear(uint32 bitIndex);
|
||||
|
||||
/**
|
||||
* Sets the bit at the specified index to the specified value.
|
||||
@@ -183,13 +167,8 @@ namespace epics { namespace pvData {
|
||||
*/
|
||||
uint32 size() const;
|
||||
|
||||
//! Returns true if any bit is set in both *this and other
|
||||
bool logical_and(const BitSet& other) const;
|
||||
//! Returns true if any bit is set in both *this or other
|
||||
bool logical_or(const BitSet& other) const;
|
||||
|
||||
/**
|
||||
* Performs a bitwise <b>AND</b> of this target bit set with the
|
||||
* Performs a logical <b>AND</b> of this target bit set with the
|
||||
* argument bit set. This bit set is modified so that each bit in it
|
||||
* has the value @c true if and only if it both initially
|
||||
* had the value @c true and the corresponding bit in the
|
||||
@@ -200,7 +179,7 @@ namespace epics { namespace pvData {
|
||||
BitSet& operator&=(const BitSet& set);
|
||||
|
||||
/**
|
||||
* Performs a bitwise <b>OR</b> of this bit set with the bit set
|
||||
* Performs a logical <b>OR</b> of this bit set with the bit set
|
||||
* argument. This bit set is modified so that a bit in it has the
|
||||
* value @c true if and only if it either already had the
|
||||
* value @c true or the corresponding bit in the bit set
|
||||
@@ -211,7 +190,7 @@ namespace epics { namespace pvData {
|
||||
BitSet& operator|=(const BitSet& set);
|
||||
|
||||
/**
|
||||
* Performs a bitwise <b>XOR</b> of this bit set with the bit set
|
||||
* Performs a logical <b>XOR</b> of this bit set with the bit set
|
||||
* argument. This bit set is modified so that a bit in it has the
|
||||
* value @c true if and only if one of the following
|
||||
* statements holds:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,328 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/* Author: Michael Davidsaver */
|
||||
/* wrapper around shared_ptr which tracks backwards references.
|
||||
* Can help to find ref. leaks, loops, and other exciting bugs.
|
||||
* See comments in sharedPtr.h
|
||||
*/
|
||||
#ifndef DEBUGPTR_H
|
||||
#define DEBUGPTR_H
|
||||
|
||||
#if __cplusplus<201103L
|
||||
# error c++11 required
|
||||
#endif
|
||||
|
||||
#include <ostream>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
#include <pv/epicsException.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
//! User code should test this macro
|
||||
//! before calling epics::debug::shared_ptr::show_refs()
|
||||
#define HAVE_SHOW_REFS
|
||||
|
||||
namespace epics {
|
||||
namespace debug {
|
||||
|
||||
struct tracker;
|
||||
class shared_ptr_base;
|
||||
|
||||
class epicsShareClass ptr_base {
|
||||
friend class shared_ptr_base;
|
||||
template<typename A>
|
||||
friend class shared_ptr;
|
||||
template<typename A>
|
||||
friend class weak_ptr;
|
||||
protected:
|
||||
typedef std::shared_ptr<tracker> track_t;
|
||||
track_t track;
|
||||
|
||||
ptr_base() noexcept : track() {}
|
||||
ptr_base(const track_t& track) :track(track) {}
|
||||
ptr_base(const ptr_base&) = delete;
|
||||
ptr_base(ptr_base&&) = delete;
|
||||
|
||||
ptr_base& operator=(const ptr_base&) = delete;
|
||||
|
||||
public:
|
||||
typedef std::set<const shared_ptr_base *> ref_set_t;
|
||||
void show_refs(std::ostream&, bool self=true, bool weak=false) const;
|
||||
void spy_refs(ref_set_t&) const;
|
||||
};
|
||||
|
||||
class epicsShareClass weak_ptr_base : public ptr_base {
|
||||
protected:
|
||||
weak_ptr_base() {}
|
||||
weak_ptr_base(const track_t& track) :ptr_base(track) {}
|
||||
};
|
||||
|
||||
class epicsShareClass shared_ptr_base : public ptr_base {
|
||||
protected:
|
||||
shared_ptr_base() noexcept
|
||||
#ifndef EXCEPT_USE_NONE
|
||||
:m_stack(), m_depth(0)
|
||||
#endif
|
||||
{}
|
||||
shared_ptr_base(const track_t& track) :ptr_base(track)
|
||||
#ifndef EXCEPT_USE_NONE
|
||||
,m_stack(), m_depth(0)
|
||||
#endif
|
||||
{}
|
||||
~shared_ptr_base() {track_clear();}
|
||||
|
||||
// add ourselves to tracker
|
||||
void track_new();
|
||||
// create new tracker if ptr!=nullptr, otherwise clear
|
||||
void track_new(void* ptr);
|
||||
// copy tracker and add ourself
|
||||
void track_assign(const shared_ptr_base& o);
|
||||
void track_clear();
|
||||
void swap(shared_ptr_base& o);
|
||||
void snap_stack();
|
||||
|
||||
#ifndef EXCEPT_USE_NONE
|
||||
void *m_stack[EXCEPT_DEPTH];
|
||||
int m_depth; // always <= EXCEPT_DEPTH
|
||||
#endif
|
||||
|
||||
public:
|
||||
void show_stack(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<typename T>
|
||||
class shared_ptr;
|
||||
template<typename T>
|
||||
class weak_ptr;
|
||||
template<class Base>
|
||||
class enable_shared_from_this;
|
||||
|
||||
template<typename Store, typename Actual>
|
||||
inline void
|
||||
do_enable_shared_from_this(const shared_ptr<Store>& dest,
|
||||
enable_shared_from_this<Actual>* self
|
||||
);
|
||||
|
||||
template<typename T>
|
||||
inline void
|
||||
do_enable_shared_from_this(const shared_ptr<T>&, ...) {}
|
||||
|
||||
template<typename T>
|
||||
class shared_ptr : public shared_ptr_base {
|
||||
typedef ::std::shared_ptr<T> real_type;
|
||||
|
||||
real_type real;
|
||||
|
||||
template<typename A>
|
||||
friend class shared_ptr;
|
||||
template<typename A>
|
||||
friend class weak_ptr;
|
||||
|
||||
// ctor for casts
|
||||
shared_ptr(const real_type& r, const ptr_base::track_t& t)
|
||||
:shared_ptr_base(t), real(r)
|
||||
{track_new();}
|
||||
public:
|
||||
typedef typename real_type::element_type element_type;
|
||||
typedef weak_ptr<T> weak_type;
|
||||
|
||||
// new NULL
|
||||
shared_ptr() noexcept {}
|
||||
// copy existing same type
|
||||
shared_ptr(const shared_ptr& o) :shared_ptr_base(o.track), real(o.real) {track_new();}
|
||||
// copy existing of implicitly castable type
|
||||
template<typename A>
|
||||
shared_ptr(const shared_ptr<A>& o) :shared_ptr_base(o.track), real(o.real) {track_new();}
|
||||
|
||||
// construct around new pointer
|
||||
template<typename A, class ... Args>
|
||||
explicit shared_ptr(A* a, Args ... args) : shared_ptr_base(), real(a, args...) {
|
||||
track_new(a);
|
||||
do_enable_shared_from_this(*this, a);
|
||||
}
|
||||
|
||||
// make strong ref from weak
|
||||
template<typename A>
|
||||
shared_ptr(const weak_ptr<A>& o) :shared_ptr_base(o.track), real(o.real) {track_new();}
|
||||
|
||||
// takeover from unique_ptr
|
||||
template<typename A>
|
||||
shared_ptr(std::unique_ptr<A>&& a) : shared_ptr_base(), real(a.release()) {track_new();}
|
||||
|
||||
~shared_ptr() {}
|
||||
|
||||
shared_ptr& operator=(const shared_ptr& o) {
|
||||
if(this!=&o) {
|
||||
real = o.real;
|
||||
track_assign(o);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
template<typename A>
|
||||
shared_ptr& operator=(const shared_ptr<A>& o) {
|
||||
if(get()!=o.get()) {
|
||||
real = o.real;
|
||||
track_assign(o);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset() noexcept { real.reset(); track_clear(); }
|
||||
template<typename A, class ... Args>
|
||||
void reset(A* a, Args ... args)
|
||||
{
|
||||
real.reset(a, args...);
|
||||
track_new(a);
|
||||
do_enable_shared_from_this(*this, a);
|
||||
}
|
||||
void swap(shared_ptr &o) noexcept
|
||||
{
|
||||
if(this!=&o) {
|
||||
real.swap(o.real);
|
||||
shared_ptr_base::swap(o);
|
||||
}
|
||||
}
|
||||
|
||||
// proxy remaining to underlying shared_ptr
|
||||
|
||||
T* get() const noexcept { return real.get(); }
|
||||
typename std::add_lvalue_reference<T>::type operator*() const noexcept { return *real; }
|
||||
T* operator->() const noexcept { return real.get(); }
|
||||
long use_count() const noexcept { return real.use_count(); }
|
||||
bool unique() const noexcept { return real.unique(); }
|
||||
explicit operator bool() const noexcept { return bool(real); }
|
||||
|
||||
bool operator==(const shared_ptr<T>& o) const { return real==o.real; }
|
||||
bool operator!=(const shared_ptr<T>& o) const { return real!=o.real; }
|
||||
bool operator<(const shared_ptr<T>& o) const { return real<o.real; }
|
||||
|
||||
template<typename A>
|
||||
bool owner_before(const shared_ptr<A>& o) { return real.owner_before(o); }
|
||||
template<typename A>
|
||||
bool owner_before(const weak_ptr<A>& o) { return real.owner_before(o); }
|
||||
|
||||
template<typename TO, typename FROM>
|
||||
friend
|
||||
shared_ptr<TO> static_pointer_cast(const shared_ptr<FROM>& src);
|
||||
template<typename TO, typename FROM>
|
||||
friend
|
||||
shared_ptr<TO> const_pointer_cast(const shared_ptr<FROM>& src);
|
||||
template<typename TO, typename FROM>
|
||||
friend
|
||||
shared_ptr<TO> dynamic_pointer_cast(const shared_ptr<FROM>& src);
|
||||
template<typename Store, typename Actual>
|
||||
friend void
|
||||
do_enable_shared_from_this(const shared_ptr<Store>& dest,
|
||||
enable_shared_from_this<Actual>* self
|
||||
);
|
||||
};
|
||||
|
||||
template<typename TO, typename FROM>
|
||||
shared_ptr<TO> static_pointer_cast(const shared_ptr<FROM>& src) {
|
||||
return shared_ptr<TO>(std::static_pointer_cast<TO>(src.real), src.track);
|
||||
}
|
||||
|
||||
template<typename TO, typename FROM>
|
||||
shared_ptr<TO> const_pointer_cast(const shared_ptr<FROM>& src) {
|
||||
return shared_ptr<TO>(std::const_pointer_cast<TO>(src.real), src.track);
|
||||
}
|
||||
|
||||
template<typename TO, typename FROM>
|
||||
shared_ptr<TO> dynamic_pointer_cast(const shared_ptr<FROM>& src) {
|
||||
return shared_ptr<TO>(std::dynamic_pointer_cast<TO>(src.real), src.track);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class weak_ptr : public weak_ptr_base {
|
||||
typedef ::std::weak_ptr<T> real_type;
|
||||
|
||||
real_type real;
|
||||
|
||||
template<typename A>
|
||||
friend class shared_ptr;
|
||||
template<typename A>
|
||||
friend class weak_ptr;
|
||||
|
||||
public:
|
||||
typedef typename real_type::element_type element_type;
|
||||
typedef weak_ptr<T> weak_type;
|
||||
|
||||
// new NULL
|
||||
weak_ptr() noexcept {}
|
||||
// copy existing same type
|
||||
weak_ptr(const weak_ptr& o) :weak_ptr_base(o.track), real(o.real) {}
|
||||
// copy existing of similar type
|
||||
template<typename A>
|
||||
weak_ptr(const weak_ptr<A>& o) :weak_ptr_base(o.track), real(o.real) {}
|
||||
|
||||
// create week ref from strong ref
|
||||
template<typename A>
|
||||
weak_ptr(const shared_ptr<A>& o) :weak_ptr_base(o.track), real(o.real) {}
|
||||
|
||||
~weak_ptr() {}
|
||||
|
||||
weak_ptr& operator=(const weak_ptr& o) {
|
||||
if(this!=&o) {
|
||||
real = o.real;
|
||||
track = o.track;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
template<typename A>
|
||||
weak_ptr& operator=(const shared_ptr<A>& o) {
|
||||
real = o.real;
|
||||
track = o.track;
|
||||
return *this;
|
||||
}
|
||||
|
||||
shared_ptr<T> lock() const noexcept { return shared_ptr<T>(real.lock(), track); }
|
||||
void reset() noexcept { track.reset(); real.reset(); }
|
||||
|
||||
long use_count() const noexcept { return real.use_count(); }
|
||||
bool unique() const noexcept { return real.unique(); }
|
||||
};
|
||||
|
||||
template<class Base>
|
||||
class enable_shared_from_this {
|
||||
mutable weak_ptr<Base> xxInternalSelf;
|
||||
|
||||
template<typename Store, typename Actual>
|
||||
friend
|
||||
void
|
||||
do_enable_shared_from_this(const shared_ptr<Store>& dest,
|
||||
enable_shared_from_this<Actual>* self
|
||||
);
|
||||
public:
|
||||
shared_ptr<Base> shared_from_this() const {
|
||||
return shared_ptr<Base>(xxInternalSelf);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Store, typename Actual>
|
||||
inline void
|
||||
do_enable_shared_from_this(const shared_ptr<Store>& dest,
|
||||
enable_shared_from_this<Actual>* self
|
||||
)
|
||||
{
|
||||
shared_ptr<Actual> actual(dynamic_pointer_cast<Actual>(dest));
|
||||
if(!actual)
|
||||
throw std::logic_error("epics::debug::enabled_shared_from_this fails");
|
||||
self->xxInternalSelf = actual;
|
||||
}
|
||||
|
||||
}} // namespace epics::debug
|
||||
|
||||
template<typename T>
|
||||
inline std::ostream& operator<<(std::ostream& strm, const epics::debug::shared_ptr<T>& ptr)
|
||||
{
|
||||
strm<<ptr.get();
|
||||
return strm;
|
||||
}
|
||||
|
||||
#endif // DEBUGPTR_H
|
||||
40
src/misc/pv/destroyable.h
Normal file
40
src/misc/pv/destroyable.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/* destroyable.h */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mse
|
||||
*/
|
||||
#ifndef DESTROYABLE_H
|
||||
#define DESTROYABLE_H
|
||||
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Instance declaring destroy method.
|
||||
*
|
||||
* @author mse
|
||||
*/
|
||||
class epicsShareClass Destroyable {
|
||||
public:
|
||||
POINTER_DEFINITIONS(Destroyable);
|
||||
/**
|
||||
* Destroy this instance.
|
||||
*/
|
||||
virtual void destroy() = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Do not allow delete on this instance and derived classes, destroy() must be used instead.
|
||||
*/
|
||||
virtual ~Destroyable() {};
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* DESTROYABLE_H */
|
||||
@@ -33,6 +33,10 @@
|
||||
#ifndef EPICSEXCEPTION_H_
|
||||
#define EPICSEXCEPTION_H_
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
@@ -177,7 +181,7 @@ do { \
|
||||
|
||||
#define PRINT_EXCEPTION(EI) PRINT_EXCEPTION2(EI,stderr)
|
||||
|
||||
#if !defined(__GNUC__) || __GNUC__ < 4
|
||||
#ifndef __GNUC__
|
||||
# define SHOW_EXCEPTION(EI) ::epics::pvData::detail::showException(EI)
|
||||
#else
|
||||
# define SHOW_EXCEPTION(EI) \
|
||||
|
||||
90
src/misc/pv/executor.h
Normal file
90
src/misc/pv/executor.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/* executor.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 EXECUTOR_H
|
||||
#define EXECUTOR_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/lock.h>
|
||||
#include <pv/event.h>
|
||||
#include <pv/thread.h>
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class Command;
|
||||
class Executor;
|
||||
typedef std::tr1::shared_ptr<Command> CommandPtr;
|
||||
typedef std::tr1::shared_ptr<Executor> ExecutorPtr;
|
||||
|
||||
/**
|
||||
* @brief A command to be called by Executor
|
||||
*
|
||||
*/
|
||||
class epicsShareClass Command {
|
||||
public:
|
||||
POINTER_DEFINITIONS(Command);
|
||||
/**
|
||||
*
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~Command(){}
|
||||
/**
|
||||
*
|
||||
* The command that is executed.
|
||||
*/
|
||||
virtual void command() = 0;
|
||||
private:
|
||||
CommandPtr next;
|
||||
friend class Executor;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A class that executes commands.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass Executor : public Runnable{
|
||||
public:
|
||||
POINTER_DEFINITIONS(Executor);
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param threadName name for the executor thread.
|
||||
* @param priority The thread priority.
|
||||
*/
|
||||
Executor(std::string const & threadName,ThreadPriority priority);
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Executor();
|
||||
/**
|
||||
*
|
||||
* Request to execute a command.
|
||||
* @param command A shared pointer to the command instance.
|
||||
*/
|
||||
void execute(CommandPtr const &command);
|
||||
/**
|
||||
*
|
||||
* The thread run method.
|
||||
*/
|
||||
virtual void run();
|
||||
private:
|
||||
CommandPtr head;
|
||||
CommandPtr tail;
|
||||
epics::pvData::Mutex mutex;
|
||||
epics::pvData::Event moreWork;
|
||||
epics::pvData::Event stopped;
|
||||
epics::pvData::Thread thread;
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* EXECUTOR_H */
|
||||
32
src/misc/pv/localStaticLock.h
Normal file
32
src/misc/pv/localStaticLock.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/* localStaticLock.h */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mse
|
||||
*/
|
||||
#ifndef LOCALSTATICLOCK_H
|
||||
#define LOCALSTATICLOCK_H
|
||||
|
||||
#include <pv/lock.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
epicsShareExtern epics::pvData::Mutex& getLocalStaticInitMutex();
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ >= 4
|
||||
// noop
|
||||
#define LOCAL_STATIC_LOCK
|
||||
#else
|
||||
#define LOCAL_STATIC_LOCK epics::pvData::Lock localStaticInitMutexLock(getLocalStaticInitMutex());
|
||||
#endif
|
||||
|
||||
static class epicsShareClass MutexInitializer {
|
||||
public:
|
||||
MutexInitializer ();
|
||||
~MutexInitializer ();
|
||||
} localStaticMutexInitializer; // Note object here in the header.
|
||||
|
||||
|
||||
#endif /* LOCALSTATICLOCK_H */
|
||||
@@ -33,8 +33,7 @@ typedef epicsMutex Mutex;
|
||||
* This is based on item 14 of
|
||||
* * Effective C++, Third Edition, Scott Meyers
|
||||
*/
|
||||
class Lock {
|
||||
EPICS_NOT_COPYABLE(Lock)
|
||||
class epicsShareClass Lock : private NoDefaultMethods {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
|
||||
128
src/misc/pv/messageQueue.h
Normal file
128
src/misc/pv/messageQueue.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/* messageQueue.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 MESSAGEQUEUE_H
|
||||
#define MESSAGEQUEUE_H
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/requester.h>
|
||||
#include <pv/queue.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class MessageNode;
|
||||
class MessageQueue;
|
||||
typedef std::tr1::shared_ptr<MessageNode> MessageNodePtr;
|
||||
typedef std::vector<MessageNodePtr> MessageNodePtrArray;
|
||||
typedef std::tr1::shared_ptr<MessageQueue> MessageQueuePtr;
|
||||
|
||||
/**
|
||||
* @brief A node that can be put on a MessageQueue.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass MessageNode {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
MessageNode();
|
||||
/**
|
||||
*
|
||||
* Get the message value.
|
||||
* @return The message value.
|
||||
*/
|
||||
std::string getMessage() const;
|
||||
/**
|
||||
* Get the message type.
|
||||
* @return The message type which is defined in Requester.
|
||||
*/
|
||||
MessageType getMessageType() const;
|
||||
private:
|
||||
std::string message;
|
||||
MessageType messageType;
|
||||
friend class MessageQueue;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A bounded queue for messages.
|
||||
*
|
||||
*
|
||||
*/
|
||||
class epicsShareClass MessageQueue : public Queue<MessageNode> {
|
||||
public:
|
||||
POINTER_DEFINITIONS(MessageQueue);
|
||||
/**
|
||||
* Factory method to create a MessageQueue.
|
||||
* @param size The number of MessageNodes in the queue.
|
||||
* @return shared_ptr to MessageQueue.
|
||||
*/
|
||||
static MessageQueuePtr create(int size);
|
||||
/**
|
||||
* Constructor
|
||||
* @param nodeArray an array of shared_ptr to MessageNodes,
|
||||
*/
|
||||
MessageQueue(MessageNodePtrArray &nodeArray);
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~MessageQueue();
|
||||
/**
|
||||
* get the next MessageNode of the queue.
|
||||
* @return A shared_ptr to the MessageNode.
|
||||
* This will be a null pointer if queue is empty.
|
||||
* If get is successful then release for this MessageNode
|
||||
* must be called before another get can be issued.
|
||||
*/
|
||||
MessageNodePtr &get();
|
||||
/**
|
||||
* Release the MessageNode that was returned by the previous call to get.
|
||||
*/
|
||||
void release();
|
||||
/**
|
||||
*
|
||||
* put a message into the message queue
|
||||
* @param message The message string.
|
||||
* @param messageType The message type as defined in Requester,
|
||||
* @param replaceLast If true and queue is full then replace.
|
||||
* @return (false,true) if a message (was not, was) put in queiue.
|
||||
*/
|
||||
bool put(std::string message,MessageType messageType,bool replaceLast);
|
||||
/**
|
||||
* Is queue empty?
|
||||
* @return (false,true) if (is not, is) empty.
|
||||
*/
|
||||
bool isEmpty() ;
|
||||
/**
|
||||
* Is queue full?
|
||||
* @return (false,true) if (is not, is) full.
|
||||
*/
|
||||
bool isFull() ;
|
||||
/**
|
||||
*
|
||||
* Clear number of times queue was overrun and return the number
|
||||
* of times the queue was overrun.
|
||||
*/
|
||||
int getClearOverrun();
|
||||
private:
|
||||
MessageNodePtr nullNode;
|
||||
MessageNodePtr lastGet;
|
||||
MessageNodePtr lastPut;
|
||||
uint32 overrun;
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* MESSAGEQUEUE_H */
|
||||
|
||||
|
||||
|
||||
@@ -11,54 +11,30 @@
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
/** @macro EPICS_NOT_COPYABLE(CLASS)
|
||||
* @brief Disable implicit copyable
|
||||
*
|
||||
* Prevent the default copy constructor and assignment
|
||||
* operator from being usable.
|
||||
*
|
||||
* For >= C++11 explicitly disable. Attempts to copy/assign will
|
||||
* fail to compile.
|
||||
*
|
||||
* For C++98 make these private, and don't implement them.
|
||||
* User code will fail to compile, implementation code will fail to link.
|
||||
@code
|
||||
struct MyClass {
|
||||
EPICS_NOT_COPYABLE(MyClass)
|
||||
public:
|
||||
...
|
||||
};
|
||||
@endcode
|
||||
*
|
||||
* @note This macro contains 'private:'.
|
||||
namespace epics { namespace pvData {
|
||||
/* This is based on Item 6 of
|
||||
* Effective C++, Third Edition, Scott Meyers
|
||||
*/
|
||||
#if __cplusplus>=201103L
|
||||
# define EPICS_NOT_COPYABLE(CLASS) private: CLASS(const CLASS&) = delete; CLASS& operator=(const CLASS&) = delete;
|
||||
#else
|
||||
# define EPICS_NOT_COPYABLE(CLASS) private: CLASS(const CLASS&); CLASS& operator=(const CLASS&);
|
||||
#endif
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
/**
|
||||
* @brief Base class for not allowing default methods.
|
||||
*
|
||||
* Note that copy constructor a copy methods are declared private.
|
||||
*
|
||||
* @deprecated Deprecated in favor of EPICS_NOT_COPYABLE() pvDataCPP 7.0.0
|
||||
*/
|
||||
class NoDefaultMethods {
|
||||
public:
|
||||
NoDefaultMethods() {}
|
||||
private:
|
||||
#if __cplusplus>=201103L
|
||||
NoDefaultMethods(const NoDefaultMethods&) = delete;
|
||||
NoDefaultMethods & operator=(const NoDefaultMethods &) = delete;
|
||||
#else
|
||||
class epicsShareClass NoDefaultMethods {
|
||||
protected:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
NoDefaultMethods(){};
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~NoDefaultMethods(){}
|
||||
private:
|
||||
// do not implement
|
||||
NoDefaultMethods(const NoDefaultMethods&);
|
||||
NoDefaultMethods & operator=(const NoDefaultMethods &);
|
||||
#endif
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
#ifndef PVUNITTEST_H
|
||||
#define PVUNITTEST_H
|
||||
|
||||
#include <sstream>
|
||||
#include <typeinfo>
|
||||
|
||||
#include <epicsUnitTest.h>
|
||||
|
||||
#include <pv/sharedPtr.h>
|
||||
#include <pv/epicsException.h>
|
||||
#include <pv/pvData.h>
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class C, void (C::*M)()>
|
||||
void test_method(const char *kname, const char *mname)
|
||||
{
|
||||
try {
|
||||
testDiag("------- %s::%s --------", kname, mname);
|
||||
C inst;
|
||||
(inst.*M)();
|
||||
} catch(std::exception& e) {
|
||||
PRINT_EXCEPTION(e);
|
||||
testAbort("unexpected exception: %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
class epicsShareClass testPassx
|
||||
{
|
||||
std::ostringstream strm;
|
||||
const bool dotest, pass;
|
||||
bool alive;
|
||||
public:
|
||||
testPassx() :dotest(false), pass(false), alive(true) {}
|
||||
explicit testPassx(bool r) :dotest(true), pass(r), alive(true) {}
|
||||
~testPassx();
|
||||
template<typename T>
|
||||
inline testPassx& operator<<(const T& v) {
|
||||
strm<<v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// allow testPassx to be returned
|
||||
// move ctor masquerading as copy ctor
|
||||
testPassx(testPassx& o);
|
||||
private:
|
||||
testPassx& operator=(const testPassx&);
|
||||
};
|
||||
|
||||
template<typename LHS, typename RHS>
|
||||
inline testPassx testEqualx(const char *nLHS, const char *nRHS, const LHS& l, const RHS& r)
|
||||
{
|
||||
return testPassx(l==r)<<nLHS<<" ("<<l<<") == "<<nRHS<<" ("<<r<<")";
|
||||
}
|
||||
|
||||
template<typename LHS, typename RHS>
|
||||
inline testPassx testNotEqualx(const char *nLHS, const char *nRHS, const LHS& l, const RHS& r)
|
||||
{
|
||||
return testPassx(l!=r)<<nLHS<<" ("<<l<<") != "<<nRHS<<" ("<<r<<")";
|
||||
}
|
||||
|
||||
}//namespace detail
|
||||
|
||||
/** @defgroup testhelpers Unit testing helpers
|
||||
*
|
||||
* Helper functions for writing unit tests.
|
||||
*
|
||||
@include unittest.cpp
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Run a class method as a test.
|
||||
*
|
||||
* Each invocation of TEST_METHOD() constructs a new instance of 'klass' on the stack.
|
||||
* Thus constructor and destructor can be used for common test setup and tear down.
|
||||
@code
|
||||
namespace { // anon
|
||||
struct MyTest {
|
||||
MyTest() { } // setup
|
||||
~MyTest() { } // tear down
|
||||
void test1() {}
|
||||
void test2() {}
|
||||
};
|
||||
} // namespace anon
|
||||
MAIN(somename) {
|
||||
testPlan(0);
|
||||
TEST_METHOD(MyTest, test1)
|
||||
TEST_METHOD(MyTest, test2)
|
||||
return testDone();
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
#define TEST_METHOD(klass, method) ::detail::test_method<klass, &klass::method>(#klass, #method)
|
||||
|
||||
/** Compare equality. print left and right hand values and expression strings
|
||||
*
|
||||
@code
|
||||
int x=5;
|
||||
testEqual(x, 5);
|
||||
// prints "ok 1 - x (5) == 5 (5)\n"
|
||||
testEqual(x, 6)<<" oops";
|
||||
// prints "not ok 1 - x (5) == 6 (6) oops\n"
|
||||
@endcode
|
||||
*/
|
||||
#define testEqual(LHS, RHS) ::detail::testEqualx(#LHS, #RHS, LHS, RHS)
|
||||
|
||||
#define testNotEqual(LHS, RHS) ::detail::testNotEqualx(#LHS, #RHS, LHS, RHS)
|
||||
|
||||
/** Pass/fail from boolean
|
||||
*
|
||||
@code
|
||||
bool y=true;
|
||||
testTrue(y);
|
||||
// prints "ok 1 - y\n"
|
||||
testTrue(!y)<<" oops";
|
||||
// prints "not ok 1 - !y oops\n"
|
||||
@endcode
|
||||
*/
|
||||
#define testTrue(B) ::detail::testPassx(!!(B))<<#B
|
||||
|
||||
/** Test that a given block throws an exception
|
||||
*
|
||||
@code
|
||||
testThrows(std::runtime_error, somefunc(5))
|
||||
@endcode
|
||||
*/
|
||||
#define testThrows(EXC, CODE) try{ CODE; testFail("unexpected success of " #CODE); }catch(EXC& e){testPass("catch expected exception: %s", e.what());}
|
||||
|
||||
/** Print test output w/o testing
|
||||
*
|
||||
@code
|
||||
testShow()<<"Foo";
|
||||
@endcode
|
||||
*/
|
||||
#define testShow() ::detail::testPassx()
|
||||
|
||||
/** Compare value of PVStructure field
|
||||
*
|
||||
@code
|
||||
PVStructurePtr x(.....);
|
||||
testFieldEqual<epics::pvData::PVInt>(x, "alarm.severity", 1);
|
||||
@endcode
|
||||
*/
|
||||
template<typename PVD>
|
||||
::detail::testPassx
|
||||
testFieldEqual(const std::tr1::shared_ptr<const epics::pvData::PVStructure>& val, const char *name, typename PVD::value_type expect)
|
||||
{
|
||||
if(!val) {
|
||||
return ::detail::testPassx(false)<<" null structure pointer";
|
||||
}
|
||||
typename PVD::const_shared_pointer fval(val->getSubField<PVD>(name));
|
||||
if(!fval) {
|
||||
epics::pvData::PVUnion::const_shared_pointer uval(val->getSubField<epics::pvData::PVUnion>(name));
|
||||
if(uval)
|
||||
fval = uval->get<PVD>();
|
||||
}
|
||||
if(!fval) {
|
||||
return ::detail::testPassx(false)<<" field '"<<name<<"' with type "<<typeid(PVD).name()<<" does not exist";
|
||||
} else {
|
||||
typename PVD::value_type actual(fval->get());
|
||||
return ::detail::testPassx(actual==expect)<<name<<" ("<<actual<<") == "<<expect;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename PVD>
|
||||
::detail::testPassx
|
||||
testFieldEqual(const std::tr1::shared_ptr<const epics::pvData::PVStructure>& val, const char *name, typename PVD::const_svector expect)
|
||||
{
|
||||
if(!val) {
|
||||
return ::detail::testPassx(false)<<" null structure pointer";
|
||||
}
|
||||
typename PVD::const_shared_pointer fval(val->getSubField<PVD>(name));
|
||||
if(!fval) {
|
||||
return ::detail::testPassx(false)<<" field '"<<name<<"' with type "<<typeid(PVD).name()<<" does not exist";
|
||||
} else {
|
||||
typename PVD::const_svector actual(fval->view());
|
||||
return ::detail::testPassx(actual==expect)<<name<<" ("<<actual<<") == "<<expect;
|
||||
}
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif // PVUNITTEST_H
|
||||
183
src/misc/pv/queue.h
Normal file
183
src/misc/pv/queue.h
Normal file
@@ -0,0 +1,183 @@
|
||||
/* queue.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 QUEUE_H
|
||||
#define QUEUE_H
|
||||
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
/**
|
||||
* @brief Template class for a bounded queue.
|
||||
*
|
||||
* An instance can make a queueElement be any class desired
|
||||
* but must create a std::vector of shared_ptr to queueElements.
|
||||
*/
|
||||
template <typename T>
|
||||
class Queue
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(Queue);
|
||||
typedef std::tr1::shared_ptr<T> queueElementPtr;
|
||||
typedef std::vector<queueElementPtr> queueElementPtrArray;
|
||||
/**
|
||||
* Constructor
|
||||
* @param elementArray The vector of shared_ptr to queue elements.
|
||||
*/
|
||||
Queue(queueElementPtrArray & elementArray);
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~Queue();
|
||||
/**
|
||||
* Clear the queue.
|
||||
*/
|
||||
void clear();
|
||||
/**
|
||||
* get the capacity of the queue, i. e. number of queue elements,
|
||||
* @return The capacity.
|
||||
*/
|
||||
int capacity();
|
||||
/**
|
||||
* Get the number of free elements in the queue.
|
||||
* @return The number.
|
||||
*/
|
||||
int getNumberFree();
|
||||
/**
|
||||
* Get the number of used elements in the queue.
|
||||
* This is the number that have been setUsed but not released.
|
||||
* @return The number.
|
||||
*/
|
||||
int getNumberUsed();
|
||||
/**
|
||||
* Get the next free element.
|
||||
* @return a shared_ptr to the queue element.
|
||||
* This is null if queue was full.
|
||||
*/
|
||||
queueElementPtr & getFree();
|
||||
/**
|
||||
* Set the element returned by getFree as used.
|
||||
* Until this is called getUsed will not return it.
|
||||
* @param element The element. It must be the element returned
|
||||
* by the most recent call to getUsed.
|
||||
*/
|
||||
void setUsed(queueElementPtr const &element);
|
||||
/**
|
||||
* Get the oldest used element;
|
||||
* @return a shared_ptr to the queue element.
|
||||
* This is null if no used element is available.`
|
||||
*/
|
||||
queueElementPtr & getUsed();
|
||||
/**
|
||||
* Release the element obtained by the most recent call to getUsed.
|
||||
* @param element The element.
|
||||
*/
|
||||
void releaseUsed(queueElementPtr const &element);
|
||||
private:
|
||||
queueElementPtr nullElement;
|
||||
queueElementPtrArray elements;
|
||||
// TODO use size_t instead
|
||||
int size;
|
||||
int numberFree;
|
||||
int numberUsed;
|
||||
int nextGetFree;
|
||||
int nextSetUsed;
|
||||
int nextGetUsed;
|
||||
int nextReleaseUsed;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
Queue<T>::Queue(std::vector<queueElementPtr> &xxx)
|
||||
: size(static_cast<int>(xxx.size())),
|
||||
numberFree(size),
|
||||
numberUsed(0),
|
||||
nextGetFree(0),
|
||||
nextSetUsed(0),
|
||||
nextGetUsed(0),
|
||||
nextReleaseUsed(0)
|
||||
{
|
||||
elements.swap(xxx);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Queue<T>::~Queue(){}
|
||||
|
||||
template <typename T>
|
||||
int Queue<T>::capacity(){return size;}
|
||||
|
||||
template <typename T>
|
||||
int Queue<T>::getNumberFree(){return numberFree;}
|
||||
|
||||
template <typename T>
|
||||
int Queue<T>::getNumberUsed(){return numberUsed;}
|
||||
|
||||
template <typename T>
|
||||
void Queue<T>::clear()
|
||||
{
|
||||
numberFree = size;
|
||||
numberUsed = 0;
|
||||
nextGetFree = 0;
|
||||
nextSetUsed = 0;
|
||||
nextGetUsed = 0;
|
||||
nextReleaseUsed = 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::tr1::shared_ptr<T> & Queue<T>::getFree()
|
||||
{
|
||||
if(numberFree==0) return nullElement;
|
||||
numberFree--;
|
||||
int ind = nextGetFree;
|
||||
std::tr1::shared_ptr<T> queueElement = elements[nextGetFree++];
|
||||
if(nextGetFree>=size) nextGetFree = 0;
|
||||
return elements[ind];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Queue<T>::setUsed(std::tr1::shared_ptr<T> const &element)
|
||||
{
|
||||
if(element!=elements[nextSetUsed++]) {
|
||||
throw std::logic_error("not correct queueElement");
|
||||
}
|
||||
numberUsed++;
|
||||
if(nextSetUsed>=size) nextSetUsed = 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::tr1::shared_ptr<T> & Queue<T>::getUsed()
|
||||
{
|
||||
if(numberUsed==0) return nullElement;
|
||||
int ind = nextGetUsed;
|
||||
std::tr1::shared_ptr<T> queueElement = elements[nextGetUsed++];
|
||||
if(nextGetUsed>=size) nextGetUsed = 0;
|
||||
return elements[ind];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Queue<T>::releaseUsed(std::tr1::shared_ptr<T> const &element)
|
||||
{
|
||||
if(element!=elements[nextReleaseUsed++]) {
|
||||
throw std::logic_error(
|
||||
"not queueElement returned by last call to getUsed");
|
||||
}
|
||||
if(nextReleaseUsed>=size) nextReleaseUsed = 0;
|
||||
numberUsed--;
|
||||
numberFree++;
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
#endif /* QUEUE_H */
|
||||
|
||||
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
#ifndef REFTRACK_H
|
||||
#define REFTRACK_H
|
||||
|
||||
/** @page pvd_reftrack RefTrack
|
||||
*
|
||||
* reftrack.h is a utility for listing, finding, and reading global atomic counters.
|
||||
* By convention used to expose object instance counters as a way of detecting (slow)
|
||||
* reference/resource leaks before they cause problems.
|
||||
*
|
||||
* cf. the IOC shell commands "refshow", "refsave", and "refdiff".
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* @code
|
||||
* // my header.h
|
||||
* struct MyClass {
|
||||
* MyClass();
|
||||
* ~MyClass();
|
||||
* static size_t num_instances;
|
||||
* ...
|
||||
* };
|
||||
* ...
|
||||
* // my src.cpp
|
||||
* size_t MyClass::num_instances;
|
||||
* MyClass::MyClass() {
|
||||
* REFTRACE_INCREMENT(num_instances);
|
||||
* }
|
||||
* MyClass::~MyClass() {
|
||||
* REFTRACE_DECREMENT(num_instances);
|
||||
* }
|
||||
* // in some IOC registrar or global ctor
|
||||
* registerRefCounter("MyClass", &MyClass::num_instances);
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <epicsVersion.h>
|
||||
|
||||
#ifndef VERSION_INT
|
||||
# define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P))
|
||||
#endif
|
||||
|
||||
#ifndef EPICS_VERSION_INT
|
||||
# define EPICS_VERSION_INT VERSION_INT(EPICS_VERSION, EPICS_REVISION, EPICS_MODIFICATION, EPICS_PATCH_LEVEL)
|
||||
#endif
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,1,0)
|
||||
# include <epicsAtomic.h>
|
||||
# define REFTRACK_USE_ATOMIC
|
||||
#endif
|
||||
|
||||
#ifdef REFTRACK_USE_ATOMIC
|
||||
# define REFTRACE_INCREMENT(counter) ::epics::atomic::increment(counter)
|
||||
# define REFTRACE_DECREMENT(counter) ::epics::atomic::decrement(counter)
|
||||
#else
|
||||
# define REFTRACE_INCREMENT(counter) do{}while(0)
|
||||
# define REFTRACE_DECREMENT(counter) do{}while(0)
|
||||
#endif
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics {
|
||||
|
||||
//! Register new global reference counter
|
||||
epicsShareFunc
|
||||
void registerRefCounter(const char *name, const size_t* counter);
|
||||
|
||||
//! Remove registration of global reference counter (if dynamically allocated)
|
||||
epicsShareFunc
|
||||
void unregisterRefCounter(const char *name, const size_t* counter);
|
||||
|
||||
//! Fetch current value of single reference counter
|
||||
epicsShareFunc
|
||||
size_t readRefCounter(const char *name);
|
||||
|
||||
//! Represent a snapshot of many reference counters
|
||||
class epicsShareClass RefSnapshot
|
||||
{
|
||||
public:
|
||||
//! A single count
|
||||
struct Count {
|
||||
size_t current;
|
||||
long delta; //!< current - previous
|
||||
Count() :current(0u), delta(0) {}
|
||||
explicit Count(size_t c, long d) :current(c), delta(d) {}
|
||||
bool operator==(const Count& o) const
|
||||
{ return current==o.current && delta==o.delta; }
|
||||
};
|
||||
|
||||
private:
|
||||
typedef std::map<std::string, Count> cnt_map_t;
|
||||
cnt_map_t counts;
|
||||
public:
|
||||
typedef cnt_map_t::const_iterator iterator;
|
||||
typedef cnt_map_t::const_iterator const_iterator;
|
||||
|
||||
/** Fetch values of all reference counters.
|
||||
*
|
||||
* This involves many atomic reads, not a single operation.
|
||||
*/
|
||||
void update();
|
||||
|
||||
const Count& operator[](const std::string& name) const;
|
||||
|
||||
iterator begin() const { return counts.begin(); }
|
||||
iterator end() const { return counts.end(); }
|
||||
size_t size() const { return counts.size(); }
|
||||
|
||||
inline void swap(RefSnapshot& o)
|
||||
{
|
||||
counts.swap(o.counts);
|
||||
}
|
||||
|
||||
/** Compute the difference lhs - rhs
|
||||
*
|
||||
* Returned RefSnapshot has Count::current=lhs.current
|
||||
* and Count::delta= lhs.current - rhs.current
|
||||
*/
|
||||
RefSnapshot operator-(const RefSnapshot& rhs) const;
|
||||
};
|
||||
|
||||
//! Print all counters with a non-zero delta
|
||||
epicsShareFunc
|
||||
std::ostream& operator<<(std::ostream& strm, const RefSnapshot& snap);
|
||||
|
||||
//! Helper to run a thread which periodically prints (via show() )
|
||||
//! global reference counter deltas.
|
||||
class epicsShareClass RefMonitor
|
||||
{
|
||||
struct Impl;
|
||||
Impl *impl;
|
||||
public:
|
||||
RefMonitor();
|
||||
virtual ~RefMonitor();
|
||||
|
||||
void start(double period=10.0);
|
||||
void stop();
|
||||
bool running() const;
|
||||
|
||||
//! call show() with current snapshot
|
||||
void current();
|
||||
protected:
|
||||
//! Default prints to stderr
|
||||
//! @param complete when false show only non-zero delta, when true show non-zero count or delta
|
||||
virtual void show(const RefSnapshot& snap, bool complete=false);
|
||||
};
|
||||
|
||||
} // namespace epics
|
||||
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/** Fetch and print current snapshot
|
||||
* @return NULL or a char* which must be free()'d
|
||||
*/
|
||||
char* epicsRefSnapshotCurrent();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif // REFTRACK_H
|
||||
71
src/misc/pv/requester.h
Normal file
71
src/misc/pv/requester.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/* requester.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 REQUESTER_H
|
||||
#define REQUESTER_H
|
||||
#include <string>
|
||||
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class Requester;
|
||||
typedef std::tr1::shared_ptr<Requester> RequesterPtr;
|
||||
|
||||
enum MessageType {
|
||||
infoMessage,warningMessage,errorMessage,fatalErrorMessage
|
||||
};
|
||||
#define MESSAGE_TYPE_COUNT 4
|
||||
|
||||
epicsShareExtern std::string getMessageTypeName(MessageType messageType);
|
||||
|
||||
/**
|
||||
* @brief Callback class for passing messages to a requester.
|
||||
*
|
||||
* This is used by many other classes and also extended by other classes.
|
||||
* The request is passed a message and a messageType.
|
||||
* A message is just a string and a messageType is:
|
||||
@code
|
||||
enum MessageType {
|
||||
infoMessage,warningMessage,errorMessage,fatalErrorMessage
|
||||
};
|
||||
@endcode
|
||||
*
|
||||
*/
|
||||
|
||||
class epicsShareClass Requester {
|
||||
public:
|
||||
POINTER_DEFINITIONS(Requester);
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~Requester(){}
|
||||
/**
|
||||
* The requester must have a name.
|
||||
* @return The requester's name.
|
||||
*/
|
||||
virtual std::string getRequesterName() = 0;
|
||||
/**
|
||||
*
|
||||
* A message for the requester.
|
||||
* @param message The message.
|
||||
* @param messageType The type of message:
|
||||
@code
|
||||
enum MessageType {
|
||||
infoMessage,warningMessage,errorMessage,fatalErrorMessage
|
||||
};
|
||||
@endcode
|
||||
*/
|
||||
virtual void message(std::string const & message,MessageType messageType);
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* REQUESTER_H */
|
||||
@@ -26,8 +26,7 @@ namespace epics {
|
||||
* @brief Serialization helper.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass SerializeHelper {
|
||||
EPICS_NOT_COPYABLE(SerializeHelper)
|
||||
class epicsShareClass SerializeHelper : public NoDefaultMethods {
|
||||
public:
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,9 +9,7 @@
|
||||
#ifndef SHAREDPTR_H
|
||||
#define SHAREDPTR_H
|
||||
|
||||
#include <memory> /* for auto_ptr */
|
||||
|
||||
/** @file sharedPtr.h
|
||||
/*
|
||||
* Pulls in the std::tr1 namespace with the following names
|
||||
*
|
||||
* class shared_ptr
|
||||
@@ -25,58 +23,12 @@
|
||||
* function enable_shared_from_this
|
||||
*/
|
||||
|
||||
/* where should we look? (In decending order of preference)
|
||||
*
|
||||
* # manual (per source file) selection
|
||||
* # c++11 version of <memory>, then alias into tr1
|
||||
* # <tr1/memory>
|
||||
* # boost version of tr1/memory
|
||||
*/
|
||||
// where should we look?
|
||||
|
||||
/* Debugging shared_ptr with debugPtr.h requires >= c++11
|
||||
*
|
||||
* Define DEBUG_SHARED_PTR globally to cause epics::debug::shared_ptr
|
||||
* to be injected as std::tr1::shared_ptr and the macro
|
||||
* HAVE_SHOW_REFS will be defined.
|
||||
*
|
||||
* epics::debug::shared_ptr wraps std::shared_ptr with additional
|
||||
* tracking of backwards references.
|
||||
* std::shared_ptr::use_count() gives the number of shared_ptr
|
||||
* (strong refs) to the referenced object.
|
||||
*
|
||||
* If use_count()==5 then epics::debug::shared_ptr::show_refs() will print
|
||||
* 5 lines of the format
|
||||
*
|
||||
* # <addr>: <IP0> <IP1> ...
|
||||
*
|
||||
* Given the numberic address of each shared_ptr as well as the call stack
|
||||
* at the point where it was initialized.
|
||||
* Use the 'addr2line' utility to interpret the stack addresses.
|
||||
*
|
||||
* On linux w/ ASLR it is necessary to turn on static linking to meaningfully
|
||||
* interpret call stack addresses.
|
||||
* Append "STATIC_BUILD=YES" to configure/CONFIG_SITE
|
||||
*/
|
||||
//#define DEBUG_SHARED_PTR
|
||||
|
||||
#if defined(SHARED_FROM_MANUAL)
|
||||
// define SHARED_FROM_MANUAL if from some reason it is desirable to manually select
|
||||
// which shared_ptr implementation to use
|
||||
#elif __cplusplus>=201103L || (defined(_MSC_VER) && (_MSC_VER>=1600)) || defined(_LIBCPP_VERSION)
|
||||
// MSVC has been bad about incrementing __cplusplus, even when new features are added. shared_ptr from MSVC 2010
|
||||
// the llvm libc++ doesn't bother with tr1, and puts shared_ptr in std:: even with -std=c++98
|
||||
# define SHARED_FROM_STD
|
||||
|
||||
#elif defined(__GNUC__) && __GNUC__>=4 && !defined(vxWorks)
|
||||
#if defined(__GNUC__) && __GNUC__>=4 && !defined(vxWorks)
|
||||
// GCC >=4.0.0
|
||||
# define SHARED_FROM_TR1
|
||||
|
||||
#elif defined(_MSC_VER) && _MSC_VER==1500
|
||||
// MSVC 2009 (eg. Visual C++ for Python 2.7)
|
||||
// Dinkumware _CPPLIB_VER=505
|
||||
// Has std::tr1::shared_ptr in <memory>
|
||||
# define SHARED_TR1_FROM_STD
|
||||
|
||||
#elif defined(_MSC_VER) && (_MSC_VER>1500 || defined(_HAS_TR1))
|
||||
// MSVC > 2008, or 2008 w/ SP1
|
||||
# define SHARED_FROM_TR1
|
||||
@@ -85,51 +37,34 @@
|
||||
# define SHARED_FROM_BOOST
|
||||
#endif
|
||||
|
||||
// go and get it
|
||||
#if defined(_MSC_VER) && (_MSC_VER>=1600)
|
||||
// MSVC 2010 has it in <memory>
|
||||
# undef SHARED_FROM_BOOST
|
||||
# undef SHARED_FROM_TR1
|
||||
#endif
|
||||
|
||||
#if defined(SHARED_FROM_MANUAL)
|
||||
// no-op
|
||||
#elif defined(SHARED_FROM_STD)
|
||||
#if defined(__clang__)
|
||||
# undef SHARED_FROM_BOOST
|
||||
# undef SHARED_FROM_TR1
|
||||
|
||||
#include <memory>
|
||||
|
||||
#ifndef DEBUG_SHARED_PTR
|
||||
|
||||
namespace std {
|
||||
// import std classes into std::tr1
|
||||
namespace std {
|
||||
namespace tr1 {
|
||||
using ::std::shared_ptr;
|
||||
using ::std::weak_ptr;
|
||||
using ::std::static_pointer_cast;
|
||||
using ::std::dynamic_pointer_cast;
|
||||
using ::std::const_pointer_cast;
|
||||
using ::std::enable_shared_from_this;
|
||||
using ::std::bad_weak_ptr;
|
||||
using std::shared_ptr;
|
||||
using std::weak_ptr;
|
||||
using std::static_pointer_cast;
|
||||
using std::dynamic_pointer_cast;
|
||||
using std::const_pointer_cast;
|
||||
using std::enable_shared_from_this;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#else // DEBUG_SHARED_PTR
|
||||
// go and get it
|
||||
|
||||
#include "debugPtr.h"
|
||||
|
||||
namespace std {
|
||||
namespace tr1 {
|
||||
using ::epics::debug::shared_ptr;
|
||||
using ::epics::debug::weak_ptr;
|
||||
using ::epics::debug::static_pointer_cast;
|
||||
using ::epics::debug::dynamic_pointer_cast;
|
||||
using ::epics::debug::const_pointer_cast;
|
||||
using ::epics::debug::enable_shared_from_this;
|
||||
using ::std::bad_weak_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // DEBUG_SHARED_PTR
|
||||
|
||||
#elif defined(SHARED_TR1_FROM_STD)
|
||||
# include <memory>
|
||||
|
||||
#elif defined(SHARED_FROM_TR1)
|
||||
#if defined(SHARED_FROM_TR1)
|
||||
# include <tr1/memory>
|
||||
|
||||
#elif defined(SHARED_FROM_BOOST)
|
||||
@@ -142,15 +77,12 @@ namespace std {
|
||||
# include <boost/tr1/memory.hpp>
|
||||
|
||||
#else
|
||||
# error No shared_ptr selection
|
||||
// eventually...
|
||||
# include <memory>
|
||||
#endif
|
||||
|
||||
// cleanup
|
||||
|
||||
#ifdef SHARED_FROM_STD
|
||||
# undef SHARED_FROM_STD
|
||||
#endif
|
||||
|
||||
#ifdef SHARED_FROM_TR1
|
||||
# undef SHARED_FROM_TR1
|
||||
#endif
|
||||
@@ -159,78 +91,10 @@ namespace std {
|
||||
# undef SHARED_FROM_BOOST
|
||||
#endif
|
||||
|
||||
namespace detail {
|
||||
template<typename T>
|
||||
struct ref_shower {
|
||||
const std::tr1::shared_ptr<T>& ptr;
|
||||
bool self, weak;
|
||||
ref_shower(const std::tr1::shared_ptr<T>& ptr, bool self, bool weak) :ptr(ptr),self(self),weak(weak) {}
|
||||
};
|
||||
}
|
||||
|
||||
/** Print a list (one per line) of shared_ptr which refer to the same object
|
||||
*
|
||||
* @param ptr Use the object pointed to by this shared_ptr
|
||||
* @param self include or omit a line for this shared_ptr
|
||||
* @param weak include a line for each weak_ptr (not implemented)
|
||||
@code
|
||||
shared_ptr<int> x;
|
||||
std::cout << show_referrers(x);
|
||||
@endcode
|
||||
*/
|
||||
template<typename T>
|
||||
inline ::detail::ref_shower<T> show_referrers(const std::tr1::shared_ptr<T>& ptr, bool self=true, bool weak=false)
|
||||
{
|
||||
return ::detail::ref_shower<T>(ptr, self, weak);
|
||||
}
|
||||
|
||||
namespace std{
|
||||
template<typename T>
|
||||
inline std::ostream& operator<<(std::ostream& strm, const ::detail::ref_shower<T>& refs)
|
||||
{
|
||||
#ifdef HAVE_SHOW_REFS
|
||||
refs.ptr.show_refs(strm, refs.self, refs.weak);
|
||||
#endif // HAVE_SHOW_REFS
|
||||
return strm;
|
||||
}
|
||||
}//namespace std
|
||||
|
||||
#define POINTER_DEFINITIONS(clazz) \
|
||||
typedef std::tr1::shared_ptr<clazz> shared_pointer; \
|
||||
typedef std::tr1::shared_ptr<const clazz> const_shared_pointer; \
|
||||
typedef std::tr1::weak_ptr<clazz> weak_pointer; \
|
||||
typedef std::tr1::weak_ptr<const clazz> const_weak_pointer
|
||||
|
||||
namespace epics{
|
||||
/** A semi-hack to help with migration from std::auto_ptr to std::unique_ptr,
|
||||
* and avoid copious deprecation warning spam
|
||||
* which may be hiding legitimate issues.
|
||||
*
|
||||
* Provides epics::auto_ptr<T> and epics::swap()
|
||||
*
|
||||
* epics::auto_ptr<T> is std::auto_ptr<T> for c++98
|
||||
* and std::unique_ptr<T> for >= c++11.
|
||||
*
|
||||
* epics::swap() is the only supported operation.
|
||||
* copy/assignment/return are not supported
|
||||
* (use auto_ptr or unique_ptr explicitly).
|
||||
*/
|
||||
#if __cplusplus>=201103L
|
||||
template<typename T>
|
||||
using auto_ptr = std::unique_ptr<T>;
|
||||
template<typename T>
|
||||
static inline void swap(auto_ptr<T>& lhs, auto_ptr<T>& rhs) {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
#else
|
||||
using std::auto_ptr;
|
||||
template<typename T>
|
||||
static inline void swap(auto_ptr<T>& lhs, auto_ptr<T>& rhs) {
|
||||
auto_ptr<T> temp(lhs);
|
||||
lhs = rhs;
|
||||
rhs = temp;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // SHAREDPTR_H
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
#ifndef SHAREDVECTOR_H
|
||||
#define SHAREDVECTOR_H
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <ostream>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <iterator>
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
# include <initializer_list>
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "pv/sharedPtr.h"
|
||||
@@ -70,24 +70,18 @@ namespace detail {
|
||||
*/
|
||||
|
||||
public:
|
||||
#if __cplusplus>=201103L
|
||||
//! @brief Empty vector (not very interesting)
|
||||
constexpr shared_vector_base() noexcept
|
||||
:m_sdata(), m_offset(0), m_count(0), m_total(0)
|
||||
{}
|
||||
#else
|
||||
|
||||
//! @brief Empty vector (not very interesting)
|
||||
shared_vector_base()
|
||||
:m_sdata(), m_offset(0), m_count(0), m_total(0)
|
||||
{}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// helper for constructors
|
||||
// Ensure that offset and size are zero when we are constructed with NULL
|
||||
void _null_input()
|
||||
{
|
||||
if(!m_sdata) {
|
||||
if(!m_sdata.get()) {
|
||||
m_offset = m_total = m_count = 0;
|
||||
} else {
|
||||
// ensure we won't have integer overflows later
|
||||
@@ -96,12 +90,19 @@ namespace detail {
|
||||
}
|
||||
public:
|
||||
|
||||
#ifdef _WIN32
|
||||
template<typename A>
|
||||
shared_vector_base(A* v, size_t o, size_t c)
|
||||
:m_sdata(v, detail::default_array_deleter<A*>())
|
||||
,m_offset(o), m_count(c), m_total(c)
|
||||
{_null_input();}
|
||||
|
||||
#else
|
||||
template<typename A>
|
||||
shared_vector_base(A v, size_t o, size_t c)
|
||||
:m_sdata(v, detail::default_array_deleter<A>())
|
||||
,m_offset(o), m_count(c), m_total(c)
|
||||
{_null_input();}
|
||||
#endif
|
||||
shared_vector_base(const std::tr1::shared_ptr<E>& d, size_t o, size_t c)
|
||||
:m_sdata(d), m_offset(o), m_count(c), m_total(c)
|
||||
{_null_input();}
|
||||
@@ -117,17 +118,6 @@ namespace detail {
|
||||
,m_count(O.m_count), m_total(O.m_total)
|
||||
{}
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
shared_vector_base(shared_vector_base &&O)
|
||||
:m_sdata(std::move(O.m_sdata))
|
||||
,m_offset(O.m_offset)
|
||||
,m_count(O.m_count)
|
||||
,m_total(O.m_total)
|
||||
{
|
||||
O.clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
typedef typename meta::strip_const<E>::type _E_non_const;
|
||||
public:
|
||||
@@ -142,11 +132,7 @@ namespace detail {
|
||||
{
|
||||
if(!O.unique())
|
||||
throw std::runtime_error("Can't freeze non-unique vector");
|
||||
#if __cplusplus >= 201103L
|
||||
m_sdata = std::move(O.m_sdata);
|
||||
#else
|
||||
m_sdata = O.m_sdata;
|
||||
#endif
|
||||
O.clear();
|
||||
}
|
||||
|
||||
@@ -160,11 +146,7 @@ namespace detail {
|
||||
,m_total(O.m_total)
|
||||
{
|
||||
O.make_unique();
|
||||
#if __cplusplus >= 201103L
|
||||
m_sdata = std::move(std::tr1::const_pointer_cast<E>(O.m_sdata));
|
||||
#else
|
||||
m_sdata = std::tr1::const_pointer_cast<E>(O.m_sdata);
|
||||
#endif
|
||||
O.clear();
|
||||
}
|
||||
|
||||
@@ -180,21 +162,6 @@ namespace detail {
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
//! @brief Move an existing vector
|
||||
shared_vector_base& operator=(shared_vector_base&& o)
|
||||
{
|
||||
if(&o!=this) {
|
||||
m_sdata=std::move(o.m_sdata);
|
||||
m_offset=o.m_offset;
|
||||
m_count=o.m_count;
|
||||
m_total=o.m_total;
|
||||
o.clear();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
//! @brief Swap the contents of this vector with another
|
||||
void swap(shared_vector_base& o) {
|
||||
if(&o!=this) {
|
||||
@@ -213,12 +180,11 @@ namespace detail {
|
||||
}
|
||||
|
||||
//! @brief Data is not shared?
|
||||
bool unique() const {return !m_sdata || m_sdata.use_count()<=1;}
|
||||
bool unique() const {return !m_sdata || m_sdata.unique();}
|
||||
|
||||
|
||||
//! @brief Number of elements visible through this vector
|
||||
size_t size() const{return m_count;}
|
||||
//! @brief shorthand for size()==0
|
||||
bool empty() const{return !m_count;}
|
||||
|
||||
|
||||
@@ -252,9 +218,7 @@ namespace detail {
|
||||
|
||||
m_total -= offset;
|
||||
|
||||
if(length > max_count)
|
||||
length = max_count;
|
||||
m_count = length;
|
||||
m_count = std::min(length, max_count);
|
||||
}
|
||||
|
||||
// Access to members.
|
||||
@@ -308,22 +272,9 @@ public:
|
||||
// allow specialization for all E to be friends
|
||||
template<typename E1, class Enable1> friend class shared_vector;
|
||||
|
||||
//! @brief Empty vector (not very interesting)
|
||||
#if __cplusplus>=201103L
|
||||
constexpr shared_vector() noexcept :base_t() {}
|
||||
#else
|
||||
shared_vector() :base_t() {}
|
||||
#endif
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
template<typename A>
|
||||
shared_vector(std::initializer_list<A> L)
|
||||
:base_t(new _E_non_const[L.size()], 0, L.size())
|
||||
{
|
||||
_E_non_const *raw = const_cast<_E_non_const*>(data());
|
||||
std::copy(L.begin(), L.end(), raw);
|
||||
}
|
||||
#endif
|
||||
//! @brief Empty vector (not very interesting)
|
||||
shared_vector() :base_t() {}
|
||||
|
||||
//! @brief Allocate (with new[]) a new vector of size c
|
||||
explicit shared_vector(size_t c)
|
||||
@@ -370,11 +321,6 @@ public:
|
||||
//! @brief Copy an existing vector of same type
|
||||
shared_vector(const shared_vector& o) :base_t(o) {}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
//! @brief Move an existing vector of same type
|
||||
shared_vector(shared_vector&& o) :base_t(std::move(o)) {}
|
||||
#endif
|
||||
|
||||
//! @internal
|
||||
//! Internal for static_shared_vector_cast
|
||||
template<typename FROM>
|
||||
@@ -396,20 +342,6 @@ public:
|
||||
:base_t(O,t)
|
||||
{}
|
||||
|
||||
inline shared_vector& operator=(const shared_vector& o)
|
||||
{
|
||||
this->base_t::operator=(o);
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
inline shared_vector& operator=(shared_vector&& o)
|
||||
{
|
||||
this->base_t::operator=(std::move(o));
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t max_size() const{return ((size_t)-1)/sizeof(E);}
|
||||
|
||||
size_t capacity() const { return this->m_total; }
|
||||
@@ -421,16 +353,11 @@ public:
|
||||
* does not increase.
|
||||
*
|
||||
* For notes on copying see docs for make_unique().
|
||||
*
|
||||
* @throws std::bad_alloc if requested allocation can not be made
|
||||
* @throws other exceptions from element copy ctor
|
||||
*/
|
||||
void reserve(size_t i) {
|
||||
if(this->unique() && i<=this->m_total)
|
||||
return;
|
||||
size_t new_count = this->m_count;
|
||||
if(new_count > i)
|
||||
new_count = i;
|
||||
size_t new_count = std::min(this->m_count, i);
|
||||
_E_non_const* temp=new _E_non_const[i];
|
||||
try{
|
||||
std::copy(begin(), begin()+new_count, temp);
|
||||
@@ -450,16 +377,13 @@ public:
|
||||
* as if make_unique() were called. This holds even if the size does not change.
|
||||
*
|
||||
* For notes on copying see docs for make_unique().
|
||||
*
|
||||
* @throws std::bad_alloc if requested allocation can not be made
|
||||
* @throws other exceptions from element copy ctor
|
||||
*/
|
||||
void resize(size_t i) {
|
||||
if(i==this->m_count) {
|
||||
make_unique();
|
||||
return;
|
||||
}
|
||||
if(this->m_sdata && this->m_sdata.use_count()==1) {
|
||||
if(this->m_sdata && this->m_sdata.unique()) {
|
||||
// we have data and exclusive ownership of it
|
||||
if(i<=this->m_total) {
|
||||
// We have room to grow (or shrink)!
|
||||
@@ -468,18 +392,13 @@ public:
|
||||
}
|
||||
}
|
||||
// must re-allocate :(
|
||||
size_t new_total = this->m_total;
|
||||
if(new_total < i)
|
||||
new_total = i;
|
||||
size_t new_total = std::max(this->m_total, i);
|
||||
_E_non_const* temp=new _E_non_const[new_total];
|
||||
try{
|
||||
size_t n = this->size();
|
||||
if(n > i)
|
||||
n = i;
|
||||
// Copy as much as possible from old,
|
||||
// remaining elements are uninitialized.
|
||||
std::copy(begin(),
|
||||
begin()+n,
|
||||
begin()+std::min(i,this->size()),
|
||||
temp);
|
||||
this->m_sdata.reset(temp, detail::default_array_deleter<pointer>());
|
||||
}catch(...){
|
||||
@@ -519,14 +438,10 @@ public:
|
||||
}
|
||||
assert(original.unique());
|
||||
@endcode
|
||||
*
|
||||
* @throws std::bad_alloc if requested allocation can not be made
|
||||
* @throws other exceptions from element copy ctor
|
||||
*/
|
||||
void make_unique() {
|
||||
if(this->unique())
|
||||
return;
|
||||
// at this point we know that !!m_sdata, so get()!=NULL
|
||||
_E_non_const *d = new _E_non_const[this->m_total];
|
||||
try {
|
||||
std::copy(this->m_sdata.get()+this->m_offset,
|
||||
@@ -540,27 +455,13 @@ public:
|
||||
this->m_offset=0;
|
||||
}
|
||||
|
||||
private:
|
||||
/* Hack alert.
|
||||
* For reasons of simplicity and efficiency, we want to use raw pointers for iteration.
|
||||
* However, shared_ptr::get() isn't defined when !m_sdata, although practically it gives NULL.
|
||||
* Unfortunately, many of the MSVC (<= VS 2010) STL methods assert() that iterators are never NULL.
|
||||
* So we fudge here by abusing 'this' so that our iterators are always !NULL.
|
||||
*/
|
||||
inline E* base_ptr() const {
|
||||
#if defined(_MSC_VER) && _MSC_VER<=1600
|
||||
return this->m_count ? this->m_sdata.get() : (E*)(this-1);
|
||||
#else
|
||||
return this->m_sdata.get();
|
||||
#endif
|
||||
}
|
||||
public:
|
||||
|
||||
// STL iterators
|
||||
|
||||
iterator begin() const{return this->base_ptr()+this->m_offset;}
|
||||
iterator begin() const{return this->m_sdata.get()+this->m_offset;}
|
||||
const_iterator cbegin() const{return begin();}
|
||||
|
||||
iterator end() const{return this->base_ptr()+this->m_offset+this->m_count;}
|
||||
iterator end() const{return this->m_sdata.get()+this->m_offset+this->m_count;}
|
||||
const_iterator cend() const{return end();}
|
||||
|
||||
reverse_iterator rbegin() const{return reverse_iterator(end());}
|
||||
@@ -612,15 +513,10 @@ public:
|
||||
|
||||
// data access
|
||||
|
||||
//! @brief Return Base pointer
|
||||
pointer data() const{return this->m_sdata.get()+this->m_offset;}
|
||||
|
||||
//! @brief Member access
|
||||
//! Undefined if empty()==true.
|
||||
reference operator[](size_t i) const {return this->m_sdata.get()[this->m_offset+i];}
|
||||
|
||||
//! @brief Member access
|
||||
//! @throws std::out_of_range if i>=size().
|
||||
reference at(size_t i) const
|
||||
{
|
||||
if(i>this->m_count)
|
||||
@@ -667,11 +563,7 @@ public:
|
||||
|
||||
typedef std::tr1::shared_ptr<E> shared_pointer_type;
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
constexpr shared_vector() noexcept :base_t(), m_vtype((ScalarType)-1) {}
|
||||
#else
|
||||
shared_vector() :base_t(), m_vtype((ScalarType)-1) {}
|
||||
#endif
|
||||
|
||||
shared_vector(pointer v, size_t o, size_t c)
|
||||
:base_t(v,o,c), m_vtype((ScalarType)-1) {}
|
||||
@@ -687,11 +579,6 @@ public:
|
||||
shared_vector(const shared_vector& o)
|
||||
:base_t(o), m_vtype(o.m_vtype) {}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
shared_vector(shared_vector&& o)
|
||||
:base_t(std::move(o)), m_vtype(o.m_vtype) {}
|
||||
#endif
|
||||
|
||||
//! @internal
|
||||
//! Internal for static_shared_vector_cast
|
||||
template<typename FROM>
|
||||
@@ -722,22 +609,6 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
shared_vector& operator=(shared_vector&& o)
|
||||
{
|
||||
if(&o!=this) {
|
||||
this->base_t::operator=(std::move(o));
|
||||
m_vtype = o.m_vtype;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
void swap(shared_vector& o) {
|
||||
base_t::swap(o);
|
||||
std::swap(m_vtype, o.m_vtype);
|
||||
}
|
||||
|
||||
size_t max_size() const{return (size_t)-1;}
|
||||
|
||||
pointer data() const{
|
||||
@@ -752,21 +623,33 @@ namespace detail {
|
||||
template<typename TO, typename FROM, class Enable = void>
|
||||
struct static_shared_vector_caster { /* no default */ };
|
||||
// from void to non-void with same const-ness
|
||||
template<typename TO, typename FROM>
|
||||
struct static_shared_vector_caster<TO, FROM,
|
||||
typename meta::_and<meta::_and<meta::is_not_void<TO>, meta::is_void<FROM> >,
|
||||
meta::same_const<TO,FROM> >::type> {
|
||||
static inline shared_vector<TO> op(const shared_vector<FROM>& src) {
|
||||
template<typename TO>
|
||||
struct static_shared_vector_caster<TO, void,
|
||||
typename meta::_and<meta::same_const<TO,void>, meta::is_not_void<TO> >::type> {
|
||||
static inline shared_vector<TO> op(const shared_vector<void>& src) {
|
||||
return shared_vector<TO>(src, detail::_shared_vector_cast_tag());
|
||||
}
|
||||
};
|
||||
template<typename TO>
|
||||
struct static_shared_vector_caster<TO, const void,
|
||||
typename meta::_and<meta::same_const<TO,const void>, meta::is_not_void<TO> >::type> {
|
||||
static inline shared_vector<TO> op(const shared_vector<const void>& src) {
|
||||
return shared_vector<TO>(src, detail::_shared_vector_cast_tag());
|
||||
}
|
||||
};
|
||||
// from non-void to void with same const-ness
|
||||
template<typename TO, typename FROM>
|
||||
struct static_shared_vector_caster<TO, FROM,
|
||||
typename meta::_and<meta::_and<meta::is_void<TO>, meta::is_not_void<FROM> >,
|
||||
meta::same_const<TO,FROM> >::type> {
|
||||
static FORCE_INLINE shared_vector<TO> op(const shared_vector<FROM>& src) {
|
||||
return shared_vector<TO>(src, detail::_shared_vector_cast_tag());
|
||||
template<typename FROM>
|
||||
struct static_shared_vector_caster<void, FROM,
|
||||
typename meta::_and<meta::same_const<void,FROM>, meta::is_not_void<FROM> >::type> {
|
||||
static FORCE_INLINE shared_vector<void> op(const shared_vector<FROM>& src) {
|
||||
return shared_vector<void>(src, detail::_shared_vector_cast_tag());
|
||||
}
|
||||
};
|
||||
template<typename FROM>
|
||||
struct static_shared_vector_caster<const void, FROM,
|
||||
typename meta::_and<meta::same_const<const void,FROM>, meta::is_not_void<FROM> >::type> {
|
||||
static FORCE_INLINE shared_vector<const void> op(const shared_vector<FROM>& src) {
|
||||
return shared_vector<const void>(src, detail::_shared_vector_cast_tag());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -946,7 +829,7 @@ const_shared_vector_cast(shared_vector<FROM>& src)
|
||||
|
||||
namespace ScalarTypeFunc {
|
||||
//! Allocate an untyped array based on ScalarType
|
||||
epicsShareFunc shared_vector<void> allocArray(ScalarType id, size_t len);
|
||||
shared_vector<void> allocArray(ScalarType id, size_t len);
|
||||
|
||||
//! Allocate an untyped array based on ScalarType
|
||||
template<ScalarType ID>
|
||||
|
||||
@@ -46,14 +46,10 @@ namespace epics { namespace pvData {
|
||||
|
||||
static Status Ok;
|
||||
|
||||
static inline Status warn(const std::string& m) { return Status(STATUSTYPE_WARNING, m); }
|
||||
static inline Status error(const std::string& m) { return Status(STATUSTYPE_ERROR, m); }
|
||||
static inline Status fatal(const std::string& m) { return Status(STATUSTYPE_FATAL, m); }
|
||||
|
||||
/**
|
||||
* Creates OK status; STATUSTYPE_OK, empty message and stackDump.
|
||||
*/
|
||||
Status() :m_statusType(STATUSTYPE_OK) {}
|
||||
Status();
|
||||
|
||||
/**
|
||||
* Create non-OK status.
|
||||
@@ -65,25 +61,25 @@ namespace epics { namespace pvData {
|
||||
*/
|
||||
Status(StatusType type, std::string const & message, std::string const & stackDump);
|
||||
|
||||
virtual ~Status() {}
|
||||
~Status();
|
||||
|
||||
/**
|
||||
* Get status type.
|
||||
* @return status type, non-<code>null</code>.
|
||||
*/
|
||||
inline StatusType getType() const { return m_statusType; }
|
||||
StatusType getType() const;
|
||||
|
||||
/**
|
||||
* Get error message describing an error. Required if error status.
|
||||
* @return error message.
|
||||
*/
|
||||
inline const std::string& getMessage() const { return m_message; }
|
||||
std::string getMessage() const;
|
||||
|
||||
/**
|
||||
* Get stack dump where error (exception) happened. Optional.
|
||||
* @return stack dump.
|
||||
*/
|
||||
inline const std::string& getStackDump() const { return m_stackDump; }
|
||||
std::string getStackDump() const;
|
||||
|
||||
/**
|
||||
* Convenient OK test. Same as <code>(getType() == StatusType.OK)</code>.
|
||||
@@ -92,47 +88,13 @@ namespace epics { namespace pvData {
|
||||
* @return OK status.
|
||||
* @see #isSuccess()
|
||||
*/
|
||||
inline bool isOK() const {
|
||||
return (m_statusType == STATUSTYPE_OK);
|
||||
}
|
||||
bool isOK() const;
|
||||
|
||||
/**
|
||||
* Check if operation succeeded (OK or WARNING).
|
||||
* Check if operation succeeded.
|
||||
* @return operation success status.
|
||||
*/
|
||||
inline bool isSuccess() const {
|
||||
return (m_statusType == STATUSTYPE_OK || m_statusType == STATUSTYPE_WARNING);
|
||||
}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
FORCE_INLINE explicit operator bool() const {
|
||||
return isSuccess();
|
||||
}
|
||||
#else
|
||||
private:
|
||||
typedef bool (Status::*truth_type)() const;
|
||||
public:
|
||||
FORCE_INLINE operator truth_type() const {
|
||||
return isSuccess() ? &Status::isSuccess : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** override this Status if the other has higher StatusType
|
||||
@code
|
||||
Status ret;
|
||||
ret |= call1();
|
||||
if(ret)
|
||||
ret |= call2();
|
||||
return ret;
|
||||
@endcode
|
||||
*/
|
||||
void maximize(const Status& o);
|
||||
|
||||
//! short hand for "this->maximize(o)"
|
||||
FORCE_INLINE Status& operator|=(const Status& o) {
|
||||
maximize(o);
|
||||
return *this;
|
||||
}
|
||||
bool isSuccess() const;
|
||||
|
||||
void serialize(ByteBuffer *buffer, SerializableControl *flusher) const;
|
||||
void deserialize(ByteBuffer *buffer, DeserializableControl *flusher);
|
||||
@@ -147,15 +109,8 @@ namespace epics { namespace pvData {
|
||||
|
||||
};
|
||||
|
||||
FORCE_INLINE std::ostream& operator<<(std::ostream& o, const Status& status) {
|
||||
status.dump(o);
|
||||
return o;
|
||||
}
|
||||
|
||||
FORCE_INLINE std::ostream& operator<<(std::ostream& o, const Status::StatusType& statusType) {
|
||||
o << Status::StatusTypeName[statusType];
|
||||
return o;
|
||||
}
|
||||
epicsShareExtern std::ostream& operator<<(std::ostream& o, const Status& status);
|
||||
epicsShareExtern std::ostream& operator<<(std::ostream& o, const Status::StatusType& statusType);
|
||||
|
||||
}}
|
||||
#endif /* STATUS_H */
|
||||
|
||||
@@ -10,9 +10,7 @@
|
||||
// gently nudge the compiler to inline our wrappers
|
||||
// Warning: Only use this when the template body is *small*.
|
||||
// You have been warned!
|
||||
#if defined(__MINGW32__)
|
||||
# define FORCE_INLINE inline
|
||||
#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 402)
|
||||
#if defined(__GNUC__) && __GNUC__>=3
|
||||
# define FORCE_INLINE __attribute__((always_inline)) inline
|
||||
#elif defined(_MSC_VER)
|
||||
# define FORCE_INLINE __forceinline
|
||||
@@ -116,31 +114,6 @@ struct _and {};
|
||||
template<typename A, typename B, class R>
|
||||
struct _and<A,B, typename A::type, typename B::type, R> { typedef R type; };
|
||||
|
||||
/** Mangle type to best pass as an argument.
|
||||
*
|
||||
* POD types passed by value.
|
||||
* All others by const reference.
|
||||
*/
|
||||
template<typename T>
|
||||
struct arg_type {typedef const T& type;};
|
||||
#define SIMPLE_ARG_TYPE(TYPE) template<> struct arg_type<TYPE> { typedef TYPE type; };
|
||||
SIMPLE_ARG_TYPE(bool)
|
||||
SIMPLE_ARG_TYPE(char)
|
||||
SIMPLE_ARG_TYPE(signed char)
|
||||
SIMPLE_ARG_TYPE(unsigned char)
|
||||
SIMPLE_ARG_TYPE(short)
|
||||
SIMPLE_ARG_TYPE(unsigned short)
|
||||
SIMPLE_ARG_TYPE(int)
|
||||
SIMPLE_ARG_TYPE(unsigned int)
|
||||
SIMPLE_ARG_TYPE(long)
|
||||
SIMPLE_ARG_TYPE(unsigned long)
|
||||
SIMPLE_ARG_TYPE(long long)
|
||||
SIMPLE_ARG_TYPE(unsigned long long)
|
||||
SIMPLE_ARG_TYPE(float)
|
||||
SIMPLE_ARG_TYPE(double)
|
||||
SIMPLE_ARG_TYPE(long double)
|
||||
#undef SIMPLE_ARG_TYPE
|
||||
|
||||
}}}
|
||||
|
||||
#endif // TEMPLATEMETA_H
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
#include <functional>
|
||||
@@ -42,7 +41,37 @@ typedef std::tr1::shared_ptr<epicsThread> EpicsThreadPtr;
|
||||
|
||||
typedef epicsThreadRunable Runnable;
|
||||
|
||||
//! Helper for those cases where a class should have more than one runnable
|
||||
template<typename C>
|
||||
class epicsShareClass RunnableMethod : public Runnable, private NoDefaultMethods
|
||||
{
|
||||
typedef void (C::*meth_t)();
|
||||
C *inst;
|
||||
meth_t meth;
|
||||
|
||||
virtual void run()
|
||||
{
|
||||
(inst->*meth)();
|
||||
}
|
||||
public:
|
||||
RunnableMethod(C* inst, void (C::*meth)())
|
||||
:inst(inst), meth(meth)
|
||||
{}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
struct FuncRunner : public epicsThreadRunable
|
||||
{
|
||||
typedef void (*fn_t)(void*);
|
||||
fn_t fn;
|
||||
void *arg;
|
||||
FuncRunner(fn_t f, void *a) :fn(f), arg(a) {}
|
||||
virtual ~FuncRunner(){}
|
||||
virtual void run()
|
||||
{
|
||||
(*fn)(arg);
|
||||
}
|
||||
};
|
||||
template<typename C>
|
||||
struct MethRunner : public epicsThreadRunable
|
||||
{
|
||||
@@ -56,14 +85,26 @@ struct MethRunner : public epicsThreadRunable
|
||||
(inst->*fn)();
|
||||
}
|
||||
};
|
||||
#if __cplusplus>=201103L
|
||||
struct BindRunner : public epicsThreadRunable
|
||||
{
|
||||
typedef std::function<void()> fn_t;
|
||||
fn_t fn;
|
||||
BindRunner(const fn_t f) : fn(f) {}
|
||||
virtual ~BindRunner() {}
|
||||
virtual void run()
|
||||
{
|
||||
fn();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief C++ wrapper for epicsThread from EPICS base.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass Thread : public epicsThread {
|
||||
EPICS_NOT_COPYABLE(Thread)
|
||||
class epicsShareClass Thread : public epicsThread, private NoDefaultMethods {
|
||||
public:
|
||||
/** @brief Holds all the configuration necessary to launch a @class Thread
|
||||
*
|
||||
@@ -98,31 +139,56 @@ public:
|
||||
std::ostringstream p_strm;
|
||||
bool p_autostart;
|
||||
Runnable *p_runner;
|
||||
typedef epics::auto_ptr<Runnable> p_owned_runner_t;
|
||||
#if __cplusplus>=201103L
|
||||
typedef std::unique_ptr<Runnable> p_owned_runner_t;
|
||||
#else
|
||||
typedef std::auto_ptr<Runnable> p_owned_runner_t;
|
||||
#endif
|
||||
p_owned_runner_t p_owned_runner;
|
||||
friend class Thread;
|
||||
Runnable& x_getrunner();
|
||||
void x_setdefault();
|
||||
Runnable& x_getrunner()
|
||||
{
|
||||
if(!this->p_runner)
|
||||
throw std::logic_error("Thread::Config missing run()");
|
||||
return *this->p_runner;
|
||||
}
|
||||
void x_setdefault()
|
||||
{
|
||||
this->p_prio = epicsThreadPriorityLow;
|
||||
this->p_autostart = true;
|
||||
this->p_runner = NULL;
|
||||
(*this).stack(epicsThreadStackSmall);
|
||||
}
|
||||
|
||||
public:
|
||||
Config();
|
||||
Config(Runnable *r);
|
||||
Config(void(*fn)(void*), void *ptr);
|
||||
Config() {this->x_setdefault();}
|
||||
Config(Runnable *r) {this->x_setdefault();this->run(r);}
|
||||
Config(void(*fn)(void*), void *ptr) {this->x_setdefault();this->run(fn, ptr);}
|
||||
template<typename C>
|
||||
Config(C* inst, void(C::*meth)()) {this->x_setdefault();this->run(inst, meth);}
|
||||
#if __cplusplus>=201103L
|
||||
Config(std::function<void()>&& fn);
|
||||
Config(const std::function<void()>& fn) {this->x_setdefault();this->run(fn);}
|
||||
#endif
|
||||
|
||||
Config& name(const std::string& n);
|
||||
Config& prio(unsigned int p);
|
||||
Config& stack(epicsThreadStackSizeClass s);
|
||||
Config& autostart(bool a);
|
||||
inline Config& name(const std::string& n)
|
||||
{ this->p_strm.str(n); return *this; }
|
||||
inline Config& prio(unsigned int p)
|
||||
{ this->p_prio = p; return *this; }
|
||||
inline Config& stack(epicsThreadStackSizeClass s)
|
||||
{ this->p_stack = epicsThreadGetStackSize(s); return *this; }
|
||||
inline Config& autostart(bool a)
|
||||
{ this->p_autostart = a; return *this; }
|
||||
|
||||
//! Thread will execute Runnable::run()
|
||||
Config& run(Runnable* r);
|
||||
Config& run(Runnable* r)
|
||||
{ this->p_runner = r; return *this; }
|
||||
//! Thread will execute (*fn)(ptr)
|
||||
Config& run(void(*fn)(void*), void *ptr);
|
||||
Config& run(void(*fn)(void*), void *ptr)
|
||||
{
|
||||
this->p_owned_runner.reset(new detail::FuncRunner(fn, ptr));
|
||||
this->p_runner = this->p_owned_runner.get();
|
||||
return *this;
|
||||
}
|
||||
//! Thread will execute (inst->*meth)()
|
||||
template<typename C>
|
||||
Config& run(C* inst, void(C::*meth)())
|
||||
@@ -132,7 +198,12 @@ public:
|
||||
return *this;
|
||||
}
|
||||
#if __cplusplus>=201103L
|
||||
Config& run(std::function<void()>&& fn);
|
||||
Config& run(const std::function<void()>& fn)
|
||||
{
|
||||
this->p_owned_runner.reset(new detail::BindRunner(fn));
|
||||
this->p_runner = this->p_owned_runner.get();
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
//! Append to thread name string. Argument must be understood by std::ostream::operator<<
|
||||
@@ -158,7 +229,14 @@ public:
|
||||
Thread(std::string name,
|
||||
ThreadPriority priority,
|
||||
Runnable *runnable,
|
||||
epicsThreadStackSizeClass stkcls=epicsThreadStackBig);
|
||||
epicsThreadStackSizeClass stkcls=epicsThreadStackSmall)
|
||||
:epicsThread(*runnable,
|
||||
name.c_str(),
|
||||
epicsThreadGetStackSize(stkcls),
|
||||
priority)
|
||||
{
|
||||
this->start();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -178,19 +256,38 @@ public:
|
||||
Thread(Runnable &runnable,
|
||||
std::string name,
|
||||
unsigned int stksize,
|
||||
unsigned int priority=lowestPriority);
|
||||
unsigned int priority=lowestPriority)
|
||||
:epicsThread(runnable,
|
||||
name.c_str(),
|
||||
stksize,
|
||||
priority)
|
||||
{
|
||||
this->start();
|
||||
}
|
||||
|
||||
//! @brief Create a new thread using the given @class Config
|
||||
//! @throws std::logic_error for improper @class Config (ie. missing runner)
|
||||
Thread(Config& c);
|
||||
Thread(Config& c)
|
||||
:epicsThread(c.x_getrunner(), c.p_strm.str().c_str(),
|
||||
c.p_stack, c.p_prio)
|
||||
{
|
||||
#if __cplusplus>=201103L
|
||||
p_owned = std::move(c.p_owned_runner);
|
||||
#else
|
||||
p_owned = c.p_owned_runner;
|
||||
#endif
|
||||
if(c.p_autostart)
|
||||
this->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Thread();
|
||||
~Thread()
|
||||
{
|
||||
this->exitWait();
|
||||
}
|
||||
|
||||
static size_t num_instances;
|
||||
private:
|
||||
Config::p_owned_runner_t p_owned;
|
||||
};
|
||||
|
||||
|
||||
70
src/misc/pv/timeFunction.h
Normal file
70
src/misc/pv/timeFunction.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/* timeFunction.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 TIMEFUNCTION_H
|
||||
#define TIMEFUNCTION_H
|
||||
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class TimeFunctionRequester;
|
||||
class TimeFunction;
|
||||
typedef std::tr1::shared_ptr<TimeFunctionRequester> TimeFunctionRequesterPtr;
|
||||
typedef std::tr1::shared_ptr<TimeFunction> TimeFunctionPtr;
|
||||
|
||||
/**
|
||||
* @brief Class that must be implemented by timeFunction requester.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass TimeFunctionRequester {
|
||||
public:
|
||||
POINTER_DEFINITIONS(TimeFunctionRequester);
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~TimeFunctionRequester(){}
|
||||
/**
|
||||
* function to be timed.
|
||||
* It will get called multiple times.
|
||||
*/
|
||||
virtual void function() = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Class for measuring time it takes to execute a function.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass TimeFunction {
|
||||
public:
|
||||
POINTER_DEFINITIONS(TimeFunction);
|
||||
/**
|
||||
* Constructor
|
||||
* @param requester The class that has a function method.
|
||||
*/
|
||||
TimeFunction(TimeFunctionRequesterPtr const & requester);
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~TimeFunction();
|
||||
/**
|
||||
* Time the function.
|
||||
* @return the time in seconds to execute the function.
|
||||
* Note that the function may be called many times.
|
||||
*/
|
||||
double timeCall();
|
||||
private:
|
||||
TimeFunctionRequesterPtr requester;
|
||||
};
|
||||
|
||||
|
||||
}}
|
||||
#endif /* TIMEFUNCTION_H */
|
||||
@@ -9,8 +9,6 @@
|
||||
#ifndef TIMER_H
|
||||
#define TIMER_H
|
||||
#include <memory>
|
||||
#include <list>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
@@ -57,28 +55,34 @@ public:
|
||||
*/
|
||||
virtual void timerStopped() = 0;
|
||||
private:
|
||||
epicsTime timeToRun;
|
||||
TimerCallbackPtr next;
|
||||
TimeStamp timeToRun;
|
||||
double period;
|
||||
bool onList;
|
||||
friend class Timer;
|
||||
struct IncreasingTime;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Support for delayed or periodic callback execution.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass Timer : private Runnable {
|
||||
class epicsShareClass Timer : public Runnable {
|
||||
public:
|
||||
POINTER_DEFINITIONS(Timer);
|
||||
/** Create a new timer queue
|
||||
/**
|
||||
* Constructor
|
||||
* @param threadName name for the timer thread.
|
||||
* @param priority thread priority
|
||||
*/
|
||||
Timer(std::string threadName, ThreadPriority priority);
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~Timer();
|
||||
//! Prevent new callbacks from being scheduled, and cancel pending callbacks
|
||||
void close();
|
||||
/**
|
||||
* The thread run method. This is called automatically.
|
||||
*/
|
||||
virtual void run();
|
||||
/**
|
||||
* schedule a callback after a delay.
|
||||
* @param timerCallback the timerCallback instance.
|
||||
@@ -100,38 +104,31 @@ public:
|
||||
/**
|
||||
* cancel a callback.
|
||||
* @param timerCallback the timerCallback to cancel.
|
||||
* @returns true if the timer was queued, and now is cancelled
|
||||
*/
|
||||
bool cancel(TimerCallbackPtr const &timerCallback);
|
||||
void cancel(TimerCallbackPtr const &timerCallback);
|
||||
/**
|
||||
* Is the callback scheduled to be called?
|
||||
* @param timerCallback the timerCallback.
|
||||
* @return (false,true) if (not, is) scheduled.
|
||||
*/
|
||||
bool isScheduled(TimerCallbackPtr const &timerCallback) const;
|
||||
bool isScheduled(TimerCallbackPtr const &timerCallback);
|
||||
/**
|
||||
* show the elements in the timer queue.
|
||||
* @param o The output stream for the output
|
||||
*/
|
||||
void dump(std::ostream& o) const;
|
||||
void dump(std::ostream& o);
|
||||
|
||||
private:
|
||||
virtual void run();
|
||||
|
||||
// call with mutex held
|
||||
void addElement(TimerCallbackPtr const &timerCallback);
|
||||
|
||||
typedef std::list<TimerCallbackPtr> queue_t;
|
||||
|
||||
mutable Mutex mutex;
|
||||
queue_t queue;
|
||||
TimerCallbackPtr head;
|
||||
Mutex mutex;
|
||||
Event waitForWork;
|
||||
bool waiting;
|
||||
Event waitForDone;
|
||||
bool alive;
|
||||
Thread thread;
|
||||
};
|
||||
|
||||
epicsShareExtern std::ostream& operator<<(std::ostream& o, const Timer& timer);
|
||||
epicsShareExtern std::ostream& operator<<(std::ostream& o, Timer& timer);
|
||||
|
||||
}}
|
||||
#endif /* TIMER_H */
|
||||
|
||||
@@ -22,29 +22,17 @@ namespace epics { namespace pvData {
|
||||
namespace detail {
|
||||
// parseToPOD wraps the epicsParse*() functions in one name
|
||||
// and throws exceptions
|
||||
epicsShareExtern void parseToPOD(const char*, boolean *out);
|
||||
epicsShareExtern void parseToPOD(const char*, int8 *out);
|
||||
epicsShareExtern void parseToPOD(const char*, uint8 *out);
|
||||
epicsShareExtern void parseToPOD(const char*, int16_t *out);
|
||||
epicsShareExtern void parseToPOD(const char*, uint16_t *out);
|
||||
epicsShareExtern void parseToPOD(const char*, int32_t *out);
|
||||
epicsShareExtern void parseToPOD(const char*, uint32_t *out);
|
||||
epicsShareExtern void parseToPOD(const char*, int64_t *out);
|
||||
epicsShareExtern void parseToPOD(const char*, uint64_t *out);
|
||||
epicsShareExtern void parseToPOD(const char*, float *out);
|
||||
epicsShareExtern void parseToPOD(const char*, double *out);
|
||||
|
||||
static inline void parseToPOD(const std::string& str, boolean *out) { return parseToPOD(str.c_str(), out); }
|
||||
static inline void parseToPOD(const std::string& str, int8 *out) { return parseToPOD(str.c_str(), out); }
|
||||
static inline void parseToPOD(const std::string& str, uint8 *out) { return parseToPOD(str.c_str(), out); }
|
||||
static inline void parseToPOD(const std::string& str, int16_t *out) { return parseToPOD(str.c_str(), out); }
|
||||
static inline void parseToPOD(const std::string& str, uint16_t *out) { return parseToPOD(str.c_str(), out); }
|
||||
static inline void parseToPOD(const std::string& str, int32_t *out) { return parseToPOD(str.c_str(), out); }
|
||||
static inline void parseToPOD(const std::string& str, uint32_t *out) { return parseToPOD(str.c_str(), out); }
|
||||
static inline void parseToPOD(const std::string& str, int64_t *out) { return parseToPOD(str.c_str(), out); }
|
||||
static inline void parseToPOD(const std::string& str, uint64_t *out) { return parseToPOD(str.c_str(), out); }
|
||||
static inline void parseToPOD(const std::string& str, float *out) { return parseToPOD(str.c_str(), out); }
|
||||
static inline void parseToPOD(const std::string& str, double *out) { return parseToPOD(str.c_str(), out); }
|
||||
epicsShareExtern void parseToPOD(const std::string&, boolean *out);
|
||||
epicsShareExtern void parseToPOD(const std::string&, int8 *out);
|
||||
epicsShareExtern void parseToPOD(const std::string&, uint8 *out);
|
||||
epicsShareExtern void parseToPOD(const std::string&, int16_t *out);
|
||||
epicsShareExtern void parseToPOD(const std::string&, uint16_t *out);
|
||||
epicsShareExtern void parseToPOD(const std::string&, int32_t *out);
|
||||
epicsShareExtern void parseToPOD(const std::string&, uint32_t *out);
|
||||
epicsShareExtern void parseToPOD(const std::string&, int64_t *out);
|
||||
epicsShareExtern void parseToPOD(const std::string&, uint64_t *out);
|
||||
epicsShareExtern void parseToPOD(const std::string&, float *out);
|
||||
epicsShareExtern void parseToPOD(const std::string&, double *out);
|
||||
|
||||
/* want to pass POD types by value,
|
||||
* and std::string by const reference
|
||||
@@ -120,28 +108,13 @@ namespace detail {
|
||||
}
|
||||
};
|
||||
|
||||
// parse POD from C string
|
||||
// TO!=const char*
|
||||
template<typename TO>
|
||||
struct cast_helper<TO, const char*,
|
||||
typename meta::_and<
|
||||
typename meta::not_same_type<TO,const char*>,
|
||||
typename meta::not_same_type<TO,std::string>
|
||||
>::type> {
|
||||
static FORCE_INLINE TO op(const char* from) {
|
||||
TO ret;
|
||||
parseToPOD(from, &ret);
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
} // end detail
|
||||
|
||||
/** @brief Casting/converting between supported scalar types.
|
||||
*
|
||||
* Supported types: uint8_t, int8_t, uint16_t, int16_t,
|
||||
* uint32_t, int32_t, uint64_t, int64_t,
|
||||
* float, double, std::string, const char* (only FROM)
|
||||
* float, double, std::string
|
||||
*
|
||||
* As defined in pvType.h
|
||||
*
|
||||
@@ -208,35 +181,6 @@ static FORCE_INLINE
|
||||
typename detail::print_convolute<T>::return_t
|
||||
print_cast(const T& v) { return detail::print_convolute<T>::op(v); }
|
||||
|
||||
struct escape;
|
||||
|
||||
epicsShareFunc
|
||||
std::ostream& operator<<(std::ostream& strm, const escape& Q);
|
||||
|
||||
//! Helper to print a string with escaping
|
||||
//! @code strm<<'"'<<escape(astring)<<'"' @endcode
|
||||
struct epicsShareClass escape
|
||||
{
|
||||
enum style_t {
|
||||
C, // default
|
||||
CSV, // a la RFC4180
|
||||
};
|
||||
private:
|
||||
const std::string& orig;
|
||||
style_t S;
|
||||
public:
|
||||
escape(const std::string& orig) :orig(orig), S(C) {}
|
||||
~escape();
|
||||
//! Change escaping style
|
||||
inline escape& style(style_t s) { S = s; return *this; }
|
||||
//! print to string and return. (alloc and copy)
|
||||
std::string str() const;
|
||||
|
||||
friend
|
||||
epicsShareFunc
|
||||
std::ostream& operator<<(std::ostream& strm, const escape& Q);
|
||||
};
|
||||
|
||||
}} // end namespace
|
||||
|
||||
#endif // PVTYPECAST_H
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <epicsUnitTest.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvUnitTest.h>
|
||||
|
||||
namespace detail {
|
||||
|
||||
testPassx::~testPassx() {
|
||||
if(!alive) return;
|
||||
std::string msg(strm.str());
|
||||
size_t nl = msg.find_first_of('\n');
|
||||
if(nl==msg.npos) {
|
||||
// single-line output
|
||||
if(dotest)
|
||||
testOk(pass, "%s", msg.c_str());
|
||||
else
|
||||
testDiag("%s", msg.c_str());
|
||||
|
||||
} else {
|
||||
// multi-line output
|
||||
std::istringstream lines(msg);
|
||||
std::string line;
|
||||
bool first = true;
|
||||
while(std::getline(lines ,line)) {
|
||||
if(dotest && first) {
|
||||
first = false;
|
||||
testOk(pass, "%s", line.c_str());
|
||||
} else {
|
||||
testDiag("%s", line.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testPassx::testPassx(testPassx& o)
|
||||
:strm(o.strm.str())
|
||||
,dotest(o.dotest)
|
||||
,pass(o.pass)
|
||||
,alive(o.alive)
|
||||
{
|
||||
strm.seekp(0, std::ios_base::end);
|
||||
o.alive = false;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
@@ -1,299 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <epicsString.h>
|
||||
#include <epicsGuard.h>
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsEvent.h>
|
||||
#include <epicsThread.h>
|
||||
#include <epicsTime.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvdVersion.h>
|
||||
#include <pv/sharedPtr.h>
|
||||
#include "pv/reftrack.h"
|
||||
|
||||
namespace {
|
||||
|
||||
#ifndef REFTRACK_USE_ATOMIC
|
||||
static inline
|
||||
size_t readref(const size_t *ref)
|
||||
{
|
||||
volatile const size_t *vref = ref;
|
||||
return *vref;
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef epicsGuard<epicsMutex> Guard;
|
||||
typedef epicsGuardRelease<epicsMutex> UnGuard;
|
||||
|
||||
struct refgbl_t {
|
||||
epicsMutex lock;
|
||||
typedef std::map<std::string, const size_t*> counters_t;
|
||||
counters_t counters;
|
||||
} *refgbl;
|
||||
|
||||
void refgbl_init(void *)
|
||||
{
|
||||
try {
|
||||
refgbl = new refgbl_t;
|
||||
} catch(std::exception& e) {
|
||||
std::cerr<<"Failed to initialize global ref. counter registry :"<<e.what()<<"\n";
|
||||
}
|
||||
}
|
||||
|
||||
epicsThreadOnceId refgbl_once = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
void refgbl_setup()
|
||||
{
|
||||
epicsThreadOnce(&refgbl_once, &refgbl_init, 0);
|
||||
if(!refgbl)
|
||||
throw std::runtime_error("Failed to initialize global ref. counter registry");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace epics {
|
||||
|
||||
void registerRefCounter(const char *name, const size_t* counter)
|
||||
{
|
||||
refgbl_setup();
|
||||
Guard G(refgbl->lock);
|
||||
refgbl->counters[name] = counter;
|
||||
}
|
||||
|
||||
void unregisterRefCounter(const char *name, const size_t* counter)
|
||||
{
|
||||
refgbl_setup();
|
||||
Guard G(refgbl->lock);
|
||||
refgbl_t::counters_t::iterator it(refgbl->counters.find(name));
|
||||
if(it!=refgbl->counters.end() && it->second==counter)
|
||||
refgbl->counters.erase(it);
|
||||
}
|
||||
|
||||
size_t readRefCounter(const char *name)
|
||||
{
|
||||
refgbl_setup();
|
||||
Guard G(refgbl->lock);
|
||||
refgbl_t::counters_t::iterator it(refgbl->counters.find(name));
|
||||
if(it==refgbl->counters.end())
|
||||
return 0;
|
||||
#ifdef REFTRACK_USE_ATOMIC
|
||||
return atomic::get(*it->second);
|
||||
#else
|
||||
return readref(it->second);
|
||||
#endif
|
||||
}
|
||||
|
||||
const RefSnapshot::Count&
|
||||
RefSnapshot::operator[](const std::string& name) const
|
||||
{
|
||||
static const Count zero;
|
||||
|
||||
cnt_map_t::const_iterator it(counts.find(name));
|
||||
return it==counts.end() ? zero : it->second;
|
||||
}
|
||||
|
||||
void RefSnapshot::update()
|
||||
{
|
||||
refgbl_t::counters_t counters;
|
||||
{
|
||||
refgbl_setup();
|
||||
Guard G(refgbl->lock);
|
||||
counters = refgbl->counters; // copy
|
||||
}
|
||||
|
||||
counts.clear();
|
||||
|
||||
for(refgbl_t::counters_t::const_iterator it=counters.begin(),
|
||||
end=counters.end();
|
||||
it!=end; ++it)
|
||||
{
|
||||
#ifdef REFTRACK_USE_ATOMIC
|
||||
size_t cnt = atomic::get(*it->second);
|
||||
#else
|
||||
size_t cnt = readref(it->second);
|
||||
#endif
|
||||
|
||||
counts[it->first] = Count(cnt, 0);
|
||||
}
|
||||
}
|
||||
|
||||
RefSnapshot RefSnapshot::operator-(const RefSnapshot& rhs) const
|
||||
{
|
||||
RefSnapshot ret;
|
||||
|
||||
RefSnapshot::cnt_map_t::const_iterator lit = counts.begin(),
|
||||
lend= counts.end(),
|
||||
rit = rhs.counts.begin(),
|
||||
rend= rhs.counts.end();
|
||||
|
||||
while(lit!=lend || rit!=rend)
|
||||
{
|
||||
if(lit==lend || (rit!=rend && lit->first > rit->first)) {
|
||||
ret.counts[rit->first] = RefSnapshot::Count(0, -long(rit->second.current));
|
||||
++rit;
|
||||
|
||||
} else if(rit==rend || lit->first < rit->first) {
|
||||
ret.counts[lit->first] = RefSnapshot::Count(lit->second.current, long(lit->second.current));
|
||||
++lit;
|
||||
|
||||
} else { // !end and lit->first == rit->first
|
||||
ret.counts[lit->first] = RefSnapshot::Count(lit->second.current,
|
||||
long(lit->second.current) - long(rit->second.current));
|
||||
++lit;
|
||||
++rit;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& strm, const RefSnapshot& snap)
|
||||
{
|
||||
for(RefSnapshot::const_iterator it = snap.begin(), end = snap.end(); it!=end; ++it)
|
||||
{
|
||||
if(it->second.delta==0) continue;
|
||||
strm<<it->first<<":\t"<<it->second.current<<" (delta "<<it->second.delta<<")\n";
|
||||
}
|
||||
return strm;
|
||||
}
|
||||
|
||||
struct RefMonitor::Impl : public epicsThreadRunable
|
||||
{
|
||||
RefMonitor& owner;
|
||||
epics::auto_ptr<epicsThread> worker;
|
||||
epicsMutex lock;
|
||||
epicsEvent wakeup;
|
||||
RefSnapshot prev;
|
||||
bool done;
|
||||
double period;
|
||||
Impl(RefMonitor* owner) :owner(*owner), done(false), period(10.0) {}
|
||||
virtual ~Impl() {}
|
||||
|
||||
virtual void run()
|
||||
{
|
||||
Guard G(lock);
|
||||
while(!done) {
|
||||
RefSnapshot current, P;
|
||||
P = prev; // copy
|
||||
{
|
||||
UnGuard U(G);
|
||||
|
||||
current.update();
|
||||
|
||||
owner.show(current-P);
|
||||
}
|
||||
|
||||
prev.swap(current);
|
||||
|
||||
{
|
||||
UnGuard U(G);
|
||||
wakeup.wait(period);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
RefMonitor::RefMonitor()
|
||||
:impl(new Impl(this))
|
||||
{}
|
||||
RefMonitor::~RefMonitor()
|
||||
{
|
||||
stop();
|
||||
delete impl;
|
||||
}
|
||||
|
||||
void RefMonitor::start(double period)
|
||||
{
|
||||
Guard G(impl->lock);
|
||||
if(impl->worker.get()) return;
|
||||
|
||||
impl->done = false;
|
||||
impl->period = period;
|
||||
impl->worker.reset(new epicsThread(*impl,
|
||||
"RefMonitor",
|
||||
epicsThreadGetStackSize(epicsThreadStackBig),
|
||||
epicsThreadPriorityMin));
|
||||
impl->worker->start();
|
||||
}
|
||||
|
||||
void RefMonitor::stop()
|
||||
{
|
||||
epics::auto_ptr<epicsThread> W;
|
||||
{
|
||||
Guard G(impl->lock);
|
||||
if(!impl->worker.get()) return;
|
||||
epics::swap(W, impl->worker);
|
||||
impl->done = true;
|
||||
}
|
||||
|
||||
impl->wakeup.signal();
|
||||
W->exitWait();
|
||||
|
||||
W.reset();
|
||||
}
|
||||
|
||||
bool RefMonitor::running() const
|
||||
{
|
||||
Guard G(impl->lock);
|
||||
return !!impl->worker.get();
|
||||
}
|
||||
|
||||
void RefMonitor::current()
|
||||
{
|
||||
RefSnapshot current, P;
|
||||
current.update();
|
||||
|
||||
{
|
||||
Guard G(impl->lock);
|
||||
P = impl->prev; // copy
|
||||
}
|
||||
|
||||
show(current-P, true);
|
||||
}
|
||||
|
||||
void RefMonitor::show(const RefSnapshot &snap, bool complete)
|
||||
{
|
||||
char buf[80];
|
||||
epicsTime::getCurrent().strftime(buf, sizeof(buf), "%a %b %d %Y %H:%M:%S.%f");
|
||||
buf[sizeof(buf)-1] = '\0';
|
||||
std::cerr<<buf<<" : References\n";
|
||||
|
||||
for(RefSnapshot::const_iterator it = snap.begin(), end = snap.end(); it!=end; ++it)
|
||||
{
|
||||
// print if delta!=0 or (complete && current!=0)
|
||||
if(it->second.delta==0 && (!complete || it->second.current==0)) continue;
|
||||
std::cerr<<it->first<<":\t"<<it->second.current<<" (delta "<<it->second.delta<<")\n";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace epics
|
||||
|
||||
|
||||
char* epicsRefSnapshotCurrent()
|
||||
{
|
||||
try {
|
||||
epics::RefSnapshot snap;
|
||||
snap.update();
|
||||
std::ostringstream strm;
|
||||
strm<<snap;
|
||||
const char *str = strm.str().c_str();
|
||||
char *ret = (char*)malloc(strlen(str)+1);
|
||||
if(ret)
|
||||
strcpy(ret, str);
|
||||
return ret;
|
||||
}catch(std::exception& e){
|
||||
return epicsStrDup(e.what());
|
||||
}
|
||||
}
|
||||
44
src/misc/requester.cpp
Normal file
44
src/misc/requester.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
/* requester.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
#include <epicsMutex.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/lock.h>
|
||||
#include <pv/requester.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
static StringArray messageTypeName(MESSAGE_TYPE_COUNT);
|
||||
|
||||
string getMessageTypeName(MessageType messageType)
|
||||
{
|
||||
// TODO not thread-safe
|
||||
static Mutex mutex;
|
||||
Lock xx(mutex);
|
||||
if(messageTypeName[0].size()==0) {
|
||||
messageTypeName[0] = "info";
|
||||
messageTypeName[1] = "warning";
|
||||
messageTypeName[2] = "error";
|
||||
messageTypeName[3] = "fatalError";
|
||||
}
|
||||
return messageTypeName[messageType];
|
||||
}
|
||||
|
||||
void Requester::message(std::string const & message,MessageType messageType)
|
||||
{
|
||||
std::cerr << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")\n";
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -98,6 +98,8 @@ namespace epics {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static string emptyStringtring;
|
||||
|
||||
string SerializeHelper::deserializeString(ByteBuffer* buffer,
|
||||
DeserializableControl* control) {
|
||||
@@ -109,7 +111,7 @@ namespace epics {
|
||||
{
|
||||
// entire string is in buffer, simply create a string out of it (copy)
|
||||
std::size_t pos = buffer->getPosition();
|
||||
string str(buffer->getBuffer()+pos, size);
|
||||
string str(buffer->getArray()+pos, size);
|
||||
buffer->setPosition(pos+size);
|
||||
return str;
|
||||
}
|
||||
@@ -122,7 +124,7 @@ namespace epics {
|
||||
while(true) {
|
||||
std::size_t toRead = min(size-i, buffer->getRemaining());
|
||||
std::size_t pos = buffer->getPosition();
|
||||
str.append(buffer->getBuffer()+pos, toRead);
|
||||
str.append(buffer->getArray()+pos, toRead);
|
||||
buffer->setPosition(pos+toRead);
|
||||
i += toRead;
|
||||
if(i<size)
|
||||
@@ -137,7 +139,7 @@ namespace epics {
|
||||
}
|
||||
}
|
||||
else
|
||||
return std::string();
|
||||
return emptyStringtring;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,11 +19,20 @@ const char* Status::StatusTypeName[] = { "OK", "WARNING", "ERROR", "FATAL" };
|
||||
|
||||
Status Status::Ok;
|
||||
|
||||
//PVDATA_REFCOUNT_MONITOR_DEFINE(status);
|
||||
|
||||
Status::Status() :
|
||||
m_statusType(STATUSTYPE_OK)
|
||||
{
|
||||
}
|
||||
|
||||
Status::Status(StatusType type, string const & message) :
|
||||
m_statusType(type), m_message(message)
|
||||
{
|
||||
if (type == STATUSTYPE_OK)
|
||||
throw std::invalid_argument("type == STATUSTYPE_OK");
|
||||
|
||||
//PVDATA_REFCOUNT_MONITOR_CONSTRUCT(status);
|
||||
}
|
||||
|
||||
Status::Status(StatusType type, string const & message, string const & stackDump) :
|
||||
@@ -31,15 +40,38 @@ Status::Status(StatusType type, string const & message, string const & stackDump
|
||||
{
|
||||
if (type == STATUSTYPE_OK)
|
||||
throw std::invalid_argument("type == STATUSTYPE_OK");
|
||||
|
||||
//PVDATA_REFCOUNT_MONITOR_CONSTRUCT(status);
|
||||
}
|
||||
|
||||
void Status::maximize(const Status& o)
|
||||
Status::~Status() {
|
||||
//PVDATA_REFCOUNT_MONITOR_DESTRUCT(status);
|
||||
}
|
||||
|
||||
Status::StatusType Status::getType() const
|
||||
{
|
||||
if(m_statusType < o.m_statusType) {
|
||||
m_statusType = o.m_statusType;
|
||||
m_message = o.m_message;
|
||||
m_stackDump = o.m_stackDump;
|
||||
}
|
||||
return m_statusType;
|
||||
}
|
||||
|
||||
|
||||
string Status::getMessage() const
|
||||
{
|
||||
return m_message;
|
||||
}
|
||||
|
||||
string Status::getStackDump() const
|
||||
{
|
||||
return m_stackDump;
|
||||
}
|
||||
|
||||
bool Status::isOK() const
|
||||
{
|
||||
return (m_statusType == STATUSTYPE_OK);
|
||||
}
|
||||
|
||||
bool Status::isSuccess() const
|
||||
{
|
||||
return (m_statusType == STATUSTYPE_OK || m_statusType == STATUSTYPE_WARNING);
|
||||
}
|
||||
|
||||
void Status::serialize(ByteBuffer *buffer, SerializableControl *flusher) const
|
||||
@@ -88,6 +120,18 @@ void Status::dump(std::ostream& o) const
|
||||
if (!m_stackDump.empty())
|
||||
o << ", stackDump=" << std::endl << m_stackDump;
|
||||
o << ']';
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& o, const Status& status)
|
||||
{
|
||||
status.dump(o);
|
||||
return o;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& o, const Status::StatusType& statusType)
|
||||
{
|
||||
o << Status::StatusTypeName[statusType];
|
||||
return o;
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
@@ -1,147 +0,0 @@
|
||||
/* thread.h */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <epicsThread.h>
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/thread.h>
|
||||
#include <pv/reftrack.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
namespace detail {
|
||||
struct FuncRunner : public epicsThreadRunable
|
||||
{
|
||||
typedef void (*fn_t)(void*);
|
||||
fn_t fn;
|
||||
void *arg;
|
||||
FuncRunner(fn_t f, void *a) :fn(f), arg(a) {}
|
||||
virtual ~FuncRunner(){}
|
||||
virtual void run()
|
||||
{
|
||||
(*fn)(arg);
|
||||
}
|
||||
};
|
||||
#if __cplusplus>=201103L
|
||||
struct BindRunner : public epicsThreadRunable
|
||||
{
|
||||
typedef std::function<void()> fn_t;
|
||||
fn_t fn;
|
||||
BindRunner(fn_t&& f) : fn(std::move(f)) {}
|
||||
virtual ~BindRunner() {}
|
||||
virtual void run()
|
||||
{
|
||||
fn();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
} // detail
|
||||
|
||||
|
||||
Runnable& Thread::Config::x_getrunner()
|
||||
{
|
||||
if(!this->p_runner)
|
||||
throw std::logic_error("Thread::Config missing run()");
|
||||
return *this->p_runner;
|
||||
}
|
||||
|
||||
void Thread::Config::x_setdefault()
|
||||
{
|
||||
this->p_prio = epicsThreadPriorityLow;
|
||||
this->p_autostart = true;
|
||||
this->p_runner = NULL;
|
||||
(*this).stack(epicsThreadStackBig);
|
||||
}
|
||||
|
||||
size_t Thread::num_instances;
|
||||
|
||||
Thread::Config::Config() {this->x_setdefault();}
|
||||
|
||||
Thread::Config::Config(Runnable *r) {this->x_setdefault();this->run(r);}
|
||||
|
||||
Thread::Config::Config(void(*fn)(void*), void *ptr) {this->x_setdefault();this->run(fn, ptr);}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
Thread::Config::Config(std::function<void()>&& fn) {this->x_setdefault();this->run(std::move(fn));}
|
||||
#endif
|
||||
|
||||
Thread::Config& Thread::Config::name(const std::string& n)
|
||||
{ this->p_strm.str(n); return *this; }
|
||||
|
||||
Thread::Config& Thread::Config::prio(unsigned int p)
|
||||
{ this->p_prio = p; return *this; }
|
||||
|
||||
Thread::Config& Thread::Config::stack(epicsThreadStackSizeClass s)
|
||||
{ this->p_stack = epicsThreadGetStackSize(s); return *this; }
|
||||
|
||||
Thread::Config& Thread::Config::autostart(bool a)
|
||||
{ this->p_autostart = a; return *this; }
|
||||
|
||||
Thread::Config& Thread::Config::run(Runnable* r)
|
||||
{ this->p_runner = r; return *this; }
|
||||
|
||||
Thread::Config& Thread::Config::run(void(*fn)(void*), void *ptr)
|
||||
{
|
||||
this->p_owned_runner.reset(new detail::FuncRunner(fn, ptr));
|
||||
this->p_runner = this->p_owned_runner.get();
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
Thread::Config& Thread::Config::run(std::function<void()> &&fn)
|
||||
{
|
||||
this->p_owned_runner.reset(new detail::BindRunner(std::move(fn)));
|
||||
this->p_runner = this->p_owned_runner.get();
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
Thread::Thread(std::string name,
|
||||
ThreadPriority priority,
|
||||
Runnable *runnable,
|
||||
epicsThreadStackSizeClass stkcls)
|
||||
:epicsThread(*runnable,
|
||||
name.c_str(),
|
||||
epicsThreadGetStackSize(stkcls),
|
||||
priority)
|
||||
{
|
||||
REFTRACE_INCREMENT(num_instances);
|
||||
this->start();
|
||||
}
|
||||
|
||||
Thread::Thread(Runnable &runnable,
|
||||
std::string name,
|
||||
unsigned int stksize,
|
||||
unsigned int priority)
|
||||
:epicsThread(runnable,
|
||||
name.c_str(),
|
||||
stksize,
|
||||
priority)
|
||||
{
|
||||
REFTRACE_INCREMENT(num_instances);
|
||||
this->start();
|
||||
}
|
||||
|
||||
Thread::Thread(Config& c)
|
||||
:epicsThread(c.x_getrunner(), c.p_strm.str().c_str(),
|
||||
c.p_stack, c.p_prio)
|
||||
{
|
||||
REFTRACE_INCREMENT(num_instances);
|
||||
#if __cplusplus>=201103L
|
||||
p_owned = std::move(c.p_owned_runner);
|
||||
#else
|
||||
p_owned = c.p_owned_runner;
|
||||
#endif
|
||||
if(c.p_autostart)
|
||||
this->start();
|
||||
}
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
this->exitWait();
|
||||
REFTRACE_DECREMENT(num_instances);
|
||||
}
|
||||
|
||||
}} // epics::pvData
|
||||
51
src/misc/timeFunction.cpp
Normal file
51
src/misc/timeFunction.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
/* timeFunction.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
|
||||
#include <epicsTime.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/timeStamp.h>
|
||||
#include <pv/timeFunction.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
TimeFunction::TimeFunction(TimeFunctionRequesterPtr const &requester)
|
||||
: requester(requester) {}
|
||||
|
||||
|
||||
TimeFunction::~TimeFunction() {}
|
||||
|
||||
double TimeFunction::timeCall()
|
||||
{
|
||||
TimeStamp startTime;
|
||||
TimeStamp endTime;
|
||||
double perCall = 0.0;
|
||||
long ntimes = 1;
|
||||
while(true) {
|
||||
startTime.getCurrent();
|
||||
for(long i=0; i<ntimes; i++) requester->function();
|
||||
endTime.getCurrent();
|
||||
double diff = TimeStamp::diff(endTime,startTime);
|
||||
if(diff>=1.0) {
|
||||
perCall = diff/(double)ntimes;
|
||||
break;
|
||||
}
|
||||
ntimes *= 2;
|
||||
}
|
||||
return perCall;
|
||||
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -6,13 +6,16 @@
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include <epicsThread.h>
|
||||
#include <epicsGuard.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/timer.h>
|
||||
@@ -28,52 +31,67 @@ TimerCallback::TimerCallback()
|
||||
}
|
||||
|
||||
Timer::Timer(string threadName,ThreadPriority priority)
|
||||
:waitForWork(false)
|
||||
,waiting(false)
|
||||
,alive(true)
|
||||
,thread(threadName,priority,this)
|
||||
: waitForWork(false),
|
||||
waitForDone(false),
|
||||
alive(true),
|
||||
thread(threadName,priority,this)
|
||||
{}
|
||||
|
||||
struct TimerCallback::IncreasingTime {
|
||||
bool operator()(const TimerCallbackPtr& lhs, const TimerCallbackPtr& rhs) {
|
||||
assert(lhs && rhs);
|
||||
return lhs->timeToRun < rhs->timeToRun;
|
||||
}
|
||||
};
|
||||
|
||||
// call with mutex held
|
||||
void Timer::addElement(TimerCallbackPtr const & timerCallback)
|
||||
{
|
||||
assert(!timerCallback->onList);
|
||||
|
||||
queue_t temp;
|
||||
temp.push_back(timerCallback);
|
||||
|
||||
timerCallback->onList = true;
|
||||
|
||||
// merge sorted lists.
|
||||
// for us effectively insertion sort.
|
||||
queue.merge(temp, TimerCallback::IncreasingTime());
|
||||
if(head.get()==NULL) {
|
||||
head = timerCallback;
|
||||
timerCallback->next.reset();
|
||||
return;
|
||||
}
|
||||
TimerCallbackPtr nextNode(head);
|
||||
TimerCallbackPtr prevNode;
|
||||
while(true) {
|
||||
if(timerCallback->timeToRun < nextNode->timeToRun) {
|
||||
if(prevNode.get()!=NULL) {
|
||||
prevNode->next = timerCallback;
|
||||
} else {
|
||||
head = timerCallback;
|
||||
}
|
||||
timerCallback->next = nextNode;
|
||||
return;
|
||||
}
|
||||
if(nextNode->next.get()==NULL) {
|
||||
nextNode->next = timerCallback;
|
||||
timerCallback->next.reset();
|
||||
return;
|
||||
}
|
||||
prevNode = nextNode;
|
||||
nextNode = nextNode->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Timer::cancel(TimerCallbackPtr const &timerCallback)
|
||||
void Timer::cancel(TimerCallbackPtr const &timerCallback)
|
||||
{
|
||||
Lock xx(mutex);
|
||||
if(!timerCallback->onList) return false;
|
||||
for(queue_t::iterator it(queue.begin()), end(queue.end()); it != end; ++it)
|
||||
{
|
||||
TimerCallbackPtr& cur = *it;
|
||||
if(cur.get() == timerCallback.get()) {
|
||||
cur->onList = false;
|
||||
queue.erase(it); // invalidates cur and it
|
||||
return true;
|
||||
if(!timerCallback->onList) return;
|
||||
TimerCallbackPtr nextNode(head);
|
||||
TimerCallbackPtr prevNode;
|
||||
while(true) {
|
||||
if(nextNode.get()==timerCallback.get()) {
|
||||
if(prevNode.get()!=NULL) {
|
||||
prevNode->next = timerCallback->next;
|
||||
} else {
|
||||
head = timerCallback->next;
|
||||
}
|
||||
timerCallback->next.reset();
|
||||
timerCallback->onList = false;
|
||||
return;
|
||||
}
|
||||
prevNode = nextNode;
|
||||
nextNode = nextNode->next;
|
||||
}
|
||||
throw std::logic_error("Timer::cancel() onList==true, but not found");
|
||||
throw std::logic_error(string(""));
|
||||
}
|
||||
|
||||
bool Timer::isScheduled(TimerCallbackPtr const &timerCallback) const
|
||||
bool Timer::isScheduled(TimerCallbackPtr const &timerCallback)
|
||||
{
|
||||
Lock xx(mutex);
|
||||
return timerCallback->onList;
|
||||
@@ -82,75 +100,63 @@ bool Timer::isScheduled(TimerCallbackPtr const &timerCallback) const
|
||||
|
||||
void Timer::run()
|
||||
{
|
||||
epicsGuard<epicsMutex> G(mutex);
|
||||
|
||||
epicsTime now(epicsTime::getCurrent());
|
||||
|
||||
while(alive) {
|
||||
double waitfor;
|
||||
|
||||
if(queue.empty()) {
|
||||
// no jobs, just go to sleep
|
||||
waiting = true;
|
||||
epicsGuardRelease<epicsMutex> U(G);
|
||||
|
||||
TimeStamp currentTime;
|
||||
while(true) {
|
||||
double period = 0.0;
|
||||
TimerCallbackPtr nodeToCall;
|
||||
{
|
||||
Lock xx(mutex);
|
||||
currentTime.getCurrent();
|
||||
if (!alive) break;
|
||||
TimerCallbackPtr timerCallback = head;
|
||||
if(timerCallback.get()!=NULL) {
|
||||
double diff = TimeStamp::diff(
|
||||
timerCallback->timeToRun,currentTime);
|
||||
if(diff<=0.0) {
|
||||
nodeToCall = timerCallback;
|
||||
nodeToCall->onList = false;
|
||||
head = head->next;
|
||||
period = timerCallback->period;
|
||||
if(period>0.0) {
|
||||
timerCallback->timeToRun += period;
|
||||
addElement(timerCallback);
|
||||
}
|
||||
timerCallback = head;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(nodeToCall.get()!=NULL) {
|
||||
nodeToCall->callback();
|
||||
}
|
||||
{
|
||||
Lock xx(mutex);
|
||||
if(!alive) break;
|
||||
}
|
||||
if(head.get()==NULL) {
|
||||
waitForWork.wait();
|
||||
now = epicsTime::getCurrent();
|
||||
|
||||
} else if((waitfor = queue.front()->timeToRun - now) <= 0) {
|
||||
// execute first expired job
|
||||
|
||||
TimerCallbackPtr work;
|
||||
work.swap(queue.front());
|
||||
work->onList = false;
|
||||
queue.pop_front();
|
||||
|
||||
{
|
||||
epicsGuardRelease<epicsMutex> U(G);
|
||||
|
||||
work->callback();
|
||||
}
|
||||
|
||||
if(work->period > 0.0) {
|
||||
work->timeToRun += work->period;
|
||||
addElement(work);
|
||||
}
|
||||
|
||||
// don't update 'now' until all expired jobs run
|
||||
|
||||
} else {
|
||||
waiting = true;
|
||||
// wait for first un-expired
|
||||
epicsGuardRelease<epicsMutex> U(G);
|
||||
|
||||
waitForWork.wait(waitfor);
|
||||
now = epicsTime::getCurrent();
|
||||
}
|
||||
waiting = false;
|
||||
}
|
||||
} else {
|
||||
double delay = TimeStamp::diff(head->timeToRun,currentTime);
|
||||
waitForWork.wait(delay);
|
||||
}
|
||||
}
|
||||
waitForDone.signal();
|
||||
}
|
||||
|
||||
Timer::~Timer() {
|
||||
close();
|
||||
}
|
||||
|
||||
void Timer::close() {
|
||||
{
|
||||
Lock xx(mutex);
|
||||
if(!alive)
|
||||
return; // already closed
|
||||
alive = false;
|
||||
}
|
||||
waitForWork.signal();
|
||||
thread.exitWait();
|
||||
|
||||
queue_t temp;
|
||||
temp.swap(queue);
|
||||
|
||||
for(;!temp.empty(); temp.pop_front()) {
|
||||
TimerCallbackPtr& head = temp.front();
|
||||
head->onList = false;
|
||||
waitForDone.wait();
|
||||
TimerCallbackPtr timerCallback;
|
||||
while(true) {
|
||||
timerCallback = head;
|
||||
if(head.get()==NULL) break;
|
||||
head->timerStopped();
|
||||
head = timerCallback->next;
|
||||
timerCallback->next.reset();
|
||||
timerCallback->onList = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,44 +172,49 @@ void Timer::schedulePeriodic(
|
||||
double delay,
|
||||
double period)
|
||||
{
|
||||
epicsTime now(epicsTime::getCurrent());
|
||||
|
||||
bool wakeup;
|
||||
if(isScheduled(timerCallback)) {
|
||||
throw std::logic_error(string("already queued"));
|
||||
}
|
||||
{
|
||||
Lock xx(mutex);
|
||||
if(timerCallback->onList) {
|
||||
throw std::logic_error(string("already queued"));
|
||||
}
|
||||
|
||||
if(!alive) {
|
||||
xx.unlock();
|
||||
timerCallback->timerStopped();
|
||||
return;
|
||||
}
|
||||
|
||||
timerCallback->timeToRun = now + delay;
|
||||
timerCallback->period = period;
|
||||
|
||||
addElement(timerCallback);
|
||||
wakeup = waiting && queue.front()==timerCallback;
|
||||
}
|
||||
if(wakeup) waitForWork.signal();
|
||||
TimeStamp timeStamp;
|
||||
timeStamp.getCurrent();
|
||||
timeStamp += delay;
|
||||
timerCallback->timeToRun.getCurrent();
|
||||
timerCallback->timeToRun += delay;
|
||||
timerCallback->period = period;
|
||||
bool isFirst = false;
|
||||
{
|
||||
Lock xx(mutex);
|
||||
addElement(timerCallback);
|
||||
if(timerCallback.get()==head.get()) isFirst = true;
|
||||
}
|
||||
if(isFirst) waitForWork.signal();
|
||||
}
|
||||
|
||||
void Timer::dump(std::ostream& o) const
|
||||
void Timer::dump(std::ostream& o)
|
||||
{
|
||||
Lock xx(mutex);
|
||||
if(!alive) return;
|
||||
epicsTime now(epicsTime::getCurrent());
|
||||
|
||||
for(queue_t::const_iterator it(queue.begin()), end(queue.end()); it!=end; ++it) {
|
||||
const TimerCallbackPtr& nodeToCall = *it;
|
||||
o << "timeToRun " << (nodeToCall->timeToRun - now)
|
||||
<< " period " << nodeToCall->period << "\n";
|
||||
}
|
||||
TimeStamp currentTime;
|
||||
TimerCallbackPtr nodeToCall(head);
|
||||
currentTime.getCurrent();
|
||||
while(true) {
|
||||
if(nodeToCall.get()==NULL) return;
|
||||
TimeStamp timeToRun = nodeToCall->timeToRun;
|
||||
double period = nodeToCall->period;
|
||||
double diff = TimeStamp::diff(timeToRun,currentTime);
|
||||
o << "timeToRun " << diff << " period " << period << std::endl;
|
||||
nodeToCall = nodeToCall->next;
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& o, const Timer& timer)
|
||||
std::ostream& operator<<(std::ostream& o, Timer& timer)
|
||||
{
|
||||
timer.dump(o);
|
||||
return o;
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <epicsConvert.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
@@ -20,7 +18,7 @@ using std::string;
|
||||
|
||||
namespace {
|
||||
|
||||
static void noconvert()
|
||||
static void noconvert(size_t, void*, const void*)
|
||||
{
|
||||
throw std::runtime_error("castUnsafeV: Conversion not supported");
|
||||
}
|
||||
@@ -33,10 +31,12 @@ static void castVTyped(size_t count, void *draw, const void *sraw)
|
||||
|
||||
//std::transform(src, src+count, dest, castUnsafe<TO,FROM>);
|
||||
|
||||
const FROM *last = src+count;
|
||||
try {
|
||||
for(size_t i=0; i<count; i++) {
|
||||
dest[i] = castUnsafe<TO,FROM>(src[i]);
|
||||
}
|
||||
while (src != last) {
|
||||
*dest = castUnsafe<TO,FROM>(*src);
|
||||
++dest; ++src;
|
||||
}
|
||||
} catch (std::exception& ex) {
|
||||
// do not report index for scalars (or arrays with one element)
|
||||
if (count > 1)
|
||||
@@ -55,15 +55,186 @@ template<typename T>
|
||||
static void copyV(size_t count, void *draw, const void *sraw)
|
||||
{
|
||||
T *dest=(T*)draw;
|
||||
const T *src=(const T*)sraw;
|
||||
const T *src=(T*)sraw;
|
||||
std::copy(src, src+count, dest);
|
||||
}
|
||||
|
||||
template<int N>
|
||||
static void copyMem(size_t count, void *draw, const void *sraw)
|
||||
typedef void (*convertfn)(size_t, void*, const void*);
|
||||
|
||||
/* lookup table of converter functions.
|
||||
* first dimension is TO, second is FROM
|
||||
*/
|
||||
static convertfn converters[pvString+1][pvString+1] =
|
||||
{
|
||||
memcpy(draw, sraw, count*N);
|
||||
}
|
||||
// to pvBoolean
|
||||
{ ©V<epics::pvData::boolean>,
|
||||
&noconvert,
|
||||
&noconvert,
|
||||
&noconvert,
|
||||
&noconvert,
|
||||
&noconvert,
|
||||
&noconvert,
|
||||
&noconvert,
|
||||
&noconvert,
|
||||
&noconvert,
|
||||
&noconvert,
|
||||
&castVTyped<epics::pvData::boolean, string>,
|
||||
},
|
||||
// to pvByte
|
||||
{&noconvert,
|
||||
©V<int8_t>,
|
||||
&castVTyped<int8_t, int16_t>,
|
||||
&castVTyped<int8_t, int32_t>,
|
||||
&castVTyped<int8_t, int64_t>,
|
||||
&castVTyped<int8_t, uint8_t>,
|
||||
&castVTyped<int8_t, uint16_t>,
|
||||
&castVTyped<int8_t, uint32_t>,
|
||||
&castVTyped<int8_t, uint64_t>,
|
||||
&castVTyped<int8_t, float>,
|
||||
&castVTyped<int8_t, double>,
|
||||
&castVTyped<int8_t, string>,
|
||||
},
|
||||
// to pvShort
|
||||
{&noconvert,
|
||||
&castVTyped<int16_t, int8_t>,
|
||||
©V<int16_t>,
|
||||
&castVTyped<int16_t, int32_t>,
|
||||
&castVTyped<int16_t, int64_t>,
|
||||
&castVTyped<int16_t, uint8_t>,
|
||||
&castVTyped<int16_t, uint16_t>,
|
||||
&castVTyped<int16_t, uint32_t>,
|
||||
&castVTyped<int16_t, uint64_t>,
|
||||
&castVTyped<int16_t, float>,
|
||||
&castVTyped<int16_t, double>,
|
||||
&castVTyped<int16_t, string>,
|
||||
},
|
||||
// to pvInt
|
||||
{&noconvert,
|
||||
&castVTyped<int32_t, int8_t>,
|
||||
&castVTyped<int32_t, int16_t>,
|
||||
©V<int32_t>,
|
||||
&castVTyped<int32_t, int64_t>,
|
||||
&castVTyped<int32_t, uint8_t>,
|
||||
&castVTyped<int32_t, uint16_t>,
|
||||
&castVTyped<int32_t, uint32_t>,
|
||||
&castVTyped<int32_t, uint64_t>,
|
||||
&castVTyped<int32_t, float>,
|
||||
&castVTyped<int32_t, double>,
|
||||
&castVTyped<int32_t, string>,
|
||||
},
|
||||
// to pvLong
|
||||
{&noconvert,
|
||||
&castVTyped<int64_t, int8_t>,
|
||||
&castVTyped<int64_t, int16_t>,
|
||||
&castVTyped<int64_t, int32_t>,
|
||||
©V<int64_t>,
|
||||
&castVTyped<int64_t, uint8_t>,
|
||||
&castVTyped<int64_t, uint16_t>,
|
||||
&castVTyped<int64_t, uint32_t>,
|
||||
&castVTyped<int64_t, uint64_t>,
|
||||
&castVTyped<int64_t, float>,
|
||||
&castVTyped<int64_t, double>,
|
||||
&castVTyped<int64_t, string>,
|
||||
},
|
||||
// to pvUByte
|
||||
{&noconvert,
|
||||
&castVTyped<uint8_t, int8_t>,
|
||||
&castVTyped<uint8_t, int16_t>,
|
||||
&castVTyped<uint8_t, int32_t>,
|
||||
&castVTyped<uint8_t, uint64_t>,
|
||||
©V<uint8_t>,
|
||||
&castVTyped<uint8_t, uint16_t>,
|
||||
&castVTyped<uint8_t, uint32_t>,
|
||||
&castVTyped<uint8_t, uint64_t>,
|
||||
&castVTyped<uint8_t, float>,
|
||||
&castVTyped<uint8_t, double>,
|
||||
&castVTyped<uint8_t, string>,
|
||||
},
|
||||
// to pvUShort
|
||||
{&noconvert,
|
||||
&castVTyped<uint16_t, int8_t>,
|
||||
&castVTyped<uint16_t, int16_t>,
|
||||
&castVTyped<uint16_t, int32_t>,
|
||||
&castVTyped<uint16_t, uint64_t>,
|
||||
&castVTyped<uint16_t, uint8_t>,
|
||||
©V<uint16_t>,
|
||||
&castVTyped<uint16_t, uint32_t>,
|
||||
&castVTyped<uint16_t, uint64_t>,
|
||||
&castVTyped<uint16_t, float>,
|
||||
&castVTyped<uint16_t, double>,
|
||||
&castVTyped<uint16_t, string>,
|
||||
},
|
||||
// to pvUInt
|
||||
{&noconvert,
|
||||
&castVTyped<uint32_t, int8_t>,
|
||||
&castVTyped<uint32_t, int16_t>,
|
||||
&castVTyped<uint32_t, int32_t>,
|
||||
&castVTyped<uint32_t, uint64_t>,
|
||||
&castVTyped<uint32_t, uint8_t>,
|
||||
&castVTyped<uint32_t, uint16_t>,
|
||||
©V<uint32_t>,
|
||||
&castVTyped<uint32_t, uint64_t>,
|
||||
&castVTyped<uint32_t, float>,
|
||||
&castVTyped<uint32_t, double>,
|
||||
&castVTyped<uint32_t, string>,
|
||||
},
|
||||
// to pvULong
|
||||
{&noconvert,
|
||||
&castVTyped<uint64_t, int8_t>,
|
||||
&castVTyped<uint64_t, int16_t>,
|
||||
&castVTyped<uint64_t, int32_t>,
|
||||
&castVTyped<uint64_t, uint64_t>,
|
||||
&castVTyped<uint64_t, uint8_t>,
|
||||
&castVTyped<uint64_t, uint16_t>,
|
||||
&castVTyped<uint64_t, uint32_t>,
|
||||
©V<uint64_t>,
|
||||
&castVTyped<uint64_t, float>,
|
||||
&castVTyped<uint64_t, double>,
|
||||
&castVTyped<uint64_t, string>,
|
||||
},
|
||||
// to pvFloat
|
||||
{&noconvert,
|
||||
&castVTyped<float, int8_t>,
|
||||
&castVTyped<float, int16_t>,
|
||||
&castVTyped<float, int32_t>,
|
||||
&castVTyped<float, uint64_t>,
|
||||
&castVTyped<float, uint8_t>,
|
||||
&castVTyped<float, uint16_t>,
|
||||
&castVTyped<float, uint32_t>,
|
||||
&castVTyped<float, uint64_t>,
|
||||
©V<float>,
|
||||
&castVTyped<float, double>,
|
||||
&castVTyped<float, string>,
|
||||
},
|
||||
// to pvDouble
|
||||
{&noconvert,
|
||||
&castVTyped<double, int8_t>,
|
||||
&castVTyped<double, int16_t>,
|
||||
&castVTyped<double, int32_t>,
|
||||
&castVTyped<double, uint64_t>,
|
||||
&castVTyped<double, uint8_t>,
|
||||
&castVTyped<double, uint16_t>,
|
||||
&castVTyped<double, uint32_t>,
|
||||
&castVTyped<double, uint64_t>,
|
||||
&castVTyped<double, float>,
|
||||
©V<double>,
|
||||
&castVTyped<double, string>,
|
||||
},
|
||||
// to pvString
|
||||
{&castVTyped<string, epics::pvData::boolean>,
|
||||
&castVTyped<string, int8_t>,
|
||||
&castVTyped<string, int16_t>,
|
||||
&castVTyped<string, int32_t>,
|
||||
&castVTyped<string, uint64_t>,
|
||||
&castVTyped<string, uint8_t>,
|
||||
&castVTyped<string, uint16_t>,
|
||||
&castVTyped<string, uint32_t>,
|
||||
&castVTyped<string, uint64_t>,
|
||||
&castVTyped<string, float>,
|
||||
&castVTyped<string, double>,
|
||||
©V<string>,
|
||||
},
|
||||
};
|
||||
|
||||
} // end namespace
|
||||
|
||||
@@ -71,207 +242,12 @@ namespace epics { namespace pvData {
|
||||
|
||||
void castUnsafeV(size_t count, ScalarType to, void *dest, ScalarType from, const void *src)
|
||||
{
|
||||
#define COPYMEM(N) copyMem<N>(count, dest, src)
|
||||
#define CAST(TO, FROM) castVTyped<TO, FROM>(count, dest, src)
|
||||
unsigned int ito=to, ifrom=from;
|
||||
|
||||
switch(to) {
|
||||
case pvBoolean:
|
||||
switch(from) {
|
||||
case pvBoolean: COPYMEM(1); return;
|
||||
case pvString: CAST(boolean, std::string); return;
|
||||
default: noconvert(); return;
|
||||
}
|
||||
break;
|
||||
if(ito>pvString || ifrom>pvString)
|
||||
throw std::runtime_error("castUnsafeV: Invalid types");
|
||||
|
||||
case pvByte:
|
||||
switch(from) {
|
||||
case pvBoolean: noconvert(); return;
|
||||
case pvByte:
|
||||
case pvUByte: COPYMEM(1); return;
|
||||
case pvShort: CAST(int8, int16); return;
|
||||
case pvUShort: CAST(int8, uint16); return;
|
||||
case pvInt: CAST(int8, int32); return;
|
||||
case pvUInt: CAST(int8, uint32); return;
|
||||
case pvLong: CAST(int8, int64); return;
|
||||
case pvULong: CAST(int8, uint64); return;
|
||||
case pvFloat: CAST(int8, float); return;
|
||||
case pvDouble: CAST(int8, double); return;
|
||||
case pvString: CAST(int8, std::string); return;
|
||||
}
|
||||
break;
|
||||
|
||||
case pvUByte:
|
||||
switch(from) {
|
||||
case pvBoolean: noconvert(); return;
|
||||
case pvByte:
|
||||
case pvUByte: COPYMEM(1); return;
|
||||
case pvShort: CAST(uint8, int16); return;
|
||||
case pvUShort: CAST(uint8, uint16); return;
|
||||
case pvInt: CAST(uint8, int32); return;
|
||||
case pvUInt: CAST(uint8, uint32); return;
|
||||
case pvLong: CAST(uint8, int64); return;
|
||||
case pvULong: CAST(uint8, uint64); return;
|
||||
case pvFloat: CAST(uint8, float); return;
|
||||
case pvDouble: CAST(uint8, double); return;
|
||||
case pvString: CAST(uint8, std::string); return;
|
||||
}
|
||||
break;
|
||||
|
||||
case pvShort:
|
||||
switch(from) {
|
||||
case pvBoolean: noconvert(); return;
|
||||
case pvByte: CAST(int16, int8); return;
|
||||
case pvUByte: CAST(int16, uint8); return;
|
||||
case pvShort:
|
||||
case pvUShort: COPYMEM(2); return;
|
||||
case pvInt: CAST(int16, int32); return;
|
||||
case pvUInt: CAST(int16, uint32); return;
|
||||
case pvLong: CAST(int16, int64); return;
|
||||
case pvULong: CAST(int16, uint64); return;
|
||||
case pvFloat: CAST(int16, float); return;
|
||||
case pvDouble: CAST(int16, double); return;
|
||||
case pvString: CAST(int16, std::string); return;
|
||||
}
|
||||
break;
|
||||
|
||||
case pvUShort:
|
||||
switch(from) {
|
||||
case pvBoolean: noconvert(); return;
|
||||
case pvByte: CAST(uint16, int8); return;
|
||||
case pvUByte: CAST(uint16, uint8); return;
|
||||
case pvShort:
|
||||
case pvUShort: COPYMEM(2); return;
|
||||
case pvInt: CAST(uint16, int32); return;
|
||||
case pvUInt: CAST(uint16, uint32); return;
|
||||
case pvLong: CAST(uint16, int64); return;
|
||||
case pvULong: CAST(uint16, uint64); return;
|
||||
case pvFloat: CAST(uint16, float); return;
|
||||
case pvDouble: CAST(uint16, double); return;
|
||||
case pvString: CAST(uint16, std::string); return;
|
||||
}
|
||||
break;
|
||||
|
||||
case pvInt:
|
||||
switch(from) {
|
||||
case pvBoolean: noconvert(); return;
|
||||
case pvByte: CAST(int32, int8); return;
|
||||
case pvUByte: CAST(int32, uint8); return;
|
||||
case pvShort: CAST(int32, int16); return;
|
||||
case pvUShort: CAST(int32, uint16); return;
|
||||
case pvInt:
|
||||
case pvUInt: COPYMEM(4); return;
|
||||
case pvLong: CAST(int32, int64); return;
|
||||
case pvULong: CAST(int32, uint64); return;
|
||||
case pvFloat: CAST(int32, float); return;
|
||||
case pvDouble: CAST(int32, double); return;
|
||||
case pvString: CAST(int32, std::string); return;
|
||||
}
|
||||
break;
|
||||
|
||||
case pvUInt:
|
||||
switch(from) {
|
||||
case pvBoolean: noconvert(); return;
|
||||
case pvByte: CAST(uint32, int8); return;
|
||||
case pvUByte: CAST(uint32, uint8); return;
|
||||
case pvShort: CAST(uint32, int16); return;
|
||||
case pvUShort: CAST(uint32, uint16); return;
|
||||
case pvInt:
|
||||
case pvUInt: COPYMEM(4); return;
|
||||
case pvLong: CAST(uint32, int64); return;
|
||||
case pvULong: CAST(uint32, uint64); return;
|
||||
case pvFloat: CAST(uint32, float); return;
|
||||
case pvDouble: CAST(uint32, double); return;
|
||||
case pvString: CAST(uint32, std::string); return;
|
||||
}
|
||||
break;
|
||||
|
||||
case pvLong:
|
||||
switch(from) {
|
||||
case pvBoolean: noconvert(); return;
|
||||
case pvByte: CAST(int64, int8); return;
|
||||
case pvUByte: CAST(int64, uint8); return;
|
||||
case pvShort: CAST(int64, int16); return;
|
||||
case pvUShort: CAST(int64, uint16); return;
|
||||
case pvInt: CAST(int64, int32); return;
|
||||
case pvUInt: CAST(int64, uint32); return;
|
||||
case pvLong:
|
||||
case pvULong: COPYMEM(8); return;
|
||||
case pvFloat: CAST(int64, float); return;
|
||||
case pvDouble: CAST(int64, double); return;
|
||||
case pvString: CAST(int64, std::string); return;
|
||||
}
|
||||
break;
|
||||
|
||||
case pvULong:
|
||||
switch(from) {
|
||||
case pvBoolean: noconvert(); return;
|
||||
case pvByte: CAST(uint64, int8); return;
|
||||
case pvUByte: CAST(uint64, uint8); return;
|
||||
case pvShort: CAST(uint64, int16); return;
|
||||
case pvUShort: CAST(uint64, uint16); return;
|
||||
case pvInt: CAST(uint64, int32); return;
|
||||
case pvUInt: CAST(uint64, uint32); return;
|
||||
case pvLong:
|
||||
case pvULong: COPYMEM(8); return;
|
||||
case pvFloat: CAST(uint64, float); return;
|
||||
case pvDouble: CAST(uint64, double); return;
|
||||
case pvString: CAST(uint64, std::string); return;
|
||||
}
|
||||
break;
|
||||
|
||||
case pvFloat:
|
||||
switch(from) {
|
||||
case pvBoolean: noconvert(); return;
|
||||
case pvByte: CAST(float, int8); return;
|
||||
case pvUByte: CAST(float, uint8); return;
|
||||
case pvShort: CAST(float, int16); return;
|
||||
case pvUShort: CAST(float, uint16); return;
|
||||
case pvInt: CAST(float, int32); return;
|
||||
case pvUInt: CAST(float, uint32); return;
|
||||
case pvLong: CAST(float, int64); return;
|
||||
case pvULong: CAST(float, uint64); return;
|
||||
case pvFloat: COPYMEM(4); return;
|
||||
case pvDouble: CAST(float, double); return;
|
||||
case pvString: CAST(float, std::string); return;
|
||||
}
|
||||
break;
|
||||
|
||||
case pvDouble:
|
||||
switch(from) {
|
||||
case pvBoolean: noconvert(); return;
|
||||
case pvByte: CAST(double, int8); return;
|
||||
case pvUByte: CAST(double, uint8); return;
|
||||
case pvShort: CAST(double, int16); return;
|
||||
case pvUShort: CAST(double, uint16); return;
|
||||
case pvInt: CAST(double, int32); return;
|
||||
case pvUInt: CAST(double, uint32); return;
|
||||
case pvLong: CAST(double, int64); return;
|
||||
case pvULong: CAST(double, uint64); return;
|
||||
case pvFloat: CAST(double, float); return;
|
||||
case pvDouble: COPYMEM(8); return;
|
||||
case pvString: CAST(double, std::string); return;
|
||||
}
|
||||
break;
|
||||
|
||||
case pvString:
|
||||
switch(from) {
|
||||
case pvBoolean: CAST(std::string, boolean); return;
|
||||
case pvByte: CAST(std::string, int8); return;
|
||||
case pvUByte: CAST(std::string, uint8); return;
|
||||
case pvShort: CAST(std::string, int16); return;
|
||||
case pvUShort: CAST(std::string, uint16); return;
|
||||
case pvInt: CAST(std::string, int32); return;
|
||||
case pvUInt: CAST(std::string, uint32); return;
|
||||
case pvLong: CAST(std::string, int64); return;
|
||||
case pvULong: CAST(std::string, uint64); return;
|
||||
case pvFloat: CAST(std::string, float); return;
|
||||
case pvDouble: CAST(std::string, double); return;
|
||||
case pvString: copyV<std::string>(count, dest, src); return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
THROW_EXCEPTION2(std::logic_error, "Undefined cast");
|
||||
converters[ito][ifrom](count, dest, src);
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
9
src/monitor/Makefile
Normal file
9
src/monitor/Makefile
Normal file
@@ -0,0 +1,9 @@
|
||||
# This is a Makefile fragment, see ../Makefile
|
||||
|
||||
SRC_DIRS += $(PVDATA_SRC)/monitor
|
||||
|
||||
INC += pv/monitor.h
|
||||
INC += pv/monitorPlugin.h
|
||||
|
||||
LIBSRCS += monitor.cpp
|
||||
LIBSRCS += monitorPlugin.cpp
|
||||
13
src/monitor/monitor.cpp
Normal file
13
src/monitor/monitor.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
/*monitor.cpp*/
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mse
|
||||
*/
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
|
||||
// needed to get interfaces exported
|
||||
#include <pv/monitor.h>
|
||||
86
src/monitor/monitorPlugin.cpp
Normal file
86
src/monitor/monitorPlugin.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
/* monitorPlugin.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
|
||||
#define IN_MONITORPLUGIN_CPP
|
||||
#define epicsExportSharedSymbols
|
||||
|
||||
#include <pv/monitorPlugin.h>
|
||||
|
||||
using std::string;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
MonitorPluginManagerPtr MonitorPluginManager::get()
|
||||
{
|
||||
static MonitorPluginManagerPtr pluginManager;
|
||||
static Mutex mutex;
|
||||
Lock xx(mutex);
|
||||
if(!pluginManager) {
|
||||
pluginManager = MonitorPluginManagerPtr(new MonitorPluginManager());
|
||||
}
|
||||
return pluginManager;
|
||||
}
|
||||
|
||||
bool MonitorPluginManager::addPlugin(
|
||||
string const &pluginName,
|
||||
MonitorPluginCreatorPtr const &creator)
|
||||
{
|
||||
mutex.lock();
|
||||
std::list<MonitorPluginCreatorPtr>::iterator iter;
|
||||
for (iter = monitorPluginList.begin();iter!=monitorPluginList.end();iter++)
|
||||
{
|
||||
if(*iter==creator)
|
||||
{
|
||||
mutex.unlock();
|
||||
return false;
|
||||
}
|
||||
if(((*iter)->getName().compare(pluginName))==0)
|
||||
{
|
||||
mutex.unlock();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
monitorPluginList.push_back(creator);
|
||||
mutex.unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
MonitorPluginCreatorPtr MonitorPluginManager::findPlugin(
|
||||
string const &pluginName)
|
||||
{
|
||||
mutex.lock();
|
||||
std::list<MonitorPluginCreatorPtr>::iterator iter;
|
||||
for (iter = monitorPluginList.begin();iter!=monitorPluginList.end();++iter)
|
||||
{
|
||||
if(((*iter)->getName().compare(pluginName))==0)
|
||||
{
|
||||
mutex.unlock();
|
||||
return *iter;
|
||||
}
|
||||
}
|
||||
mutex.unlock();
|
||||
return MonitorPluginCreatorPtr();
|
||||
}
|
||||
|
||||
void MonitorPluginManager::showNames()
|
||||
{
|
||||
mutex.lock();
|
||||
std::list<MonitorPluginCreatorPtr>::iterator iter;
|
||||
for (iter = monitorPluginList.begin();iter!=monitorPluginList.end();++iter)
|
||||
{
|
||||
std::cout << (*iter)->getName() << std::endl;
|
||||
}
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
118
src/monitor/pv/monitor.h
Normal file
118
src/monitor/pv/monitor.h
Normal file
@@ -0,0 +1,118 @@
|
||||
/* 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
|
||||
|
||||
#include <pv/status.h>
|
||||
#include <pv/destroyable.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/sharedPtr.h>
|
||||
#include <pv/bitSet.h>
|
||||
#include <pv/requester.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
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(PVStructurePtr const & pvStructurePtr)
|
||||
: pvStructurePtr(pvStructurePtr),
|
||||
changedBitSet(BitSet::create(static_cast<uint32>(pvStructurePtr->getNumberFields()))),
|
||||
overrunBitSet(BitSet::create(static_cast<uint32>(pvStructurePtr->getNumberFields())))
|
||||
{}
|
||||
PVStructurePtr pvStructurePtr;
|
||||
BitSet::shared_pointer changedBitSet;
|
||||
BitSet::shared_pointer overrunBitSet;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Monitor changes to fields of a pvStructure.
|
||||
*
|
||||
* This is used by pvAccess to implement monitors.
|
||||
* @author mrk
|
||||
*/
|
||||
class epicsShareClass Monitor : public Destroyable{
|
||||
public:
|
||||
POINTER_DEFINITIONS(Monitor);
|
||||
virtual ~Monitor(){}
|
||||
/**
|
||||
* Start monitoring.
|
||||
* @return completion status.
|
||||
*/
|
||||
virtual Status start() = 0;
|
||||
/**
|
||||
* Stop Monitoring.
|
||||
* @return completion status.
|
||||
*/
|
||||
virtual Status stop() = 0;
|
||||
/**
|
||||
* If monitor has occurred return data.
|
||||
* @return monitorElement for modified data.
|
||||
* Must call get to determine if data is available.
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Callback implemented by monitor clients.
|
||||
*
|
||||
* Requester for ChannelMonitor.
|
||||
* @author mrk
|
||||
*/
|
||||
class epicsShareClass MonitorRequester : public virtual Requester {
|
||||
public:
|
||||
POINTER_DEFINITIONS(MonitorRequester);
|
||||
virtual ~MonitorRequester(){}
|
||||
/**
|
||||
* The client and server have both completed the createMonitor request.
|
||||
* @param status Completion status.
|
||||
* @param monitor The monitor
|
||||
* @param structure The structure defining the data.
|
||||
*/
|
||||
virtual void monitorConnect(Status const & status,
|
||||
MonitorPtr const & monitor, StructureConstPtr const & structure) = 0;
|
||||
/**
|
||||
* A monitor event has occurred.
|
||||
* The requester must call Monitor.poll to get data.
|
||||
* @param monitor The monitor.
|
||||
*/
|
||||
virtual void monitorEvent(MonitorPtr const & monitor) = 0;
|
||||
/**
|
||||
* The data source is no longer available.
|
||||
* @param monitor The monitor.
|
||||
*/
|
||||
virtual void unlisten(MonitorPtr const & monitor) = 0;
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* MONITOR_H */
|
||||
180
src/monitor/pv/monitorPlugin.h
Normal file
180
src/monitor/pv/monitorPlugin.h
Normal file
@@ -0,0 +1,180 @@
|
||||
/* monitorPlugin.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 MONITORPLUGIN_H
|
||||
#define MONITORPLUGIN_H
|
||||
|
||||
#include <list>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/sharedPtr.h>
|
||||
#include <pv/bitSet.h>
|
||||
#include <pv/monitor.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
#if !defined(IN_MONITORPLUGIN_CPP)
|
||||
#warning monitorPlugin.h is deprecated
|
||||
#endif
|
||||
|
||||
#if !defined(IN_MONITORPLUGIN_CPP) && defined(__GNUC__) && !(defined(__vxworks) && !defined(_WRS_VXWORKS_MAJOR))
|
||||
#define USAGE_DEPRECATED __attribute__((deprecated))
|
||||
#else
|
||||
#define USAGE_DEPRECATED
|
||||
#endif
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class MonitorPlugin;
|
||||
typedef std::tr1::shared_ptr<MonitorPlugin> MonitorPluginPtr;
|
||||
|
||||
|
||||
class MonitorPluginCreator;
|
||||
typedef std::tr1::shared_ptr<MonitorPluginCreator> MonitorPluginCreatorPtr;
|
||||
|
||||
|
||||
class MonitorPluginManager;
|
||||
typedef std::tr1::shared_ptr<MonitorPluginManager> MonitorPluginManagerPtr;
|
||||
|
||||
|
||||
/**
|
||||
* @brief A plugin for raising monitors;
|
||||
*
|
||||
* This is for use by pvAccess servers that support monitors.
|
||||
* Since the interface has only a dependence on pvData it
|
||||
* can be used for other purposes.
|
||||
* A monitor is assumed to be associated with a field of a top-level
|
||||
* structure.
|
||||
*/
|
||||
class epicsShareClass USAGE_DEPRECATED MonitorPlugin
|
||||
{
|
||||
public:
|
||||
virtual ~MonitorPlugin(){}
|
||||
/**
|
||||
* getName
|
||||
* @returns The name of the plugin
|
||||
*/
|
||||
virtual std::string const & getName() = 0;
|
||||
/**
|
||||
* Should a monitor be raised?
|
||||
* @param pvField The field being monitored.
|
||||
* @param pvTop The top-level structure in which the field resides.
|
||||
* @param monitorElement The client data and bitsets.
|
||||
* @returns true or false.
|
||||
* True is returned if the change to this field should cause a monitor.
|
||||
* False is returned in a change only to this field should not cause a
|
||||
* monitor.
|
||||
*/
|
||||
virtual bool causeMonitor(
|
||||
PVFieldPtr const &pvField,
|
||||
PVStructurePtr const &pvTop,
|
||||
MonitorElementPtr const &monitorElement) = 0;
|
||||
/**
|
||||
* A monitor will be sent to the client.
|
||||
* @param monitorElement The data for the client.
|
||||
* The plugin is allowed to change the data values.
|
||||
*/
|
||||
virtual void monitorDone(
|
||||
MonitorElementPtr const &monitorElement)
|
||||
{}
|
||||
/**
|
||||
* Begin monitoring
|
||||
*/
|
||||
virtual void startMonitoring(){}
|
||||
/**
|
||||
* Stop monitoring
|
||||
*/
|
||||
virtual void stopMonitoring(){}
|
||||
/**
|
||||
* Begin a set of puts.
|
||||
*/
|
||||
virtual void beginGroupPut() {};
|
||||
/**
|
||||
* End a set of puts.
|
||||
*/
|
||||
virtual void endGroupPut() {};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A class that creates a plugin.
|
||||
*
|
||||
* Normlly a plugin is created for a single client.
|
||||
*/
|
||||
class epicsShareClass USAGE_DEPRECATED MonitorPluginCreator
|
||||
{
|
||||
public:
|
||||
virtual ~MonitorPluginCreator() {}
|
||||
/**
|
||||
* Create a monitor plugin.
|
||||
* @param field The introspection interface for the field monitored.
|
||||
* @param top The introspection interface for the client structure.
|
||||
* @param pvFieldOptions The options the client requested.
|
||||
* The structure has a set of PVString subfields.
|
||||
* The options are a set of name,value pairs.
|
||||
* The subfield name is the name and the subfield value is the value.
|
||||
* @returns shared pointer to a MonitorPluginCreator.
|
||||
*/
|
||||
virtual MonitorPluginPtr create(
|
||||
FieldConstPtr const &field,
|
||||
StructureConstPtr const &top,
|
||||
PVStructurePtr const &pvFieldOptions) = 0;
|
||||
/**
|
||||
* getName
|
||||
* @returns The name of the plugin
|
||||
*/
|
||||
virtual std::string const & getName() = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Manager for plugins.
|
||||
*
|
||||
* This manages a set of monitor plugins.
|
||||
* @author mrk
|
||||
*/
|
||||
class epicsShareClass USAGE_DEPRECATED MonitorPluginManager
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(MonitorPluginManager);
|
||||
/**
|
||||
* Factory to get the manager.
|
||||
* @return shared pointer to manager.
|
||||
*/
|
||||
static MonitorPluginManagerPtr get();
|
||||
/** destructor
|
||||
*/
|
||||
~MonitorPluginManager(){}
|
||||
/* add plugin
|
||||
* @param pluginName The name of the plugin.
|
||||
* @param creator The creator.
|
||||
* @returns true or false
|
||||
* false is returned if a plugin with that name is already present
|
||||
*/
|
||||
bool addPlugin(
|
||||
std::string const &pluginName,
|
||||
MonitorPluginCreatorPtr const &creator);
|
||||
/* find plugin
|
||||
*
|
||||
* @param plugin name
|
||||
* @returns share pointer to plugin creator.
|
||||
* If a plugin with that name is not found NULL is returned.
|
||||
*/
|
||||
MonitorPluginCreatorPtr findPlugin(std::string const &pluginName);
|
||||
/* showNames
|
||||
*
|
||||
*/
|
||||
void showNames();
|
||||
private:
|
||||
MonitorPluginManager(){}
|
||||
std::list<MonitorPluginCreatorPtr> monitorPluginList;
|
||||
epics::pvData::Mutex mutex;
|
||||
};
|
||||
|
||||
#undef USAGE_DEPRECATED
|
||||
|
||||
}}
|
||||
#endif /* MONITORPLUGIN_H */
|
||||
@@ -19,10 +19,10 @@
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
epicsShareExtern const int32 milliSecPerSec;
|
||||
epicsShareExtern const int32 microSecPerSec;
|
||||
epicsShareExtern const int32 nanoSecPerSec;
|
||||
epicsShareExtern const int64 posixEpochAtEpicsEpoch;
|
||||
epicsShareExtern int32 milliSecPerSec;
|
||||
epicsShareExtern int32 microSecPerSec;
|
||||
epicsShareExtern int32 nanoSecPerSec;
|
||||
epicsShareExtern int64 posixEpochAtEpicsEpoch;
|
||||
|
||||
/** @brief Methods for manipulating timeStamp.
|
||||
*
|
||||
@@ -98,7 +98,7 @@ public:
|
||||
* Set userTag.
|
||||
* @param userTag application specific.
|
||||
*/
|
||||
void setUserTag(int32 userTag) {this->userTag = userTag;}
|
||||
void setUserTag(int userTag) {this->userTag = userTag;}
|
||||
/**
|
||||
* Set time fields in timeStamp.
|
||||
* Result will be normalized.
|
||||
|
||||
@@ -72,25 +72,10 @@ bool PVAlarm::set(Alarm const & alarm)
|
||||
throw std::logic_error(notAttached);
|
||||
}
|
||||
if(pvSeverity->isImmutable() || pvMessage->isImmutable()) return false;
|
||||
Alarm current;
|
||||
get(current);
|
||||
bool returnValue = false;
|
||||
if(current.getSeverity()!=alarm.getSeverity())
|
||||
{
|
||||
pvSeverity->put(alarm.getSeverity());
|
||||
returnValue = true;
|
||||
}
|
||||
if(current.getStatus()!=alarm.getStatus())
|
||||
{
|
||||
pvStatus->put(alarm.getStatus());
|
||||
returnValue = true;
|
||||
}
|
||||
if(current.getMessage()!=alarm.getMessage())
|
||||
{
|
||||
pvMessage->put(alarm.getMessage());
|
||||
returnValue = true;
|
||||
}
|
||||
return returnValue;
|
||||
pvSeverity->put(alarm.getSeverity());
|
||||
pvStatus->put(alarm.getStatus());
|
||||
pvMessage->put(alarm.getMessage());
|
||||
return true;
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
@@ -70,25 +70,10 @@ bool PVControl::set(Control const & control)
|
||||
throw std::logic_error(notAttached);
|
||||
}
|
||||
if(pvLow->isImmutable() || pvHigh->isImmutable() || pvMinStep->isImmutable()) return false;
|
||||
Control current;
|
||||
get(current);
|
||||
bool returnValue = false;
|
||||
if(current.getLow()!=control.getLow())
|
||||
{
|
||||
pvLow->put(control.getLow());
|
||||
returnValue = true;
|
||||
}
|
||||
if(current.getHigh()!=control.getHigh())
|
||||
{
|
||||
pvHigh->put(control.getHigh());
|
||||
returnValue = true;
|
||||
}
|
||||
if(current.getMinStep()!=control.getMinStep())
|
||||
{
|
||||
pvMinStep->put(control.getMinStep());
|
||||
returnValue = true;
|
||||
}
|
||||
return returnValue;
|
||||
pvLow->put(control.getLow());
|
||||
pvHigh->put(control.getHigh());
|
||||
pvMinStep->put(control.getMinStep());
|
||||
return true;
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
@@ -85,38 +85,13 @@ bool PVDisplay::set(Display const & display)
|
||||
}
|
||||
if(pvDescription->isImmutable() || pvFormat->isImmutable()) return false;
|
||||
if(pvUnits->isImmutable() || pvLow->isImmutable() || pvHigh->isImmutable())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Display current;
|
||||
get(current);
|
||||
bool returnValue = false;
|
||||
if(current.getDescription()!=display.getDescription())
|
||||
{
|
||||
pvDescription->put(display.getDescription());
|
||||
returnValue = true;
|
||||
}
|
||||
if(current.getFormat()!=display.getFormat())
|
||||
{
|
||||
pvFormat->put(display.getFormat());
|
||||
returnValue = true;
|
||||
}
|
||||
if(current.getUnits()!=display.getUnits())
|
||||
{
|
||||
pvUnits->put(display.getUnits());
|
||||
returnValue = true;
|
||||
}
|
||||
if(current.getLow()!=display.getLow())
|
||||
{
|
||||
pvLow->put(display.getLow());
|
||||
returnValue = true;
|
||||
}
|
||||
if(current.getHigh()!=display.getHigh())
|
||||
{
|
||||
pvHigh->put(display.getHigh());
|
||||
returnValue = true;
|
||||
}
|
||||
return returnValue;
|
||||
pvDescription->put(display.getDescription());
|
||||
pvFormat->put(display.getFormat());
|
||||
pvUnits->put(display.getUnits());
|
||||
pvLow->put(display.getLow());
|
||||
pvHigh->put(display.getHigh());
|
||||
return true;
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
@@ -30,14 +30,14 @@ bool PVTimeStamp::attach(PVFieldPtr const & pvField)
|
||||
PVStructure* pvStructure = xxx.get();
|
||||
while(true) {
|
||||
PVLongPtr pvLong = pvStructure->getSubField<PVLong>("secondsPastEpoch");
|
||||
if(pvLong) {
|
||||
if(pvLong.get()!=NULL) {
|
||||
pvSecs = pvLong;
|
||||
pvNano = pvStructure->getSubField<PVInt>("nanoseconds");
|
||||
pvUserTag = pvStructure->getSubField<PVInt>("userTag");
|
||||
}
|
||||
if(pvSecs
|
||||
&& pvNano
|
||||
&& pvUserTag) return true;
|
||||
if(pvSecs.get()!=NULL
|
||||
&& pvNano.get()!=NULL
|
||||
&& pvUserTag.get()!=NULL) return true;
|
||||
detach();
|
||||
// look up the tree for a timeSyamp
|
||||
pvStructure = pvStructure->getParent();
|
||||
@@ -73,25 +73,10 @@ bool PVTimeStamp::set(TimeStamp const & timeStamp)
|
||||
throw std::logic_error(notAttached);
|
||||
}
|
||||
if(pvSecs->isImmutable() || pvNano->isImmutable()) return false;
|
||||
TimeStamp current;
|
||||
get(current);
|
||||
bool returnValue = false;
|
||||
if(current.getSecondsPastEpoch()!=timeStamp.getSecondsPastEpoch())
|
||||
{
|
||||
pvSecs->put(timeStamp.getSecondsPastEpoch());
|
||||
returnValue = true;
|
||||
}
|
||||
if(current.getNanoseconds()!=timeStamp.getNanoseconds())
|
||||
{
|
||||
pvNano->put(timeStamp.getNanoseconds());
|
||||
returnValue = true;
|
||||
}
|
||||
if(current.getUserTag()!=timeStamp.getUserTag())
|
||||
{
|
||||
pvUserTag->put(timeStamp.getUserTag());
|
||||
returnValue = true;
|
||||
}
|
||||
return returnValue;
|
||||
pvSecs->put(timeStamp.getSecondsPastEpoch());
|
||||
pvUserTag->put(timeStamp.getUserTag());
|
||||
pvNano->put(timeStamp.getNanoseconds());
|
||||
return true;
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user