Compare commits
108 Commits
7.1.0-pre1
...
8.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
828506720c | ||
|
|
ad712b63f9 | ||
|
|
4c3d5a788d | ||
|
|
df89135455 | ||
|
|
95d452870c | ||
|
|
8a82ff9fe4 | ||
|
|
dd24b2ad75 | ||
|
|
17fa7a7724 | ||
|
|
d01ba94ed7 | ||
|
|
c7c7585950 | ||
|
|
f869936cfd | ||
|
|
0ad253c6f0 | ||
|
|
52d04d5044 | ||
|
|
bff2dc9cd2 | ||
|
|
b903df5d0d | ||
|
|
416d910577 | ||
|
|
37b7a0708f | ||
|
|
6ceaa6adb0 | ||
|
|
0447826e7c | ||
|
|
3ef60a61a2 | ||
|
|
31802a8bde | ||
|
|
c2bc77a649 | ||
|
|
4dd7a18301 | ||
|
|
a29894ee2b | ||
|
|
2f8ac7f673 | ||
|
|
b050fbbcbe | ||
|
|
caa11605fc | ||
|
|
cfcdd1a3f9 | ||
|
|
95ff606ba1 | ||
|
|
4cc9b650c5 | ||
|
|
cd2436342d | ||
|
|
3ae2d09fe3 | ||
|
|
35b3403de6 | ||
|
|
f780ebdf76 | ||
|
|
93f0518b4b | ||
|
|
6da871fa64 | ||
|
|
d53cb0cbc9 | ||
|
|
8f0111e482 | ||
|
|
5525119778 | ||
|
|
6410600205 | ||
|
|
a5d44745d1 | ||
|
|
8c275cbc1c | ||
|
|
b79f69231c | ||
|
|
ff165595c4 | ||
|
|
12d851dc6f | ||
|
|
643f289c23 | ||
|
|
68e74ed1d2 | ||
|
|
f0ef0965c4 | ||
|
|
61ce532fdf | ||
|
|
d746e1bfb3 | ||
|
|
deccc41b9a | ||
|
|
4ffddfa2f6 | ||
|
|
2814c779bd | ||
|
|
4c73607799 | ||
|
|
d776f6eaf0 | ||
|
|
706ef01782 | ||
|
|
00c62cbd67 | ||
|
|
da8f3d6cc7 | ||
|
|
0bc95e51c2 | ||
|
|
727153e965 | ||
|
|
d00f54228d | ||
|
|
77c67802a3 | ||
|
|
7d68d177d7 | ||
|
|
9b20505dcd | ||
|
|
5f93e292b2 | ||
|
|
edd3e20f3c | ||
|
|
90cffa60d6 | ||
|
|
6171cd6867 | ||
|
|
fa731bf6c3 | ||
|
|
818fce324c | ||
|
|
1bc867e48d | ||
|
|
434b9f7a9f | ||
|
|
f54602dead | ||
|
|
fb546b41c1 | ||
|
|
e400d9f5fd | ||
|
|
f0fa8a2481 | ||
|
|
c3b0b49e3f | ||
|
|
45265b4f9b | ||
|
|
aa87a2a23d | ||
|
|
5a59b1da75 | ||
|
|
32aa0dd72f | ||
|
|
c1188b16a1 | ||
|
|
a02a60c658 | ||
| c5f9f5a2dc | |||
|
|
342b1bc8ef | ||
|
|
64158376f5 | ||
|
|
850d4ff056 | ||
|
|
f0cfe1c85a | ||
|
|
c8b615b3ee | ||
|
|
c67fdafb43 | ||
|
|
f66d277918 | ||
|
|
4ef7db20f8 | ||
|
|
e1216dfa76 | ||
|
|
340fa8a7cb | ||
|
|
a029455466 | ||
|
|
27f78c430b | ||
|
|
3d707e5e95 | ||
|
|
0406a2f614 | ||
|
|
a1c0e432ee | ||
|
|
57e57d9e43 | ||
|
|
e4e4188eaf | ||
|
|
f1553cc90e | ||
|
|
997e68c99a | ||
|
|
25663d9a7b | ||
|
|
1e55266396 | ||
|
|
810ae15991 | ||
|
|
271fec7f5e | ||
|
|
c43486791e |
@@ -1,21 +1,10 @@
|
||||
#!/bin/sh
|
||||
set -e -x
|
||||
|
||||
# set RTEMS to eg. "4.9" or "4.10"
|
||||
# requires qemu, bison, flex, texinfo, install-info
|
||||
if [ -n "$RTEMS" ]
|
||||
then
|
||||
# find local qemu-system-i386
|
||||
export PATH="$HOME/.cache/qemu/usr/bin:$PATH"
|
||||
echo -n "Using QEMU: "
|
||||
type qemu-system-i386 || echo "Missing qemu"
|
||||
EXTRA=RTEMS_QEMU_FIXUPS=YES
|
||||
fi
|
||||
|
||||
make -j2 $EXTRA
|
||||
make -j2
|
||||
|
||||
if [ "$TEST" != "NO" ]
|
||||
then
|
||||
make tapfiles
|
||||
make -s test-results
|
||||
make -j2 tapfiles
|
||||
make -j2 -s test-results
|
||||
fi
|
||||
|
||||
@@ -1,39 +1,6 @@
|
||||
#!/bin/sh
|
||||
set -e -x
|
||||
|
||||
CURDIR="$PWD"
|
||||
|
||||
QDIR="$HOME/.cache/qemu"
|
||||
|
||||
if [ -n "$RTEMS" -a "$TEST" = "YES" ]
|
||||
then
|
||||
git clone --quiet --branch vme --depth 10 https://github.com/mdavidsaver/qemu.git "$HOME/.build/qemu"
|
||||
cd "$HOME/.build/qemu"
|
||||
|
||||
HEAD=`git log -n1 --pretty=format:%H`
|
||||
echo "HEAD revision $HEAD"
|
||||
|
||||
[ -e "$HOME/.cache/qemu/built" ] && BUILT=`cat "$HOME/.cache/qemu/built"`
|
||||
echo "Cached revision $BUILT"
|
||||
|
||||
if [ "$HEAD" != "$BUILT" ]
|
||||
then
|
||||
echo "Building QEMU"
|
||||
git submodule --quiet update --init
|
||||
|
||||
install -d "$HOME/.build/qemu/build"
|
||||
cd "$HOME/.build/qemu/build"
|
||||
|
||||
"$HOME/.build/qemu/configure" --prefix="$HOME/.cache/qemu/usr" --target-list=i386-softmmu --disable-werror
|
||||
make -j2
|
||||
make install
|
||||
|
||||
echo "$HEAD" > "$HOME/.cache/qemu/built"
|
||||
fi
|
||||
fi
|
||||
|
||||
cd "$CURDIR"
|
||||
|
||||
cat << EOF > configure/RELEASE.local
|
||||
EPICS_BASE=$HOME/.source/epics-base
|
||||
EOF
|
||||
@@ -41,23 +8,8 @@ EOF
|
||||
install -d "$HOME/.source"
|
||||
cd "$HOME/.source"
|
||||
|
||||
add_base_module() {
|
||||
MODULE=$1
|
||||
BRANCH=$2
|
||||
( cd epics-base/modules && \
|
||||
git clone --quiet --depth 5 --branch "$MODULE"/"$BRANCH" https://github.com/${REPOBASE:-epics-base}/epics-base.git "$MODULE" && \
|
||||
cd "$MODULE" && git log -n1 )
|
||||
}
|
||||
|
||||
if [ "$BRBASE" ]
|
||||
then
|
||||
git clone --quiet --depth 5 --branch "$BRBASE" https://github.com/${REPOBASE:-epics-base}/epics-base.git epics-base
|
||||
(cd epics-base && git log -n1 )
|
||||
else
|
||||
git clone --quiet --depth 5 --branch core/"${BRCORE:-master}" https://github.com/${REPOBASE:-epics-base}/epics-base.git epics-base
|
||||
( cd epics-base && git log -n1 )
|
||||
add_base_module libcom "${BRLIBCOM:-master}"
|
||||
fi
|
||||
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`
|
||||
|
||||
@@ -115,24 +67,17 @@ EOF
|
||||
if [ -n "$RTEMS" ]
|
||||
then
|
||||
echo "Cross RTEMS${RTEMS} for pc386"
|
||||
install -d /home/travis/.cache
|
||||
curl -L "https://github.com/mdavidsaver/rsb/releases/download/travis-20160306-2/rtems${RTEMS}-i386-trusty-20190306-2.tar.gz" \
|
||||
| tar -C /home/travis/.cache -xj
|
||||
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/travis/.cache/rtems${RTEMS}-i386
|
||||
RTEMS_BASE=$HOME/.rtems
|
||||
EOF
|
||||
cat << EOF >> epics-base/configure/CONFIG_SITE
|
||||
CROSS_COMPILER_TARGET_ARCHS+=RTEMS-pc386
|
||||
CROSS_COMPILER_TARGET_ARCHS += RTEMS-pc386-qemu
|
||||
EOF
|
||||
|
||||
# find local qemu-system-i386
|
||||
export PATH="$HOME/.cache/qemu/usr/bin:$PATH"
|
||||
echo -n "Using QEMU: "
|
||||
type qemu-system-i386 || echo "Missing qemu"
|
||||
EXTRA=RTEMS_QEMU_FIXUPS=YES
|
||||
fi
|
||||
|
||||
make -j2 -C epics-base $EXTRA
|
||||
|
||||
17
.readthedocs.yml
Normal file
17
.readthedocs.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
# .readthedocs.yml
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Build documentation in the documentation/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: documentation/conf.py
|
||||
|
||||
# Build documentation with MkDocs
|
||||
#mkdocs:
|
||||
# configuration: mkdocs.yml
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
formats: all
|
||||
46
.travis.yml
46
.travis.yml
@@ -1,8 +1,5 @@
|
||||
sudo: false
|
||||
dist: trusty
|
||||
language: c++
|
||||
compiler:
|
||||
- gcc
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
@@ -11,19 +8,36 @@ addons:
|
||||
- perl
|
||||
- clang
|
||||
- g++-mingw-w64-i686
|
||||
- qemu-system-x86
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- dist: bionic
|
||||
env: BRBASE=7.0 CMPLR=gcc
|
||||
- dist: trusty
|
||||
env: BRBASE=7.0 CMPLR=gcc
|
||||
- dist: bionic
|
||||
env: BRBASE=7.0 CMPLR=clang
|
||||
- dist: trusty
|
||||
env: BRBASE=7.0 CMPLR=clang
|
||||
- dist: trusty
|
||||
env: BRBASE=7.0 USR_CXXFLAGS=-std=c++11
|
||||
- dist: trusty
|
||||
env: BRBASE=7.0 CMPLR=clang USR_CXXFLAGS=-std=c++11
|
||||
- dist: trusty
|
||||
env: BRBASE=7.0 WINE=32 TEST=NO STATIC=YES
|
||||
- dist: trusty
|
||||
env: BRBASE=7.0 WINE=32 TEST=NO STATIC=NO
|
||||
- dist: trusty
|
||||
env: BRBASE=7.0 RTEMS=4.10 TEST=NO
|
||||
- dist: trusty
|
||||
env: BRBASE=7.0 RTEMS=4.9 TEST=NO
|
||||
- dist: trusty
|
||||
env: BRBASE=3.16
|
||||
- dist: trusty
|
||||
env: BRBASE=3.15
|
||||
|
||||
install:
|
||||
- ./.ci/travis-prepare.sh
|
||||
script:
|
||||
- ./.ci/travis-build.sh
|
||||
env:
|
||||
- BRCORE=master BRLIBCOM=master
|
||||
- CMPLR=clang
|
||||
- USR_CXXFLAGS=-std=c++11
|
||||
- CMPLR=clang USR_CXXFLAGS=-std=c++11
|
||||
- WINE=32 TEST=NO STATIC=YES
|
||||
- WINE=32 TEST=NO STATIC=NO
|
||||
- RTEMS=4.10 TEST=NO
|
||||
- RTEMS=4.9 TEST=NO
|
||||
- BRBASE=3.16
|
||||
- BRBASE=3.15
|
||||
- BRBASE=3.14
|
||||
|
||||
316
Doxyfile
316
Doxyfile
@@ -1,4 +1,4 @@
|
||||
# Doxyfile 1.8.6
|
||||
# Doxyfile 1.8.8
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project.
|
||||
@@ -32,33 +32,33 @@ DOXYFILE_ENCODING = UTF-8
|
||||
# title of most generated pages and in a few other places.
|
||||
# The default value is: My Project.
|
||||
|
||||
PROJECT_NAME = pvDataCPP
|
||||
PROJECT_NAME = "PVData C++"
|
||||
|
||||
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER =
|
||||
PROJECT_NUMBER = 8.0.1-dev
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
# quick idea about the purpose of the project. Keep the description short.
|
||||
|
||||
PROJECT_BRIEF =
|
||||
PROJECT_BRIEF =
|
||||
|
||||
# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
|
||||
# the documentation. The maximum height of the logo should not exceed 55 pixels
|
||||
# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
|
||||
# to the output directory.
|
||||
|
||||
PROJECT_LOGO =
|
||||
PROJECT_LOGO =
|
||||
|
||||
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
|
||||
# into which the generated documentation will be written. If a relative path is
|
||||
# entered, it will be relative to the location where doxygen was started. If
|
||||
# left blank the current directory will be used.
|
||||
|
||||
OUTPUT_DIRECTORY =
|
||||
OUTPUT_DIRECTORY = .
|
||||
|
||||
# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
|
||||
# directories (in 2 levels) under the output directory of each output format and
|
||||
@@ -70,6 +70,14 @@ OUTPUT_DIRECTORY =
|
||||
|
||||
CREATE_SUBDIRS = NO
|
||||
|
||||
# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
|
||||
# characters to appear in the names of generated files. If set to NO, non-ASCII
|
||||
# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
|
||||
# U+3044.
|
||||
# The default value is: NO.
|
||||
|
||||
ALLOW_UNICODE_NAMES = NO
|
||||
|
||||
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
|
||||
# documentation generated by doxygen is written. Doxygen will use this
|
||||
# information to generate all constant output in the proper language.
|
||||
@@ -110,7 +118,17 @@ REPEAT_BRIEF = YES
|
||||
# the entity):The $name class, The $name widget, The $name file, is, provides,
|
||||
# specifies, contains, represents, a, an and the.
|
||||
|
||||
ABBREVIATE_BRIEF =
|
||||
ABBREVIATE_BRIEF = "The $name class" \
|
||||
"The $name widget" \
|
||||
"The $name file" \
|
||||
is \
|
||||
provides \
|
||||
specifies \
|
||||
contains \
|
||||
represents \
|
||||
a \
|
||||
an \
|
||||
the
|
||||
|
||||
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
|
||||
# doxygen will generate a detailed section even if there is only a brief
|
||||
@@ -125,7 +143,7 @@ ALWAYS_DETAILED_SEC = NO
|
||||
# operators of the base classes will not be shown.
|
||||
# The default value is: NO.
|
||||
|
||||
INLINE_INHERITED_MEMB = YES
|
||||
INLINE_INHERITED_MEMB = NO
|
||||
|
||||
# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
|
||||
# before files name in the file list and in the header files. If set to NO the
|
||||
@@ -144,7 +162,7 @@ FULL_PATH_NAMES = YES
|
||||
# will be relative from the directory where doxygen is started.
|
||||
# This tag requires that the tag FULL_PATH_NAMES is set to YES.
|
||||
|
||||
STRIP_FROM_PATH =
|
||||
STRIP_FROM_PATH = src
|
||||
|
||||
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
|
||||
# path mentioned in the documentation of a class, which tells the reader which
|
||||
@@ -153,7 +171,7 @@ STRIP_FROM_PATH =
|
||||
# specify the list of include paths that are normally passed to the compiler
|
||||
# using the -I flag.
|
||||
|
||||
STRIP_FROM_INC_PATH =
|
||||
STRIP_FROM_INC_PATH = src
|
||||
|
||||
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
|
||||
# less readable) file names. This can be useful is your file systems doesn't
|
||||
@@ -220,13 +238,13 @@ TAB_SIZE = 4
|
||||
# "Side Effects:". You can put \n's in the value part of an alias to insert
|
||||
# newlines.
|
||||
|
||||
ALIASES =
|
||||
ALIASES =
|
||||
|
||||
# This tag can be used to specify a number of word-keyword mappings (TCL only).
|
||||
# A mapping has the form "name=value". For example adding "class=itcl::class"
|
||||
# will allow you to use the command class in the itcl::class meaning.
|
||||
|
||||
TCL_SUBST =
|
||||
TCL_SUBST =
|
||||
|
||||
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
|
||||
# only. Doxygen will then generate output that is more tailored for C. For
|
||||
@@ -261,16 +279,19 @@ OPTIMIZE_OUTPUT_VHDL = NO
|
||||
# extension. Doxygen has a built-in mapping, but you can override or extend it
|
||||
# using this tag. The format is ext=language, where ext is a file extension, and
|
||||
# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
|
||||
# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
|
||||
# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
|
||||
# (default is Fortran), use: inc=Fortran f=C.
|
||||
# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
|
||||
# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
|
||||
# Fortran. In the later case the parser tries to guess whether the code is fixed
|
||||
# or free formatted code, this is the default for Fortran type files), VHDL. For
|
||||
# instance to make doxygen treat .inc files as Fortran files (default is PHP),
|
||||
# and .f files as C (default is Fortran), use: inc=Fortran f=C.
|
||||
#
|
||||
# Note For files without extension you can use no_extension as a placeholder.
|
||||
#
|
||||
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
|
||||
# the files are not read by doxygen.
|
||||
|
||||
EXTENSION_MAPPING =
|
||||
EXTENSION_MAPPING =
|
||||
|
||||
# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
|
||||
# according to the Markdown format, which allows for more readable
|
||||
@@ -298,7 +319,7 @@ AUTOLINK_SUPPORT = YES
|
||||
# diagrams that involve STL classes more complete and accurate.
|
||||
# The default value is: NO.
|
||||
|
||||
BUILTIN_STL_SUPPORT = YES
|
||||
BUILTIN_STL_SUPPORT = NO
|
||||
|
||||
# If you use Microsoft's C++/CLI language, you should set this option to YES to
|
||||
# enable parsing support.
|
||||
@@ -398,7 +419,7 @@ LOOKUP_CACHE_SIZE = 0
|
||||
# normally produced when WARNINGS is set to YES.
|
||||
# The default value is: NO.
|
||||
|
||||
EXTRACT_ALL = YES
|
||||
EXTRACT_ALL = NO
|
||||
|
||||
# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
|
||||
# be included in the documentation.
|
||||
@@ -487,7 +508,7 @@ INTERNAL_DOCS = NO
|
||||
# and Mac users are advised to set this option to NO.
|
||||
# The default value is: system dependent.
|
||||
|
||||
CASE_SENSE_NAMES = YES
|
||||
CASE_SENSE_NAMES = NO
|
||||
|
||||
# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
|
||||
# their full class and namespace scopes in the documentation. If set to YES the
|
||||
@@ -606,7 +627,7 @@ GENERATE_DEPRECATEDLIST= YES
|
||||
# sections, marked by \if <section_label> ... \endif and \cond <section_label>
|
||||
# ... \endcond blocks.
|
||||
|
||||
ENABLED_SECTIONS =
|
||||
ENABLED_SECTIONS =
|
||||
|
||||
# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
|
||||
# initial value of a variable or macro / define can have for it to appear in the
|
||||
@@ -648,7 +669,7 @@ SHOW_NAMESPACES = YES
|
||||
# by doxygen. Whatever the program writes to standard output is used as the file
|
||||
# version. For an example see the documentation.
|
||||
|
||||
FILE_VERSION_FILTER =
|
||||
FILE_VERSION_FILTER =
|
||||
|
||||
# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
|
||||
# by doxygen. The layout file controls the global structure of the generated
|
||||
@@ -661,7 +682,7 @@ FILE_VERSION_FILTER =
|
||||
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
|
||||
# tag is left empty.
|
||||
|
||||
LAYOUT_FILE =
|
||||
LAYOUT_FILE =
|
||||
|
||||
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
|
||||
# the reference definitions. This must be a list of .bib files. The .bib
|
||||
@@ -669,10 +690,9 @@ LAYOUT_FILE =
|
||||
# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
|
||||
# For LaTeX the style of the bibliography can be controlled using
|
||||
# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
|
||||
# search path. Do not use file names with spaces, bibtex cannot handle them. See
|
||||
# also \cite for info how to create references.
|
||||
# search path. See also \cite for info how to create references.
|
||||
|
||||
CITE_BIB_FILES =
|
||||
CITE_BIB_FILES =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to warning and progress messages
|
||||
@@ -699,7 +719,7 @@ WARNINGS = YES
|
||||
# will automatically be disabled.
|
||||
# The default value is: YES.
|
||||
|
||||
WARN_IF_UNDOCUMENTED = YES
|
||||
WARN_IF_UNDOCUMENTED = NO
|
||||
|
||||
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
|
||||
# potential errors in the documentation, such as not documenting some parameters
|
||||
@@ -731,7 +751,7 @@ WARN_FORMAT = "$file:$line: $text"
|
||||
# messages should be written. If left blank the output is written to standard
|
||||
# error (stderr).
|
||||
|
||||
WARN_LOGFILE =
|
||||
WARN_LOGFILE =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the input files
|
||||
@@ -743,7 +763,12 @@ WARN_LOGFILE =
|
||||
# spaces.
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = include
|
||||
INPUT = src/pv \
|
||||
documentation/mainpage.dox \
|
||||
src/copy/pv \
|
||||
src/misc/pv \
|
||||
src/json/pv \
|
||||
documentation/release_notes.dox
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
@@ -763,13 +788,13 @@ INPUT_ENCODING = UTF-8
|
||||
# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
|
||||
# *.qsf, *.as and *.js.
|
||||
|
||||
FILE_PATTERNS =
|
||||
FILE_PATTERNS =
|
||||
|
||||
# The RECURSIVE tag can be used to specify whether or not subdirectories should
|
||||
# be searched for input files as well.
|
||||
# The default value is: NO.
|
||||
|
||||
RECURSIVE = YES
|
||||
RECURSIVE = NO
|
||||
|
||||
# The EXCLUDE tag can be used to specify files and/or directories that should be
|
||||
# excluded from the INPUT source files. This way you can easily exclude a
|
||||
@@ -778,7 +803,7 @@ RECURSIVE = YES
|
||||
# Note that relative paths are relative to the directory from which doxygen is
|
||||
# run.
|
||||
|
||||
EXCLUDE =
|
||||
EXCLUDE =
|
||||
|
||||
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
|
||||
# directories that are symbolic links (a Unix file system feature) are excluded
|
||||
@@ -794,7 +819,7 @@ EXCLUDE_SYMLINKS = NO
|
||||
# Note that the wildcards are matched against the file with absolute path, so to
|
||||
# exclude all test directories for example use the pattern */test/*
|
||||
|
||||
EXCLUDE_PATTERNS =
|
||||
EXCLUDE_PATTERNS =
|
||||
|
||||
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
|
||||
# (namespaces, classes, functions, etc.) that should be excluded from the
|
||||
@@ -805,20 +830,20 @@ EXCLUDE_PATTERNS =
|
||||
# Note that the wildcards are matched against the file with absolute path, so to
|
||||
# exclude all test directories use the pattern */test/*
|
||||
|
||||
EXCLUDE_SYMBOLS =
|
||||
EXCLUDE_SYMBOLS =
|
||||
|
||||
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
|
||||
# that contain example code fragments that are included (see the \include
|
||||
# command).
|
||||
|
||||
EXAMPLE_PATH =
|
||||
EXAMPLE_PATH = examples
|
||||
|
||||
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
|
||||
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
|
||||
# *.h) to filter out the source-files in the directories. If left blank all
|
||||
# files are included.
|
||||
|
||||
EXAMPLE_PATTERNS =
|
||||
EXAMPLE_PATTERNS = *.cpp
|
||||
|
||||
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
|
||||
# searched for input files to be used with the \include or \dontinclude commands
|
||||
@@ -831,7 +856,7 @@ EXAMPLE_RECURSIVE = NO
|
||||
# that contain images that are to be included in the documentation (see the
|
||||
# \image command).
|
||||
|
||||
IMAGE_PATH =
|
||||
IMAGE_PATH =
|
||||
|
||||
# The INPUT_FILTER tag can be used to specify a program that doxygen should
|
||||
# invoke to filter for each input file. Doxygen will invoke the filter program
|
||||
@@ -848,7 +873,7 @@ IMAGE_PATH =
|
||||
# code is scanned, but not when the output code is generated. If lines are added
|
||||
# or removed, the anchors will not be placed correctly.
|
||||
|
||||
INPUT_FILTER =
|
||||
INPUT_FILTER =
|
||||
|
||||
# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
|
||||
# basis. Doxygen will compare the file name with each pattern and apply the
|
||||
@@ -857,7 +882,7 @@ INPUT_FILTER =
|
||||
# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
|
||||
# patterns match the file name, INPUT_FILTER is applied.
|
||||
|
||||
FILTER_PATTERNS =
|
||||
FILTER_PATTERNS =
|
||||
|
||||
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
|
||||
# INPUT_FILTER ) will also be used to filter the input files that are used for
|
||||
@@ -872,14 +897,14 @@ FILTER_SOURCE_FILES = NO
|
||||
# *.ext= (so without naming a filter).
|
||||
# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
|
||||
|
||||
FILTER_SOURCE_PATTERNS =
|
||||
FILTER_SOURCE_PATTERNS =
|
||||
|
||||
# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
|
||||
# is part of the input, its contents will be placed on the main page
|
||||
# (index.html). This can be useful if you have a project on for instance GitHub
|
||||
# and want to reuse the introduction page also for the doxygen output.
|
||||
|
||||
USE_MDFILE_AS_MAINPAGE =
|
||||
USE_MDFILE_AS_MAINPAGE =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to source browsing
|
||||
@@ -911,13 +936,13 @@ STRIP_CODE_COMMENTS = YES
|
||||
# function all documented functions referencing it will be listed.
|
||||
# The default value is: NO.
|
||||
|
||||
REFERENCED_BY_RELATION = YES
|
||||
REFERENCED_BY_RELATION = NO
|
||||
|
||||
# If the REFERENCES_RELATION tag is set to YES then for each documented function
|
||||
# all documented entities called/used by that function will be listed.
|
||||
# The default value is: NO.
|
||||
|
||||
REFERENCES_RELATION = YES
|
||||
REFERENCES_RELATION = NO
|
||||
|
||||
# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
|
||||
# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
|
||||
@@ -967,6 +992,25 @@ USE_HTAGS = NO
|
||||
|
||||
VERBATIM_HEADERS = YES
|
||||
|
||||
# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the
|
||||
# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
|
||||
# cost of reduced performance. This can be particularly helpful with template
|
||||
# rich C++ code for which doxygen's built-in parser lacks the necessary type
|
||||
# information.
|
||||
# Note: The availability of this option depends on whether or not doxygen was
|
||||
# compiled with the --with-libclang option.
|
||||
# The default value is: NO.
|
||||
|
||||
CLANG_ASSISTED_PARSING = YES
|
||||
|
||||
# If clang assisted parsing is enabled you can provide the compiler with command
|
||||
# line options that you would normally use when invoking the compiler. Note that
|
||||
# the include paths will already be set by doxygen for the files and directories
|
||||
# specified with INPUT and INCLUDE_PATH.
|
||||
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
|
||||
|
||||
CLANG_OPTIONS =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the alphabetical class index
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -976,7 +1020,7 @@ VERBATIM_HEADERS = YES
|
||||
# classes, structs, unions or interfaces.
|
||||
# The default value is: YES.
|
||||
|
||||
ALPHABETICAL_INDEX = NO
|
||||
ALPHABETICAL_INDEX = YES
|
||||
|
||||
# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
|
||||
# which the alphabetical index list will be split.
|
||||
@@ -991,7 +1035,7 @@ COLS_IN_ALPHA_INDEX = 5
|
||||
# while generating the index headers.
|
||||
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
|
||||
|
||||
IGNORE_PREFIX =
|
||||
IGNORE_PREFIX =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the HTML output
|
||||
@@ -1008,7 +1052,7 @@ GENERATE_HTML = YES
|
||||
# The default directory is: html.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_OUTPUT = documentation/html
|
||||
HTML_OUTPUT = html/doxygen
|
||||
|
||||
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
|
||||
# generated HTML page (for example: .htm, .php, .asp).
|
||||
@@ -1035,7 +1079,7 @@ HTML_FILE_EXTENSION = .html
|
||||
# of the possible markers and block names see the documentation.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_HEADER =
|
||||
HTML_HEADER =
|
||||
|
||||
# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
|
||||
# generated HTML page. If the tag is left blank doxygen will generate a standard
|
||||
@@ -1045,7 +1089,7 @@ HTML_HEADER =
|
||||
# that doxygen normally uses.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_FOOTER =
|
||||
HTML_FOOTER =
|
||||
|
||||
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
|
||||
# sheet that is used by each HTML page. It can be used to fine-tune the look of
|
||||
@@ -1057,18 +1101,20 @@ HTML_FOOTER =
|
||||
# obsolete.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_STYLESHEET =
|
||||
HTML_STYLESHEET =
|
||||
|
||||
# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
|
||||
# defined cascading style sheet that is included after the standard style sheets
|
||||
# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
|
||||
# cascading style sheets that are included after the standard style sheets
|
||||
# created by doxygen. Using this option one can overrule certain style aspects.
|
||||
# This is preferred over using HTML_STYLESHEET since it does not replace the
|
||||
# standard style sheet and is therefor more robust against future updates.
|
||||
# Doxygen will copy the style sheet file to the output directory. For an example
|
||||
# see the documentation.
|
||||
# Doxygen will copy the style sheet files to the output directory.
|
||||
# Note: The order of the extra stylesheet files is of importance (e.g. the last
|
||||
# stylesheet in the list overrules the setting of the previous ones in the
|
||||
# list). For an example see the documentation.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_EXTRA_STYLESHEET =
|
||||
HTML_EXTRA_STYLESHEET =
|
||||
|
||||
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
|
||||
# other source files which should be copied to the HTML output directory. Note
|
||||
@@ -1078,7 +1124,7 @@ HTML_EXTRA_STYLESHEET =
|
||||
# files will be copied as-is; there are no commands or markers available.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_EXTRA_FILES = documentation/pvDataCPP.html
|
||||
HTML_EXTRA_FILES =
|
||||
|
||||
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
|
||||
# will adjust the colors in the stylesheet and background images according to
|
||||
@@ -1206,7 +1252,7 @@ GENERATE_HTMLHELP = NO
|
||||
# written to the html output directory.
|
||||
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
|
||||
|
||||
CHM_FILE =
|
||||
CHM_FILE =
|
||||
|
||||
# The HHC_LOCATION tag can be used to specify the location (absolute path
|
||||
# including file name) of the HTML help compiler ( hhc.exe). If non-empty
|
||||
@@ -1214,7 +1260,7 @@ CHM_FILE =
|
||||
# The file has to be specified with full path.
|
||||
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
|
||||
|
||||
HHC_LOCATION =
|
||||
HHC_LOCATION =
|
||||
|
||||
# The GENERATE_CHI flag controls if a separate .chi index file is generated (
|
||||
# YES) or that it should be included in the master .chm file ( NO).
|
||||
@@ -1227,10 +1273,11 @@ GENERATE_CHI = NO
|
||||
# and project file content.
|
||||
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
|
||||
|
||||
CHM_INDEX_ENCODING =
|
||||
CHM_INDEX_ENCODING =
|
||||
|
||||
# The BINARY_TOC flag controls whether a binary table of contents is generated (
|
||||
# YES) or a normal table of contents ( NO) in the .chm file.
|
||||
# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it
|
||||
# enables the Previous and Next buttons.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
|
||||
|
||||
@@ -1257,7 +1304,7 @@ GENERATE_QHP = NO
|
||||
# the HTML output folder.
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QCH_FILE =
|
||||
QCH_FILE =
|
||||
|
||||
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
|
||||
# Project output. For more information please see Qt Help Project / Namespace
|
||||
@@ -1282,7 +1329,7 @@ QHP_VIRTUAL_FOLDER = doc
|
||||
# filters).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_CUST_FILTER_NAME =
|
||||
QHP_CUST_FILTER_NAME =
|
||||
|
||||
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
|
||||
# custom filter to add. For more information please see Qt Help Project / Custom
|
||||
@@ -1290,21 +1337,21 @@ QHP_CUST_FILTER_NAME =
|
||||
# filters).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_CUST_FILTER_ATTRS =
|
||||
QHP_CUST_FILTER_ATTRS =
|
||||
|
||||
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
|
||||
# project's filter section matches. Qt Help Project / Filter Attributes (see:
|
||||
# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_SECT_FILTER_ATTRS =
|
||||
QHP_SECT_FILTER_ATTRS =
|
||||
|
||||
# The QHG_LOCATION tag can be used to specify the location of Qt's
|
||||
# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
|
||||
# generated .qhp file.
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHG_LOCATION =
|
||||
QHG_LOCATION =
|
||||
|
||||
# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
|
||||
# generated, together with the HTML files, they form an Eclipse help plugin. To
|
||||
@@ -1437,7 +1484,7 @@ MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
|
||||
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
MATHJAX_EXTENSIONS =
|
||||
MATHJAX_EXTENSIONS =
|
||||
|
||||
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
|
||||
# of code that will be used on startup of the MathJax code. See the MathJax site
|
||||
@@ -1445,7 +1492,7 @@ MATHJAX_EXTENSIONS =
|
||||
# example see the documentation.
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
MATHJAX_CODEFILE =
|
||||
MATHJAX_CODEFILE =
|
||||
|
||||
# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
|
||||
# the HTML output. The underlying search engine uses javascript and DHTML and
|
||||
@@ -1466,15 +1513,15 @@ MATHJAX_CODEFILE =
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
SEARCHENGINE = NO
|
||||
SEARCHENGINE = YES
|
||||
|
||||
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
|
||||
# implemented using a web server instead of a web client using Javascript. There
|
||||
# are two flavours of web server based searching depending on the
|
||||
# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
|
||||
# searching and an index file used by the script. When EXTERNAL_SEARCH is
|
||||
# enabled the indexing and searching needs to be provided by external tools. See
|
||||
# the section "External Indexing and Searching" for details.
|
||||
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
|
||||
# setting. When disabled, doxygen will generate a PHP script for searching and
|
||||
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
|
||||
# and searching needs to be provided by external tools. See the section
|
||||
# "External Indexing and Searching" for details.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag SEARCHENGINE is set to YES.
|
||||
|
||||
@@ -1505,7 +1552,7 @@ EXTERNAL_SEARCH = NO
|
||||
# Searching" for details.
|
||||
# This tag requires that the tag SEARCHENGINE is set to YES.
|
||||
|
||||
SEARCHENGINE_URL =
|
||||
SEARCHENGINE_URL =
|
||||
|
||||
# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
|
||||
# search data is written to a file for indexing by an external tool. With the
|
||||
@@ -1521,7 +1568,7 @@ SEARCHDATA_FILE = searchdata.xml
|
||||
# projects and redirect the results back to the right project.
|
||||
# This tag requires that the tag SEARCHENGINE is set to YES.
|
||||
|
||||
EXTERNAL_SEARCH_ID =
|
||||
EXTERNAL_SEARCH_ID =
|
||||
|
||||
# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
|
||||
# projects other than the one defined by this configuration file, but that are
|
||||
@@ -1531,7 +1578,7 @@ EXTERNAL_SEARCH_ID =
|
||||
# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
|
||||
# This tag requires that the tag SEARCHENGINE is set to YES.
|
||||
|
||||
EXTRA_SEARCH_MAPPINGS =
|
||||
EXTRA_SEARCH_MAPPINGS =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the LaTeX output
|
||||
@@ -1583,7 +1630,7 @@ COMPACT_LATEX = NO
|
||||
# The default value is: a4.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
PAPER_TYPE = a4wide
|
||||
PAPER_TYPE = a4
|
||||
|
||||
# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
|
||||
# that should be included in the LaTeX output. To get the times font for
|
||||
@@ -1592,7 +1639,7 @@ PAPER_TYPE = a4wide
|
||||
# If left blank no extra packages will be included.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
EXTRA_PACKAGES =
|
||||
EXTRA_PACKAGES =
|
||||
|
||||
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
|
||||
# generated LaTeX document. The header should contain everything until the first
|
||||
@@ -1602,22 +1649,24 @@ EXTRA_PACKAGES =
|
||||
#
|
||||
# Note: Only use a user-defined header if you know what you are doing! The
|
||||
# following commands have a special meaning inside the header: $title,
|
||||
# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
|
||||
# replace them by respectively the title of the page, the current date and time,
|
||||
# only the current date, the version number of doxygen, the project name (see
|
||||
# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
|
||||
# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
|
||||
# $projectbrief, $projectlogo. Doxygen will replace $title with the empy string,
|
||||
# for the replacement values of the other commands the user is refered to
|
||||
# HTML_HEADER.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_HEADER =
|
||||
LATEX_HEADER =
|
||||
|
||||
# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
|
||||
# generated LaTeX document. The footer should contain everything after the last
|
||||
# chapter. If it is left blank doxygen will generate a standard footer.
|
||||
# chapter. If it is left blank doxygen will generate a standard footer. See
|
||||
# LATEX_HEADER for more information on how to generate a default footer and what
|
||||
# special commands can be used inside the footer.
|
||||
#
|
||||
# Note: Only use a user-defined footer if you know what you are doing!
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_FOOTER =
|
||||
LATEX_FOOTER =
|
||||
|
||||
# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
|
||||
# other source files which should be copied to the LATEX_OUTPUT output
|
||||
@@ -1625,7 +1674,7 @@ LATEX_FOOTER =
|
||||
# markers available.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_EXTRA_FILES =
|
||||
LATEX_EXTRA_FILES =
|
||||
|
||||
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
|
||||
# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
|
||||
@@ -1634,15 +1683,15 @@ LATEX_EXTRA_FILES =
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
PDF_HYPERLINKS = NO
|
||||
PDF_HYPERLINKS = YES
|
||||
|
||||
# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
|
||||
# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
|
||||
# the PDF file directly from the LaTeX files. Set this option to YES to get a
|
||||
# higher quality PDF documentation.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
USE_PDFLATEX = NO
|
||||
USE_PDFLATEX = YES
|
||||
|
||||
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
|
||||
# command to the generated LaTeX files. This will instruct LaTeX to keep running
|
||||
@@ -1725,14 +1774,14 @@ RTF_HYPERLINKS = NO
|
||||
# default style sheet that doxygen normally uses.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
RTF_STYLESHEET_FILE =
|
||||
RTF_STYLESHEET_FILE =
|
||||
|
||||
# Set optional variables used in the generation of an RTF document. Syntax is
|
||||
# similar to doxygen's config file. A template extensions file can be generated
|
||||
# using doxygen -e rtf extensionFile.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
RTF_EXTENSIONS_FILE =
|
||||
RTF_EXTENSIONS_FILE =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the man page output
|
||||
@@ -1762,6 +1811,13 @@ MAN_OUTPUT = man
|
||||
|
||||
MAN_EXTENSION = .3
|
||||
|
||||
# The MAN_SUBDIR tag determines the name of the directory created within
|
||||
# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
|
||||
# MAN_EXTENSION with the initial . removed.
|
||||
# This tag requires that the tag GENERATE_MAN is set to YES.
|
||||
|
||||
MAN_SUBDIR =
|
||||
|
||||
# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
|
||||
# will generate one additional man file for each entity documented in the real
|
||||
# man page(s). These additional files only source the real man page, but without
|
||||
@@ -1789,18 +1845,6 @@ GENERATE_XML = NO
|
||||
|
||||
XML_OUTPUT = xml
|
||||
|
||||
# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a
|
||||
# validating XML parser to check the syntax of the XML files.
|
||||
# This tag requires that the tag GENERATE_XML is set to YES.
|
||||
|
||||
XML_SCHEMA =
|
||||
|
||||
# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
|
||||
# validating XML parser to check the syntax of the XML files.
|
||||
# This tag requires that the tag GENERATE_XML is set to YES.
|
||||
|
||||
XML_DTD =
|
||||
|
||||
# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
|
||||
# listings (including syntax highlighting and cross-referencing information) to
|
||||
# the XML output. Note that enabling this will significantly increase the size
|
||||
@@ -1828,6 +1872,15 @@ GENERATE_DOCBOOK = NO
|
||||
|
||||
DOCBOOK_OUTPUT = docbook
|
||||
|
||||
# If the DOCBOOK_PROGRAMLISTING tag is set to YES doxygen will include the
|
||||
# program listings (including syntax highlighting and cross-referencing
|
||||
# information) to the DOCBOOK output. Note that enabling this will significantly
|
||||
# increase the size of the DOCBOOK output.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
|
||||
|
||||
DOCBOOK_PROGRAMLISTING = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options for the AutoGen Definitions output
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -1876,7 +1929,7 @@ PERLMOD_PRETTY = YES
|
||||
# overwrite each other's variables.
|
||||
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
|
||||
|
||||
PERLMOD_MAKEVAR_PREFIX =
|
||||
PERLMOD_MAKEVAR_PREFIX =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the preprocessor
|
||||
@@ -1895,7 +1948,7 @@ ENABLE_PREPROCESSING = YES
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
MACRO_EXPANSION = NO
|
||||
MACRO_EXPANSION = YES
|
||||
|
||||
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
|
||||
# the macro expansion is limited to the macros specified with the PREDEFINED and
|
||||
@@ -1917,7 +1970,7 @@ SEARCH_INCLUDES = YES
|
||||
# preprocessor.
|
||||
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
|
||||
|
||||
INCLUDE_PATH =
|
||||
INCLUDE_PATH = src/misc src ../../include/
|
||||
|
||||
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
|
||||
# patterns (like *.h and *.hpp) to filter out the header-files in the
|
||||
@@ -1925,7 +1978,7 @@ INCLUDE_PATH =
|
||||
# used.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
INCLUDE_FILE_PATTERNS =
|
||||
INCLUDE_FILE_PATTERNS =
|
||||
|
||||
# The PREDEFINED tag can be used to specify one or more macro names that are
|
||||
# defined before the preprocessor is started (similar to the -D option of e.g.
|
||||
@@ -1935,7 +1988,7 @@ INCLUDE_FILE_PATTERNS =
|
||||
# recursively expanded use the := operator instead of the = operator.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
PREDEFINED =
|
||||
PREDEFINED =
|
||||
|
||||
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
|
||||
# tag can be used to specify a list of macro names that should be expanded. The
|
||||
@@ -1947,14 +2000,14 @@ PREDEFINED =
|
||||
EXPAND_AS_DEFINED =
|
||||
|
||||
# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
|
||||
# remove all refrences to function-like macros that are alone on a line, have an
|
||||
# all uppercase name, and do not end with a semicolon. Such function macros are
|
||||
# typically used for boiler-plate code, and will confuse the parser if not
|
||||
# remove all references to function-like macros that are alone on a line, have
|
||||
# an all uppercase name, and do not end with a semicolon. Such function macros
|
||||
# are typically used for boiler-plate code, and will confuse the parser if not
|
||||
# removed.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
SKIP_FUNCTION_MACROS = YES
|
||||
SKIP_FUNCTION_MACROS = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to external references
|
||||
@@ -1969,17 +2022,17 @@ SKIP_FUNCTION_MACROS = YES
|
||||
# where loc1 and loc2 can be relative or absolute paths or URLs. See the
|
||||
# section "Linking to external documentation" for more information about the use
|
||||
# of tag files.
|
||||
# Note: Each tag file must have an unique name (where the name does NOT include
|
||||
# Note: Each tag file must have a unique name (where the name does NOT include
|
||||
# the path). If a tag file is not located in the directory in which doxygen is
|
||||
# run, you must also specify the path to the tagfile here.
|
||||
|
||||
TAGFILES =
|
||||
TAGFILES = "libstdc++.tag = http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen"
|
||||
|
||||
# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
|
||||
# tag file that is based on the input files it reads. See section "Linking to
|
||||
# external documentation" for more information about the usage of tag files.
|
||||
|
||||
GENERATE_TAGFILE =
|
||||
GENERATE_TAGFILE = pvdata.tag
|
||||
|
||||
# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
|
||||
# class index. If set to NO only the inherited external classes will be listed.
|
||||
@@ -1992,14 +2045,14 @@ ALLEXTERNALS = NO
|
||||
# listed.
|
||||
# The default value is: YES.
|
||||
|
||||
EXTERNAL_GROUPS = YES
|
||||
EXTERNAL_GROUPS = NO
|
||||
|
||||
# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
|
||||
# the related pages index. If set to NO, only the current project's pages will
|
||||
# be listed.
|
||||
# The default value is: YES.
|
||||
|
||||
EXTERNAL_PAGES = YES
|
||||
EXTERNAL_PAGES = NO
|
||||
|
||||
# The PERL_PATH should be the absolute path and name of the perl script
|
||||
# interpreter (i.e. the result of 'which perl').
|
||||
@@ -2027,14 +2080,14 @@ CLASS_DIAGRAMS = YES
|
||||
# the mscgen tool resides. If left empty the tool is assumed to be found in the
|
||||
# default search path.
|
||||
|
||||
MSCGEN_PATH =
|
||||
MSCGEN_PATH =
|
||||
|
||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||
# then run dia to produce the diagram and insert it in the documentation. The
|
||||
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||
# If left empty dia is assumed to be found in the default search path.
|
||||
|
||||
DIA_PATH =
|
||||
DIA_PATH =
|
||||
|
||||
# If set to YES, the inheritance and collaboration graphs will hide inheritance
|
||||
# and usage relations if the target is undocumented or is not a class.
|
||||
@@ -2047,9 +2100,9 @@ HIDE_UNDOC_RELATIONS = YES
|
||||
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
|
||||
# Bell Labs. The other options in this section have no effect if this option is
|
||||
# set to NO
|
||||
# The default value is: NO.
|
||||
# The default value is: YES.
|
||||
|
||||
HAVE_DOT = NO
|
||||
HAVE_DOT = YES
|
||||
|
||||
# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
|
||||
# to run in parallel. When set to 0 doxygen will base this on the number of
|
||||
@@ -2061,7 +2114,7 @@ HAVE_DOT = NO
|
||||
|
||||
DOT_NUM_THREADS = 0
|
||||
|
||||
# When you want a differently looking font n the dot files that doxygen
|
||||
# When you want a differently looking font in the dot files that doxygen
|
||||
# generates you can specify the font name using DOT_FONTNAME. You need to make
|
||||
# sure dot is able to find the font, which can be done by putting it in a
|
||||
# standard location or by setting the DOTFONTPATH environment variable or by
|
||||
@@ -2083,7 +2136,7 @@ DOT_FONTSIZE = 10
|
||||
# the path where dot can find it using this tag.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_FONTPATH =
|
||||
DOT_FONTPATH =
|
||||
|
||||
# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
|
||||
# each documented class showing the direct and indirect inheritance relations.
|
||||
@@ -2199,7 +2252,9 @@ DIRECTORY_GRAPH = YES
|
||||
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
|
||||
# to make the SVG files visible in IE 9+ (other browsers do not have this
|
||||
# requirement).
|
||||
# Possible values are: png, jpg, gif and svg.
|
||||
# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
|
||||
# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
|
||||
# gif:cairo:gd, gif:gd, gif:gd:gd and svg.
|
||||
# The default value is: png.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
@@ -2221,26 +2276,35 @@ INTERACTIVE_SVG = NO
|
||||
# found. If left blank, it is assumed the dot tool can be found in the path.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_PATH =
|
||||
DOT_PATH =
|
||||
|
||||
# The DOTFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain dot files that are included in the documentation (see the \dotfile
|
||||
# command).
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOTFILE_DIRS =
|
||||
DOTFILE_DIRS =
|
||||
|
||||
# The MSCFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain msc files that are included in the documentation (see the \mscfile
|
||||
# command).
|
||||
|
||||
MSCFILE_DIRS =
|
||||
MSCFILE_DIRS =
|
||||
|
||||
# The DIAFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain dia files that are included in the documentation (see the \diafile
|
||||
# command).
|
||||
|
||||
DIAFILE_DIRS =
|
||||
DIAFILE_DIRS =
|
||||
|
||||
# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
|
||||
# path where java can find the plantuml.jar file. If left blank, it is assumed
|
||||
# PlantUML is not used or called during a preprocessing step. Doxygen will
|
||||
# generate a warning when it encounters a \startuml command in this case and
|
||||
# will not generate output for the diagram.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
PLANTUML_JAR_PATH =
|
||||
|
||||
# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
|
||||
# that will be shown in the graph. If the number of nodes in a graph becomes
|
||||
|
||||
61
README.md
61
README.md
@@ -1,57 +1,16 @@
|
||||
pvaDataCPP
|
||||
==========
|
||||
# pvaDataCPP
|
||||
|
||||
pvDataCPP is a set of data types and utilities that form part of the EPICS V4 project.
|
||||
The EPICS **pvData** API provides a set of classes and utilities that form the core of the EPICS PVA implementation.
|
||||
|
||||
The pvDataCPP module is a part of the EPICS software toolkit that implements pvData structures as C++ class objects.
|
||||
|
||||
Further Info
|
||||
------------
|
||||
## Links
|
||||
|
||||
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).
|
||||
- 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/).
|
||||
|
||||
## 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 +1,12 @@
|
||||
EPICS_PVD_MAJOR_VERSION = 7
|
||||
# Version number for the PV Data API and shared library
|
||||
|
||||
EPICS_PVD_MAJOR_VERSION = 8
|
||||
EPICS_PVD_MINOR_VERSION = 0
|
||||
EPICS_PVD_MAINTENANCE_VERSION = 1
|
||||
EPICS_PVD_DEVELOPMENT_FLAG = 1
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
EPICS_PVD_DEVELOPMENT_FLAG = 0
|
||||
|
||||
# Immediately after a release the MAINTENANCE_VERSION
|
||||
# will be incremented and the DEVELOPMENT_FLAG set to 1
|
||||
|
||||
@@ -20,9 +20,14 @@ 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
|
||||
|
||||
@@ -26,8 +26,7 @@
|
||||
#MYMODULE = $(MODULES)/my-module
|
||||
|
||||
# If building the EPICS modules individually, set these:
|
||||
#EPICS_LIBCOM = $(MODULES)/libcom-3.17.0
|
||||
#EPICS_BASE = $(MODULES)/core-7.0.1
|
||||
#EPICS_BASE = /path/to/base
|
||||
|
||||
# Set RULES here if you want to use build rules from elsewhere:
|
||||
#RULES = $(MODULES)/build-rules
|
||||
|
||||
@@ -38,7 +38,7 @@ PROJECT_NAME = "PVData C++"
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER =
|
||||
PROJECT_NUMBER = 8.0.0
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
@@ -319,7 +319,7 @@ AUTOLINK_SUPPORT = YES
|
||||
# diagrams that involve STL classes more complete and accurate.
|
||||
# The default value is: NO.
|
||||
|
||||
BUILTIN_STL_SUPPORT = YES
|
||||
BUILTIN_STL_SUPPORT = NO
|
||||
|
||||
# If you use Microsoft's C++/CLI language, you should set this option to YES to
|
||||
# enable parsing support.
|
||||
@@ -764,11 +764,11 @@ WARN_LOGFILE =
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = ../src/pv \
|
||||
./mainpage.h \
|
||||
./mainpage.dox \
|
||||
../src/copy/pv \
|
||||
../src/misc/pv \
|
||||
../src/json/pv \
|
||||
./release_notes.h
|
||||
./release_notes.dox
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
@@ -1001,7 +1001,7 @@ VERBATIM_HEADERS = YES
|
||||
# compiled with the --with-libclang option.
|
||||
# The default value is: NO.
|
||||
|
||||
CLANG_ASSISTED_PARSING = NO
|
||||
CLANG_ASSISTED_PARSING = YES
|
||||
|
||||
# If clang assisted parsing is enabled you can provide the compiler with command
|
||||
# line options that you would normally use when invoking the compiler. Note that
|
||||
@@ -1948,7 +1948,7 @@ ENABLE_PREPROCESSING = YES
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
MACRO_EXPANSION = NO
|
||||
MACRO_EXPANSION = YES
|
||||
|
||||
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
|
||||
# the macro expansion is limited to the macros specified with the PREDEFINED and
|
||||
@@ -1970,7 +1970,7 @@ SEARCH_INCLUDES = YES
|
||||
# preprocessor.
|
||||
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
|
||||
|
||||
INCLUDE_PATH =
|
||||
INCLUDE_PATH = ../src/misc ../src ../../../include/
|
||||
|
||||
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
|
||||
# patterns (like *.h and *.hpp) to filter out the header-files in the
|
||||
@@ -1997,7 +1997,7 @@ PREDEFINED =
|
||||
# definition found in the source code.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
EXPAND_AS_DEFINED =
|
||||
EXPAND_AS_DEFINED =
|
||||
|
||||
# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
|
||||
# remove all references to function-like macros that are alone on a line, have
|
||||
@@ -2007,7 +2007,7 @@ EXPAND_AS_DEFINED =
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
SKIP_FUNCTION_MACROS = YES
|
||||
SKIP_FUNCTION_MACROS = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to external references
|
||||
@@ -2026,7 +2026,7 @@ SKIP_FUNCTION_MACROS = YES
|
||||
# the path). If a tag file is not located in the directory in which doxygen is
|
||||
# run, you must also specify the path to the tagfile here.
|
||||
|
||||
TAGFILES = ../../../base-git/documentation/epics-base.tag
|
||||
TAGFILES = "libstdc++.tag = http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen"
|
||||
|
||||
# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
|
||||
# tag file that is based on the input files it reads. See section "Linking to
|
||||
@@ -2045,14 +2045,14 @@ ALLEXTERNALS = NO
|
||||
# listed.
|
||||
# The default value is: YES.
|
||||
|
||||
EXTERNAL_GROUPS = YES
|
||||
EXTERNAL_GROUPS = NO
|
||||
|
||||
# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
|
||||
# the related pages index. If set to NO, only the current project's pages will
|
||||
# be listed.
|
||||
# The default value is: YES.
|
||||
|
||||
EXTERNAL_PAGES = YES
|
||||
EXTERNAL_PAGES = NO
|
||||
|
||||
# The PERL_PATH should be the absolute path and name of the perl script
|
||||
# interpreter (i.e. the result of 'which perl').
|
||||
|
||||
@@ -3,11 +3,14 @@ all: gen
|
||||
clean:
|
||||
rm -rf doxygen_sqlite3.db html
|
||||
|
||||
gen:
|
||||
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
|
||||
|
||||
7
documentation/_static/css/custom.css
Normal file
7
documentation/_static/css/custom.css
Normal file
@@ -0,0 +1,7 @@
|
||||
.wy-side-nav-search {
|
||||
background-color: #18334B;
|
||||
}
|
||||
|
||||
.wy-side-nav-search input[type="text"] {
|
||||
border-color: #18334b;
|
||||
}
|
||||
78
documentation/conf.py
Normal file
78
documentation/conf.py
Normal file
@@ -0,0 +1,78 @@
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file only contains a selection of the most common options. For a full
|
||||
# list see the documentation:
|
||||
# http://www.sphinx-doc.org/en/master/config
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'EPICS Documentation'
|
||||
copyright = '2019, EPICS Controls.'
|
||||
author = 'EPICS'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.intersphinx',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
# Intersphinx links to subprojects
|
||||
intersphinx_mapping = {
|
||||
'how-tos': ('https://docs.epics-controls.org/projects/how-tos/en/latest', None),
|
||||
}
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
html_css_files = [
|
||||
'css/custom.css',
|
||||
]
|
||||
|
||||
master_doc = 'index'
|
||||
|
||||
html_theme_options = {
|
||||
'logo_only': True,
|
||||
}
|
||||
html_logo = "images/EPICS_white_logo_v02.png"
|
||||
|
||||
html_extra_path = ['../html']
|
||||
|
||||
|
||||
# -- Run Doxygen ------------------------------------------------------------
|
||||
|
||||
import subprocess
|
||||
subprocess.call('cd ..; mkdir -p html/doxygen; doxygen', shell=True)
|
||||
Binary file not shown.
BIN
documentation/images/EPICS_white_logo_v02.png
Normal file
BIN
documentation/images/EPICS_white_logo_v02.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
16
documentation/index.rst
Normal file
16
documentation/index.rst
Normal file
@@ -0,0 +1,16 @@
|
||||
pvData (C++) Library
|
||||
====================
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
EPICS Website <https://epics-controls.org>
|
||||
EPICS Documentation Home <https://docs.epics-controls.org>
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: pvDataCPP
|
||||
|
||||
Reference Manual and API Documentation <https://docs.epics-controls.org/projects/pvdata-cpp/en/latest/doxygen>
|
||||
Source Code Repository on GitHub <https://github.com/epics-base/pvDataCPP>
|
||||
@@ -3,7 +3,8 @@
|
||||
/**
|
||||
@mainpage pvDataCPP documentation
|
||||
|
||||
- [Download](https://sourceforge.net/projects/epics-pvdata/files/)
|
||||
- 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
|
||||
|
||||
@@ -23,33 +24,32 @@ See pv/pvData.h header.
|
||||
Define a structure type and create a container with default values.
|
||||
|
||||
@code
|
||||
epics::pvData::StructureConstPtr stype; // aka std::tr1::shared_ptr<const epics::pvData::Structure>
|
||||
stype = epics::pvData::getFieldCreate()->createFieldBuilder()
|
||||
->add("fld1", epics::pvData::pvInt)
|
||||
namespace pvd = epics::pvData;
|
||||
pvd::StructureConstPtr stype(pvd::FieldBuilder::begin()
|
||||
->add("fld1", pvd::pvInt)
|
||||
->addNestedStructure("sub")
|
||||
->add("fld2", epics::pvData::pvString)
|
||||
->add("fld2", pvd::pvString)
|
||||
->endNested()
|
||||
->createStructure();
|
||||
->createStructure());
|
||||
|
||||
epics::pvData::PVStructuretPtr value; // aka std::tr1::shared_ptr<epics::pvData::PVStructure>
|
||||
value = epics::pvData::getPVDataCreate()->createPVStructure(stype);
|
||||
pvd::PVStructuretPtr value(stype->build());
|
||||
|
||||
value->getSubField<epics::pvData::PVInt>("fld1")->put(4); // store integer 4
|
||||
value->getSubField<epics::pvData::PVScalar>("sub.fld2")->putFrom(4.2); // convert and store string "4.2"
|
||||
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 {
|
||||
epics::pvData::int32 fld1;
|
||||
pvd::int32 fld1;
|
||||
struct {
|
||||
std::string fld2;
|
||||
} sub;
|
||||
};
|
||||
stype value;
|
||||
value.fld1 = 4;
|
||||
value.fld2 = epics::pvData::castUnsafe<std::string>(4.2);
|
||||
value.fld2 = pvd::castUnsafe<std::string>(4.2);
|
||||
@endcode
|
||||
|
||||
*/
|
||||
@@ -2,46 +2,93 @@
|
||||
|
||||
@page release_notes Release Notes
|
||||
|
||||
Release 7.1.0 (UNRELEASED)
|
||||
Release 8.0.1 (Nov 2019)
|
||||
==========================
|
||||
|
||||
- Incompatible changes
|
||||
- Requires Base >= 3.15
|
||||
- Bug fixes
|
||||
|
||||
Release 8.0.0 (July 2019)
|
||||
=========================
|
||||
|
||||
- Deprecations
|
||||
- pv/localStaticLock.h
|
||||
- ByteBuffer::getArray()
|
||||
- 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 shared_vector::swap() for void specialization.
|
||||
- Changes in several Field sub-classes to return const ref. instead of a copy.
|
||||
- pv/localStaticLock.h
|
||||
- pv/pvCopy.h (see epics::pvData::PVRequestMapper)
|
||||
- Additions
|
||||
- shared_vector add c++11 move and construct for initializer list.
|
||||
- Add AnyScalar::clear()
|
||||
- Add ctor AnyScalar(ScalarType, const void*) to allow construction from an untyped buffer.
|
||||
- Add Timer::close()
|
||||
- Allow castUnsafe() from const char* without first allocating a std::string.
|
||||
- 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
|
||||
- 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
|
||||
- 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 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
|
||||
@@ -3,7 +3,6 @@
|
||||
SRC_DIRS += $(PVDATA_SRC)/copy
|
||||
|
||||
INC += pv/createRequest.h
|
||||
INC += pv/pvCopy.h
|
||||
|
||||
LIBSRCS += createRequest.cpp
|
||||
LIBSRCS += pvCopy.cpp
|
||||
LIBSRCS += requestmapper.cpp
|
||||
|
||||
@@ -318,7 +318,7 @@ struct CreateRequestImpl {
|
||||
if (!request.empty()) removeBlanks(request);
|
||||
if (request.empty())
|
||||
{
|
||||
return pvDataCreate->createPVStructure(fieldCreate->createStructure());
|
||||
return fieldCreate->createStructure()->build();
|
||||
}
|
||||
size_t offsetRecord = request.find("record[");
|
||||
size_t offsetField = request.find("field(");
|
||||
@@ -437,7 +437,7 @@ struct CreateRequestImpl {
|
||||
}
|
||||
StructureConstPtr structure = fieldCreate->createStructure(names, fields);
|
||||
if(!structure) throw std::invalid_argument("bad request " + crequest);
|
||||
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(structure);
|
||||
PVStructurePtr pvStructure = structure->build();
|
||||
for(size_t i=0; i<optionList.size(); ++i) {
|
||||
OptionPair pair = optionList[i];
|
||||
string name = pair.name;
|
||||
|
||||
@@ -8,14 +8,18 @@
|
||||
#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.
|
||||
*
|
||||
@@ -61,6 +65,168 @@ protected:
|
||||
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!)
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* CREATEREQUEST_H */
|
||||
|
||||
@@ -1,232 +0,0 @@
|
||||
/* 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(){}
|
||||
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 */
|
||||
@@ -1,649 +0,0 @@
|
||||
/* 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, ' ');
|
||||
}
|
||||
|
||||
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 PVCopyPtr();
|
||||
}
|
||||
} else if(pvStructure->getSubField<PVStructure>("field")) {
|
||||
pvStructure = pvRequest->getSubField<PVStructure>("field");
|
||||
}
|
||||
PVCopyPtr pvCopy(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 PVStructurePtr();
|
||||
}
|
||||
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 PVStructurePtr();
|
||||
}
|
||||
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 StructureConstPtr();
|
||||
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 StructureConstPtr();
|
||||
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 CopyMasterNodePtr();
|
||||
}
|
||||
|
||||
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 CopyMasterNodePtr();
|
||||
}
|
||||
|
||||
}}
|
||||
328
src/copy/requestmapper.cpp
Normal file
328
src/copy/requestmapper.cpp
Normal file
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* 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 operator==(const Field& a, const Field& b)
|
||||
bool compare(const Field& a, const Field& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
@@ -33,53 +33,53 @@ bool operator==(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 A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
case scalarArray: {
|
||||
const ScalarArray &A=static_cast<const ScalarArray&>(a);
|
||||
const ScalarArray &B=static_cast<const ScalarArray&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
case structure: {
|
||||
const Structure &A=static_cast<const Structure&>(a);
|
||||
const Structure &B=static_cast<const Structure&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
case structureArray: {
|
||||
const StructureArray &A=static_cast<const StructureArray&>(a);
|
||||
const StructureArray &B=static_cast<const StructureArray&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
case union_: {
|
||||
const Union &A=static_cast<const Union&>(a);
|
||||
const Union &B=static_cast<const Union&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
case unionArray: {
|
||||
const UnionArray &A=static_cast<const UnionArray&>(a);
|
||||
const UnionArray &B=static_cast<const UnionArray&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
default:
|
||||
throw std::logic_error("Invalid Field type in comparison");
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const Scalar& a, const Scalar& b)
|
||||
bool compare(const Scalar& a, const Scalar& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
return a.getScalarType()==b.getScalarType();
|
||||
}
|
||||
|
||||
bool operator==(const ScalarArray& a, const ScalarArray& b)
|
||||
bool compare(const ScalarArray& a, const ScalarArray& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
return a.getElementType()==b.getElementType();
|
||||
}
|
||||
|
||||
bool operator==(const Structure& a, const Structure& b)
|
||||
bool compare(const Structure& a, const Structure& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
@@ -101,12 +101,12 @@ bool operator==(const Structure& a, const Structure& b)
|
||||
return std::equal( an.begin(), an.end(), bn.begin() );
|
||||
}
|
||||
|
||||
bool operator==(const StructureArray& a, const StructureArray& b)
|
||||
bool compare(const StructureArray& a, const StructureArray& b)
|
||||
{
|
||||
return *(a.getStructure().get())==*(b.getStructure().get());
|
||||
}
|
||||
|
||||
bool operator==(const Union& a, const Union& b)
|
||||
bool compare(const Union& a, const Union& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
@@ -128,12 +128,12 @@ bool operator==(const Union& a, const Union& b)
|
||||
return std::equal( an.begin(), an.end(), bn.begin() );
|
||||
}
|
||||
|
||||
bool operator==(const UnionArray& a, const UnionArray& b)
|
||||
bool compare(const UnionArray& a, const UnionArray& b)
|
||||
{
|
||||
return *(a.getUnion().get())==*(b.getUnion().get());
|
||||
}
|
||||
|
||||
bool operator==(const BoundedString& a, const BoundedString& b)
|
||||
bool compare(const BoundedString& a, const BoundedString& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
|
||||
@@ -7,10 +7,6 @@
|
||||
* @author mrk
|
||||
*/
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
@@ -18,6 +14,7 @@
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
#include <epicsString.h>
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
@@ -27,6 +24,8 @@
|
||||
#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;
|
||||
@@ -34,13 +33,54 @@ using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
static DebugLevel debugLevel = lowDebug;
|
||||
|
||||
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
|
||||
}
|
||||
};
|
||||
|
||||
Field::Field(Type type)
|
||||
: m_fieldType(type)
|
||||
, m_hash(0)
|
||||
{
|
||||
REFTRACE_INCREMENT(num_instances);
|
||||
}
|
||||
@@ -49,6 +89,27 @@ Field::~Field() {
|
||||
REFTRACE_DECREMENT(num_instances);
|
||||
}
|
||||
|
||||
void Field::cacheCleanup()
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
@@ -62,7 +123,10 @@ Scalar::Scalar(ScalarType scalarType)
|
||||
THROW_EXCEPTION2(std::invalid_argument, "Can't construct Scalar from invalid ScalarType");
|
||||
}
|
||||
|
||||
Scalar::~Scalar(){}
|
||||
Scalar::~Scalar()
|
||||
{
|
||||
cacheCleanup();
|
||||
}
|
||||
|
||||
std::ostream& Scalar::dump(std::ostream& o) const
|
||||
{
|
||||
@@ -119,6 +183,11 @@ 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
|
||||
@@ -147,20 +216,21 @@ BoundedString::BoundedString(std::size_t maxStringLength) :
|
||||
THROW_EXCEPTION2(std::invalid_argument, "maxLength == 0");
|
||||
}
|
||||
|
||||
BoundedString::~BoundedString() {}
|
||||
BoundedString::~BoundedString()
|
||||
{
|
||||
cacheCleanup();
|
||||
}
|
||||
|
||||
|
||||
static string emptyStringtring;
|
||||
|
||||
static void serializeStructureField(const Structure* structure, ByteBuffer* buffer, SerializableControl* control)
|
||||
{
|
||||
// to optimize default (non-empty) IDs optimization
|
||||
// empty IDs are not allowed
|
||||
string id = structure->getID();
|
||||
if (id == Structure::DEFAULT_ID) // TODO slow comparison
|
||||
SerializeHelper::serializeString(emptyStringtring, buffer, control);
|
||||
else
|
||||
SerializeHelper::serializeString(id, buffer, control);
|
||||
// to optimize default (non-empty) IDs optimization
|
||||
// empty IDs are not allowed
|
||||
string id = structure->getID();
|
||||
if (id == Structure::DEFAULT_ID) // TODO slow comparison
|
||||
SerializeHelper::serializeString(string(), buffer, control);
|
||||
else
|
||||
SerializeHelper::serializeString(id, buffer, control);
|
||||
|
||||
FieldConstPtrArray const & fields = structure->getFields();
|
||||
StringArray const & fieldNames = structure->getFieldNames();
|
||||
@@ -188,7 +258,7 @@ static StructureConstPtr deserializeStructureField(const FieldCreate* fieldCreat
|
||||
if (id.empty())
|
||||
return fieldCreate->createStructure(fieldNames, fields);
|
||||
else
|
||||
return fieldCreate->createStructure(id, fieldNames, fields);
|
||||
return fieldCreate->createStructure(id, fieldNames, fields);
|
||||
}
|
||||
|
||||
static void serializeUnionField(const Union* punion, ByteBuffer* buffer, SerializableControl* control)
|
||||
@@ -197,7 +267,7 @@ static void serializeUnionField(const Union* punion, ByteBuffer* buffer, Seriali
|
||||
// empty IDs are not allowed
|
||||
string id = punion->getID();
|
||||
if (id == Union::DEFAULT_ID) // TODO slow comparison
|
||||
SerializeHelper::serializeString(emptyStringtring, buffer, control);
|
||||
SerializeHelper::serializeString(string(), buffer, control);
|
||||
else
|
||||
SerializeHelper::serializeString(id, buffer, control);
|
||||
|
||||
@@ -245,7 +315,10 @@ ScalarArray::ScalarArray(ScalarType elementType)
|
||||
throw std::invalid_argument("Can't construct ScalarArray from invalid ScalarType");
|
||||
}
|
||||
|
||||
ScalarArray::~ScalarArray() {}
|
||||
ScalarArray::~ScalarArray()
|
||||
{
|
||||
cacheCleanup();
|
||||
}
|
||||
|
||||
const string ScalarArray::getIDScalarArrayLUT() const
|
||||
{
|
||||
@@ -285,8 +358,16 @@ 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() {}
|
||||
|
||||
BoundedScalarArray::~BoundedScalarArray()
|
||||
{
|
||||
cacheCleanup();
|
||||
}
|
||||
|
||||
BoundedScalarArray::BoundedScalarArray(ScalarType elementType, size_t size)
|
||||
: ScalarArray(elementType),
|
||||
@@ -308,7 +389,10 @@ void BoundedScalarArray::serialize(ByteBuffer *buffer, SerializableControl *cont
|
||||
}
|
||||
|
||||
|
||||
FixedScalarArray::~FixedScalarArray() {}
|
||||
FixedScalarArray::~FixedScalarArray()
|
||||
{
|
||||
cacheCleanup();
|
||||
}
|
||||
|
||||
FixedScalarArray::FixedScalarArray(ScalarType elementType, size_t size)
|
||||
: ScalarArray(elementType),
|
||||
@@ -336,8 +420,9 @@ StructureArray::StructureArray(StructureConstPtr const & structure)
|
||||
{
|
||||
}
|
||||
|
||||
StructureArray::~StructureArray() {
|
||||
if(debugLevel==highDebug) printf("~StructureArray\n");
|
||||
StructureArray::~StructureArray()
|
||||
{
|
||||
cacheCleanup();
|
||||
}
|
||||
|
||||
string StructureArray::getID() const
|
||||
@@ -365,13 +450,19 @@ 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() {
|
||||
if(debugLevel==highDebug) printf("~UnionArray\n");
|
||||
UnionArray::~UnionArray()
|
||||
{
|
||||
cacheCleanup();
|
||||
}
|
||||
|
||||
string UnionArray::getID() const
|
||||
@@ -407,6 +498,11 @@ 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()
|
||||
@@ -451,7 +547,10 @@ Structure::Structure (
|
||||
}
|
||||
}
|
||||
|
||||
Structure::~Structure() { }
|
||||
Structure::~Structure()
|
||||
{
|
||||
cacheCleanup();
|
||||
}
|
||||
|
||||
|
||||
string Structure::getID() const
|
||||
@@ -478,6 +577,21 @@ 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;
|
||||
@@ -544,6 +658,11 @@ 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()
|
||||
@@ -609,7 +728,10 @@ Union::Union (
|
||||
}
|
||||
}
|
||||
|
||||
Union::~Union() { }
|
||||
Union::~Union()
|
||||
{
|
||||
cacheCleanup();
|
||||
}
|
||||
|
||||
int32 Union::guess(Type t, ScalarType s) const
|
||||
{
|
||||
@@ -672,6 +794,21 @@ 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;
|
||||
@@ -742,6 +879,11 @@ 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)
|
||||
@@ -843,6 +985,19 @@ 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;
|
||||
@@ -1079,10 +1234,9 @@ ScalarConstPtr FieldCreate::createScalar(ScalarType scalarType) const
|
||||
|
||||
BoundedStringConstPtr FieldCreate::createBoundedString(std::size_t maxLength) const
|
||||
{
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<BoundedString> s(new BoundedString(maxLength), Field::Deleter());
|
||||
BoundedStringConstPtr sa = s;
|
||||
return sa;
|
||||
std::tr1::shared_ptr<BoundedString> s(new BoundedString(maxLength));
|
||||
Helper::cache(this, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
ScalarArrayConstPtr FieldCreate::createScalarArray(ScalarType elementType) const
|
||||
@@ -1104,10 +1258,9 @@ ScalarArrayConstPtr FieldCreate::createFixedScalarArray(ScalarType elementType,
|
||||
THROW_EXCEPTION2(std::invalid_argument, strm.str());
|
||||
}
|
||||
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<ScalarArray> s(new FixedScalarArray(elementType, size), Field::Deleter());
|
||||
ScalarArrayConstPtr sa = s;
|
||||
return sa;
|
||||
std::tr1::shared_ptr<ScalarArray> s(new FixedScalarArray(elementType, size));
|
||||
Helper::cache(this, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
ScalarArrayConstPtr FieldCreate::createBoundedScalarArray(ScalarType elementType, size_t size) const
|
||||
@@ -1118,10 +1271,9 @@ ScalarArrayConstPtr FieldCreate::createBoundedScalarArray(ScalarType elementType
|
||||
THROW_EXCEPTION2(std::invalid_argument, strm.str());
|
||||
}
|
||||
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<ScalarArray> s(new BoundedScalarArray(elementType, size), Field::Deleter());
|
||||
ScalarArrayConstPtr sa = s;
|
||||
return sa;
|
||||
std::tr1::shared_ptr<ScalarArray> s(new BoundedScalarArray(elementType, size));
|
||||
Helper::cache(this, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
StructureConstPtr FieldCreate::createStructure () const
|
||||
@@ -1178,10 +1330,9 @@ StructureConstPtr FieldCreate::createStructure (
|
||||
StringArray const & fieldNames,FieldConstPtrArray const & fields) const
|
||||
{
|
||||
validateFieldNames(fieldNames);
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields), Field::Deleter());
|
||||
StructureConstPtr structure = sp;
|
||||
return structure;
|
||||
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
}
|
||||
|
||||
StructureConstPtr FieldCreate::createStructure (
|
||||
@@ -1190,29 +1341,26 @@ StructureConstPtr FieldCreate::createStructure (
|
||||
FieldConstPtrArray const & fields) const
|
||||
{
|
||||
validateFieldNames(fieldNames);
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields,id), Field::Deleter());
|
||||
StructureConstPtr structure = sp;
|
||||
return structure;
|
||||
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields,id));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
}
|
||||
|
||||
StructureArrayConstPtr FieldCreate::createStructureArray(
|
||||
StructureConstPtr const & structure) const
|
||||
{
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<StructureArray> sp(new StructureArray(structure), Field::Deleter());
|
||||
StructureArrayConstPtr structureArray = sp;
|
||||
return structureArray;
|
||||
std::tr1::shared_ptr<StructureArray> sp(new StructureArray(structure));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
}
|
||||
|
||||
UnionConstPtr FieldCreate::createUnion (
|
||||
StringArray const & fieldNames,FieldConstPtrArray const & fields) const
|
||||
{
|
||||
validateFieldNames(fieldNames);
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields), Field::Deleter());
|
||||
UnionConstPtr punion = sp;
|
||||
return punion;
|
||||
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
}
|
||||
|
||||
UnionConstPtr FieldCreate::createUnion (
|
||||
@@ -1221,10 +1369,9 @@ UnionConstPtr FieldCreate::createUnion (
|
||||
FieldConstPtrArray const & fields) const
|
||||
{
|
||||
validateFieldNames(fieldNames);
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields,id), Field::Deleter());
|
||||
UnionConstPtr punion = sp;
|
||||
return punion;
|
||||
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields,id));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
}
|
||||
|
||||
UnionConstPtr FieldCreate::createVariantUnion () const
|
||||
@@ -1235,10 +1382,9 @@ UnionConstPtr FieldCreate::createVariantUnion () const
|
||||
UnionArrayConstPtr FieldCreate::createUnionArray(
|
||||
UnionConstPtr const & punion) const
|
||||
{
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<UnionArray> sp(new UnionArray(punion), Field::Deleter());
|
||||
UnionArrayConstPtr unionArray = sp;
|
||||
return unionArray;
|
||||
std::tr1::shared_ptr<UnionArray> sp(new UnionArray(punion));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
}
|
||||
|
||||
UnionArrayConstPtr FieldCreate::createVariantUnionArray () const
|
||||
@@ -1367,12 +1513,10 @@ 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),
|
||||
Field::Deleter());
|
||||
FieldConstPtr p = sp;
|
||||
return p;
|
||||
new BoundedString(size));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
}
|
||||
else
|
||||
throw std::invalid_argument("invalid type encoding");
|
||||
@@ -1397,19 +1541,17 @@ 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),
|
||||
Field::Deleter());
|
||||
FieldConstPtr p = sp;
|
||||
return p;
|
||||
new FixedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Field> sp(
|
||||
new BoundedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size),
|
||||
Field::Deleter());
|
||||
FieldConstPtr p = sp;
|
||||
return p;
|
||||
new BoundedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
}
|
||||
}
|
||||
else if (typeCode == 0x80)
|
||||
@@ -1421,9 +1563,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), Field::Deleter());
|
||||
FieldConstPtr p = sp;
|
||||
return p;
|
||||
std::tr1::shared_ptr<Field> sp(new StructureArray(elementStructure));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
}
|
||||
else if (typeCode == 0x81)
|
||||
{
|
||||
@@ -1434,9 +1576,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), Field::Deleter());
|
||||
FieldConstPtr p = sp;
|
||||
return p;
|
||||
std::tr1::shared_ptr<Field> sp(new UnionArray(elementUnion));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
}
|
||||
else if (typeCode == 0x82)
|
||||
{
|
||||
@@ -1457,6 +1599,7 @@ struct field_factory {
|
||||
FieldCreatePtr fieldCreate;
|
||||
field_factory() :fieldCreate(new FieldCreate()) {
|
||||
registerRefCounter("Field", &Field::num_instances);
|
||||
registerRefCounter("Thread", &Thread::num_instances);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1485,23 +1628,21 @@ FieldCreate::FieldCreate()
|
||||
{
|
||||
for (int i = 0; i <= MAX_SCALAR_TYPE; i++)
|
||||
{
|
||||
// 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<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<ScalarArray> spa(new ScalarArray(static_cast<ScalarType>(i)), Field::Deleter());
|
||||
ScalarArrayConstPtr pa = spa;
|
||||
std::tr1::shared_ptr<ScalarArray> spa(new ScalarArray(static_cast<ScalarType>(i)));
|
||||
Helper::cache(this, spa);
|
||||
scalarArrays.push_back(spa);
|
||||
}
|
||||
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Union> su(new Union(), Field::Deleter());
|
||||
std::tr1::shared_ptr<Union> su(new Union());
|
||||
Helper::cache(this, su);
|
||||
variantUnion = su;
|
||||
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<UnionArray> sua(new UnionArray(variantUnion), Field::Deleter());
|
||||
std::tr1::shared_ptr<UnionArray> sua(new UnionArray(variantUnion));
|
||||
Helper::cache(this, sua);
|
||||
variantUnionArray = sua;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,6 @@
|
||||
* @author mrk
|
||||
*/
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
@@ -65,6 +61,48 @@ template<> const ScalarType PVStringArray::typeCode = pvString;
|
||||
template<typename T>
|
||||
PVScalarValue<T>::~PVScalarValue() {}
|
||||
|
||||
template<typename T>
|
||||
std::ostream& PVScalarValue<T>::dumpValue(std::ostream& o) const
|
||||
{
|
||||
return o << get();
|
||||
}
|
||||
|
||||
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,
|
||||
SerializableControl *pflusher) const {
|
||||
@@ -104,6 +142,13 @@ PVString::PVString(ScalarConstPtr const & scalar)
|
||||
storage.maxLength = 0;
|
||||
}
|
||||
|
||||
std::ostream& PVString::dumpValue(std::ostream& o) const
|
||||
{
|
||||
// we escape, but do not quote, for scalar string
|
||||
o<<escape(get());
|
||||
return o;
|
||||
}
|
||||
|
||||
/* 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.
|
||||
@@ -160,6 +205,59 @@ PVValueArray<PVUnionPtr>::PVValueArray(UnionArrayConstPtr const & 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);
|
||||
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::setCapacity(size_t capacity)
|
||||
{
|
||||
@@ -352,6 +450,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));
|
||||
}
|
||||
|
||||
// Factory
|
||||
|
||||
PVDataCreate::PVDataCreate()
|
||||
@@ -648,6 +758,18 @@ 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
|
||||
|
||||
|
||||
@@ -184,62 +184,16 @@ void PVField::copy(const PVField& from)
|
||||
if(isImmutable())
|
||||
throw std::invalid_argument("destination is immutable");
|
||||
|
||||
if (getField()->getType() != from.getField()->getType())
|
||||
if (getField() != from.getField())
|
||||
throw std::invalid_argument("field types do not match");
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
copyUnchecked(from);
|
||||
}
|
||||
|
||||
void PVField::copyUnchecked(const PVField& from)
|
||||
{
|
||||
assert(getField()==from.getField());
|
||||
|
||||
switch(getField()->getType())
|
||||
{
|
||||
case scalar:
|
||||
|
||||
@@ -60,9 +60,7 @@ PVStructure::PVStructure(StructureConstPtr const & structurePtr,
|
||||
}
|
||||
}
|
||||
|
||||
PVStructure::~PVStructure()
|
||||
{
|
||||
}
|
||||
PVStructure::~PVStructure() {}
|
||||
|
||||
void PVStructure::setImmutable()
|
||||
{
|
||||
@@ -74,16 +72,6 @@ void PVStructure::setImmutable()
|
||||
PVField::setImmutable();
|
||||
}
|
||||
|
||||
const StructureConstPtr& PVStructure::getStructure() const
|
||||
{
|
||||
return structurePtr;
|
||||
}
|
||||
|
||||
const PVFieldPtrArray & PVStructure::getPVFields() const
|
||||
{
|
||||
return pvFields;
|
||||
}
|
||||
|
||||
PVFieldPtr PVStructure::getSubFieldImpl(size_t fieldOffset, bool throws) const
|
||||
{
|
||||
const PVStructure *current = this;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <stdexcept>
|
||||
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/lock.h>
|
||||
@@ -22,32 +23,85 @@ 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")
|
||||
{}
|
||||
: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();
|
||||
}
|
||||
,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())
|
||||
{}
|
||||
|
||||
StandardField::~StandardField(){}
|
||||
|
||||
@@ -145,361 +199,6 @@ 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)
|
||||
{
|
||||
@@ -564,98 +263,21 @@ StructureConstPtr StandardField::enumerated(string const &properties)
|
||||
return createProperties("epics:nt/NTEnum:1.0",field,properties);
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::alarm()
|
||||
{
|
||||
return alarmField;
|
||||
}
|
||||
static StandardFieldPtr *stdFieldGbl;
|
||||
|
||||
StructureConstPtr StandardField::timeStamp()
|
||||
{
|
||||
return timeStampField;
|
||||
}
|
||||
static epicsThreadOnceId stdFieldGblOnce = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
StructureConstPtr StandardField::display()
|
||||
void StandardField::once(void*)
|
||||
{
|
||||
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;
|
||||
stdFieldGbl = new StandardFieldPtr;
|
||||
stdFieldGbl->reset(new StandardField);
|
||||
}
|
||||
|
||||
const StandardFieldPtr &StandardField::getStandardField()
|
||||
{
|
||||
static StandardFieldPtr standardFieldCreate;
|
||||
static Mutex mutex;
|
||||
Lock xx(mutex);
|
||||
epicsThreadOnce(&stdFieldGblOnce, &StandardField::once, 0);
|
||||
|
||||
if(standardFieldCreate.get()==0)
|
||||
{
|
||||
standardFieldCreate = StandardFieldPtr(new StandardField());
|
||||
standardFieldCreate->init();
|
||||
}
|
||||
return standardFieldCreate;
|
||||
return *stdFieldGbl;
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
@@ -3,42 +3,515 @@
|
||||
* 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>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvIntrospect.h>
|
||||
#include <epicsTime.h>
|
||||
#include <epicsString.h>
|
||||
|
||||
using std::string;
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/bitSet.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/json.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
namespace format
|
||||
{
|
||||
static int indent_index = std::ios_base::xalloc();
|
||||
static int indent_index = std::ios_base::xalloc();
|
||||
|
||||
long& indent_value(std::ios_base& ios)
|
||||
{
|
||||
return ios.iword(indent_index);
|
||||
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>()<<' ';
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, indent_level const& indent)
|
||||
{
|
||||
indent_value(os) = indent.level;
|
||||
return os;
|
||||
}
|
||||
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>()<<' ';
|
||||
}
|
||||
|
||||
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, ' ');
|
||||
}
|
||||
if(pvMessage && !pvMessage->get().empty())
|
||||
strm<<pvMessage->get()<<' ';
|
||||
}
|
||||
|
||||
array_at_internal operator<<(std::ostream& str, array_at const& manip)
|
||||
{
|
||||
return array_at_internal(manip.index, str);
|
||||
}
|
||||
};
|
||||
|
||||
}}
|
||||
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;
|
||||
|
||||
{
|
||||
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...
|
||||
}
|
||||
}
|
||||
|
||||
// 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';
|
||||
}
|
||||
|
||||
PVStringArray::svector labels;
|
||||
{
|
||||
PVStringArray::const_shared_pointer lf(top.getSubField<PVStringArray>("labels"));
|
||||
if(lf) {
|
||||
PVStringArray::const_svector L(lf->view());
|
||||
labels = thaw(L);
|
||||
}
|
||||
}
|
||||
|
||||
const PVFieldPtrArray& columns = cf->getPVFields();
|
||||
std::vector<shared_vector<std::string> > coldat(columns.size());
|
||||
|
||||
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) {
|
||||
JSONPrintOptions opts;
|
||||
opts.multiLine = false;
|
||||
printJSON(strm, format.xtop, format.xshow ? *format.xshow : BitSet().set(0), opts);
|
||||
strm<<'\n';
|
||||
return strm;
|
||||
|
||||
} 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
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
#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;
|
||||
@@ -280,5 +278,3 @@ parseJSON(std::istream& strm)
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#endif // EPICS_VERSION_INT
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvdVersion.h>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
|
||||
#include "pv/json.h"
|
||||
|
||||
namespace {
|
||||
@@ -118,5 +116,3 @@ bool yajl_parse_helper(std::istream& src,
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#endif // EPICS_VERSION_INT
|
||||
|
||||
@@ -11,9 +11,6 @@
|
||||
#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;
|
||||
@@ -302,13 +299,17 @@ struct handler {
|
||||
operator yajl_handle() { return handle; }
|
||||
};
|
||||
|
||||
struct noop {
|
||||
void operator()(pvd::PVField*) {}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
|
||||
epicsShareFunc
|
||||
void parseJSON(std::istream& strm,
|
||||
const PVField::shared_pointer& dest,
|
||||
PVField& dest,
|
||||
BitSet *assigned)
|
||||
{
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
@@ -318,7 +319,12 @@ void parseJSON(std::istream& strm,
|
||||
conf.checkUTF8 = 1;
|
||||
#endif
|
||||
|
||||
context ctxt(dest, assigned);
|
||||
// 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));
|
||||
@@ -334,9 +340,7 @@ void parseJSON(std::istream& strm,
|
||||
|
||||
if(!ctxt.stack.empty())
|
||||
throw std::logic_error("field stack not empty");
|
||||
assert(fakedest.use_count()==1);
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#endif // EPICS_VERSION_INT
|
||||
|
||||
|
||||
@@ -10,9 +10,7 @@
|
||||
#include <pv/pvdVersion.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/valueBuilder.h>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
|
||||
#include <pv/bitSet.h>
|
||||
#include "pv/json.h"
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
@@ -40,9 +38,9 @@ struct args {
|
||||
}
|
||||
};
|
||||
|
||||
void show_field(args& A, const pvd::PVField* fld);
|
||||
void show_field(args& A, const pvd::PVField* fld, const pvd::BitSet *mask);
|
||||
|
||||
void show_struct(args& A, const pvd::PVStructure* fld)
|
||||
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();
|
||||
@@ -52,13 +50,18 @@ void show_struct(args& A, const pvd::PVStructure* fld)
|
||||
A.strm.put('{');
|
||||
A.indent++;
|
||||
|
||||
bool first = true;
|
||||
for(size_t i=0, N=names.size(); i<N; i++)
|
||||
{
|
||||
if(i!=0)
|
||||
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());
|
||||
show_field(A, children[i].get(), mask);
|
||||
}
|
||||
|
||||
A.indent--;
|
||||
@@ -66,7 +69,7 @@ void show_struct(args& A, const pvd::PVStructure* fld)
|
||||
A.strm.put('}');
|
||||
}
|
||||
|
||||
void show_field(args& A, const pvd::PVField* fld)
|
||||
void show_field(args& A, const pvd::PVField* fld, const pvd::BitSet *mask)
|
||||
{
|
||||
switch(fld->getField()->getType())
|
||||
{
|
||||
@@ -79,7 +82,7 @@ void show_field(args& A, const pvd::PVField* fld)
|
||||
A.strm<<scalar->getAs<std::string>();
|
||||
}
|
||||
}
|
||||
break;
|
||||
return;
|
||||
case pvd::scalarArray:
|
||||
{
|
||||
const pvd::PVScalarArray *scalar=static_cast<const pvd::PVScalarArray*>(fld);
|
||||
@@ -102,10 +105,10 @@ void show_field(args& A, const pvd::PVField* fld)
|
||||
}
|
||||
A.strm.put(']');
|
||||
}
|
||||
break;
|
||||
return;
|
||||
case pvd::structure:
|
||||
show_struct(A, static_cast<const pvd::PVStructure*>(fld));
|
||||
break;
|
||||
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());
|
||||
@@ -117,7 +120,7 @@ void show_field(args& A, const pvd::PVField* fld)
|
||||
A.strm.put(',');
|
||||
A.doIntent();
|
||||
if(arr[i])
|
||||
show_struct(A, arr[i].get());
|
||||
show_struct(A, arr[i].get(), 0);
|
||||
else
|
||||
A.strm<<"NULL";
|
||||
}
|
||||
@@ -126,7 +129,7 @@ void show_field(args& A, const pvd::PVField* fld)
|
||||
A.doIntent();
|
||||
A.strm.put(']');
|
||||
}
|
||||
break;
|
||||
return;
|
||||
case pvd::union_:
|
||||
{
|
||||
const pvd::PVUnion *U=static_cast<const pvd::PVUnion*>(fld);
|
||||
@@ -135,15 +138,63 @@ void show_field(args& A, const pvd::PVField* fld)
|
||||
if(!C) {
|
||||
A.strm<<"null";
|
||||
} else {
|
||||
show_field(A, C.get());
|
||||
show_field(A, C.get(), 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if(A.opts.ignoreUnprintable)
|
||||
A.strm<<"// unprintable field type";
|
||||
else
|
||||
throw std::runtime_error("Encountered unprintable field type");
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,13 +209,23 @@ JSONPrintOptions::JSONPrintOptions()
|
||||
{}
|
||||
|
||||
void printJSON(std::ostream& strm,
|
||||
const PVField::const_shared_pointer& val,
|
||||
const PVStructure& val,
|
||||
const BitSet& mask,
|
||||
const JSONPrintOptions& opts)
|
||||
{
|
||||
args A(strm, opts);
|
||||
show_field(A, val.get());
|
||||
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
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
#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
|
||||
@@ -51,15 +49,32 @@ struct epicsShareClass JSONPrintOptions
|
||||
|
||||
/** Print PVStructure as JSON
|
||||
*
|
||||
* Restrictions:
|
||||
*
|
||||
* - No support for union or array of union
|
||||
* 'mask' selects those fields which will be printed.
|
||||
* @version Overload added after 7.0.0
|
||||
*/
|
||||
epicsShareFunc
|
||||
void printJSON(std::ostream& strm,
|
||||
const PVField::const_shared_pointer& val,
|
||||
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:
|
||||
@@ -82,12 +97,22 @@ PVStructure::shared_pointer parseJSON(std::istream& strm);
|
||||
* @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,
|
||||
const PVField::shared_pointer& dest,
|
||||
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()
|
||||
*
|
||||
@@ -120,8 +145,4 @@ typedef size_t size_arg;
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#else
|
||||
# error JSON parser requires EPICS Base >= 3.15.0.1
|
||||
#endif // EPICS_VERSION_INT
|
||||
|
||||
#endif // PV_JSON_H
|
||||
|
||||
@@ -15,7 +15,6 @@ INC += pv/timer.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
|
||||
@@ -31,8 +30,8 @@ LIBSRCS += serializeHelper.cpp
|
||||
LIBSRCS += event.cpp
|
||||
LIBSRCS += timer.cpp
|
||||
LIBSRCS += status.cpp
|
||||
LIBSRCS += localStaticLock.cpp
|
||||
LIBSRCS += typeCast.cpp
|
||||
LIBSRCS += thread.cpp
|
||||
LIBSRCS += parseToPOD.cpp
|
||||
LIBSRCS += pvUnitTest.cpp
|
||||
LIBSRCS += debugPtr.cpp
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
@@ -25,406 +25,6 @@ using std::string;
|
||||
#define NEED_LONGLONG
|
||||
#endif
|
||||
|
||||
#ifndef EPICS_VERSION_INT
|
||||
#define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P))
|
||||
#define EPICS_VERSION_INT VERSION_INT(EPICS_VERSION, EPICS_REVISION, EPICS_MODIFICATION, EPICS_PATCH_LEVEL)
|
||||
#endif
|
||||
|
||||
#if EPICS_VERSION_INT < VERSION_INT(3,15,0,1)
|
||||
/* These integer conversion primitives added to epicsStdlib.c in 3.15.0.1 */
|
||||
|
||||
#define S_stdlib_noConversion 1 /* No digits to convert */
|
||||
#define S_stdlib_extraneous 2 /* Extraneous characters */
|
||||
#define S_stdlib_underflow 3 /* Too small to represent */
|
||||
#define S_stdlib_overflow 4 /* Too large to represent */
|
||||
#define S_stdlib_badBase 5 /* Number base not supported */
|
||||
|
||||
static int
|
||||
epicsParseLong(const char *str, long *to, int base, char **units)
|
||||
{
|
||||
int c;
|
||||
char *endp;
|
||||
long value;
|
||||
|
||||
while ((c = *str) && isspace(c))
|
||||
++str;
|
||||
|
||||
errno = 0;
|
||||
value = strtol(str, &endp, base);
|
||||
|
||||
if (endp == str)
|
||||
return S_stdlib_noConversion;
|
||||
if (errno == EINVAL) /* Not universally supported */
|
||||
return S_stdlib_badBase;
|
||||
if (errno == ERANGE)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
while ((c = *endp) && isspace(c))
|
||||
++endp;
|
||||
if (c && !units)
|
||||
return S_stdlib_extraneous;
|
||||
|
||||
*to = value;
|
||||
if (units)
|
||||
*units = endp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseULong(const char *str, unsigned long *to, int base, char **units)
|
||||
{
|
||||
int c;
|
||||
char *endp;
|
||||
unsigned long value;
|
||||
|
||||
while ((c = *str) && isspace(c))
|
||||
++str;
|
||||
|
||||
errno = 0;
|
||||
value = strtoul(str, &endp, base);
|
||||
|
||||
if (endp == str)
|
||||
return S_stdlib_noConversion;
|
||||
if (errno == EINVAL) /* Not universally supported */
|
||||
return S_stdlib_badBase;
|
||||
if (errno == ERANGE)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
while ((c = *endp) && isspace(c))
|
||||
++endp;
|
||||
if (c && !units)
|
||||
return S_stdlib_extraneous;
|
||||
|
||||
*to = value;
|
||||
if (units)
|
||||
*units = endp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseDouble(const char *str, double *to, char **units)
|
||||
{
|
||||
int c;
|
||||
char *endp;
|
||||
double value;
|
||||
|
||||
while ((c = *str) && isspace(c))
|
||||
++str;
|
||||
|
||||
errno = 0;
|
||||
value = epicsStrtod(str, &endp);
|
||||
|
||||
if (endp == str)
|
||||
return S_stdlib_noConversion;
|
||||
if (errno == ERANGE)
|
||||
return (value == 0) ? S_stdlib_underflow : S_stdlib_overflow;
|
||||
|
||||
while ((c = *endp) && isspace(c))
|
||||
++endp;
|
||||
if (c && !units)
|
||||
return S_stdlib_extraneous;
|
||||
|
||||
*to = value;
|
||||
if (units)
|
||||
*units = endp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* These call the primitives */
|
||||
|
||||
static int
|
||||
epicsParseInt8(const char *str, epicsInt8 *to, int base, char **units)
|
||||
{
|
||||
long value;
|
||||
int status = epicsParseLong(str, &value, base, units);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (value < -0x80 || value > 0x7f)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = (epicsInt8)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseUInt8(const char *str, epicsUInt8 *to, int base, char **units)
|
||||
{
|
||||
unsigned long value;
|
||||
int status = epicsParseULong(str, &value, base, units);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (value > 0xff && value <= ~0xffUL)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = (epicsUInt8)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseInt16(const char *str, epicsInt16 *to, int base, char **units)
|
||||
{
|
||||
long value;
|
||||
int status = epicsParseLong(str, &value, base, units);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (value < -0x8000 || value > 0x7fff)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = (epicsInt16)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseUInt16(const char *str, epicsUInt16 *to, int base, char **units)
|
||||
{
|
||||
unsigned long value;
|
||||
int status = epicsParseULong(str, &value, base, units);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (value > 0xffff && value <= ~0xffffUL)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = (epicsUInt16)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseInt32(const char *str, epicsInt32 *to, int base, char **units)
|
||||
{
|
||||
long value;
|
||||
int status = epicsParseLong(str, &value, base, units);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
#if (LONG_MAX > 0x7fffffff)
|
||||
if (value < -0x80000000L || value > 0x7fffffffL)
|
||||
return S_stdlib_overflow;
|
||||
#endif
|
||||
|
||||
*to = (epicsInt32)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseUInt32(const char *str, epicsUInt32 *to, int base, char **units)
|
||||
{
|
||||
unsigned long value;
|
||||
int status = epicsParseULong(str, &value, base, units);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
#if (ULONG_MAX > 0xffffffff)
|
||||
if (value > 0xffffffffUL && value <= ~0xffffffffUL)
|
||||
return S_stdlib_overflow;
|
||||
#endif
|
||||
|
||||
*to = (epicsUInt32)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseFloat(const char *str, float *to, char **units)
|
||||
{
|
||||
double value, abs;
|
||||
int status = epicsParseDouble(str, &value, units);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
abs = fabs(value);
|
||||
if (value > 0 && abs <= FLT_MIN)
|
||||
return S_stdlib_underflow;
|
||||
if (finite(value) && abs >= FLT_MAX)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = (float)value;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Sometimes we have to provide our own copy of strtoll()
|
||||
#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)
|
||||
// On VxWorks, Base-3.15 provides strtoll()
|
||||
# define NEED_OLL_FUNCS (EPICS_VERSION_INT < VERSION_INT(3,15,0,1))
|
||||
#else
|
||||
// Other architectures all provide strtoll()
|
||||
# define NEED_OLL_FUNCS 0
|
||||
#endif
|
||||
|
||||
#if defined(NEED_LONGLONG) && NEED_OLL_FUNCS
|
||||
static
|
||||
long long strtoll(const char *ptr, char ** endp, int base)
|
||||
{
|
||||
size_t inlen = strlen(ptr);
|
||||
long long result;
|
||||
unsigned char offset=0;
|
||||
|
||||
assert(base==0);
|
||||
|
||||
if(ptr[0]=='-')
|
||||
offset=1;
|
||||
|
||||
try {
|
||||
std::istringstream strm(ptr);
|
||||
|
||||
assert(strm.rdbuf()->in_avail()>=0
|
||||
&& inlen==(size_t)strm.rdbuf()->in_avail());
|
||||
|
||||
if(ptr[offset]=='0') {
|
||||
if(ptr[offset+1]=='x')
|
||||
strm >> std::hex;
|
||||
else
|
||||
strm >> std::oct;
|
||||
}
|
||||
|
||||
strm >> result;
|
||||
if(strm.fail())
|
||||
goto noconvert;
|
||||
|
||||
assert(strm.rdbuf()->in_avail()>=0
|
||||
&& inlen>=(size_t)strm.rdbuf()->in_avail());
|
||||
|
||||
size_t consumed = inlen - strm.rdbuf()->in_avail();
|
||||
*endp = (char*)ptr + consumed;
|
||||
|
||||
return result;
|
||||
|
||||
} catch(...) {
|
||||
goto noconvert;
|
||||
}
|
||||
|
||||
return result;
|
||||
noconvert:
|
||||
*endp = (char*)ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(vxWorks)
|
||||
/* The VxWorks version of std::istringstream >> uint64_t is buggy,
|
||||
* provide our own implementation
|
||||
*/
|
||||
static
|
||||
unsigned long long strtoull(const char *nptr, char **endptr, int base)
|
||||
{
|
||||
const char *s = nptr;
|
||||
unsigned long long acc;
|
||||
int c;
|
||||
unsigned long long cutoff;
|
||||
int neg = 0, any, cutlim;
|
||||
|
||||
do
|
||||
c = *s++;
|
||||
while (isspace(c));
|
||||
if (c == '-')
|
||||
{
|
||||
neg = 1;
|
||||
c = *s++;
|
||||
}
|
||||
else if (c == '+')
|
||||
c = *s++;
|
||||
if ((base == 0 || base == 16) &&
|
||||
c == '0' && (*s == 'x' || *s == 'X'))
|
||||
{
|
||||
c = s[1];
|
||||
s += 2;
|
||||
base = 16;
|
||||
}
|
||||
if (base == 0)
|
||||
base = c == '0' ? 8 : 10;
|
||||
|
||||
cutoff = (unsigned long long) UINT64_MAX / (unsigned long long) base;
|
||||
cutlim = (unsigned long long) UINT64_MAX % (unsigned long long) base;
|
||||
|
||||
for (acc = 0, any = 0;; c = *s++)
|
||||
{
|
||||
if (isdigit(c))
|
||||
c -= '0';
|
||||
else if (isalpha(c))
|
||||
c -= isupper(c) ? 'A' - 10 : 'a' - 10;
|
||||
else
|
||||
break;
|
||||
if (c >= base)
|
||||
break;
|
||||
if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
|
||||
any = -1;
|
||||
else
|
||||
{
|
||||
any = 1;
|
||||
acc *= base;
|
||||
acc += c;
|
||||
}
|
||||
}
|
||||
if (any < 0)
|
||||
{
|
||||
acc = UINT64_MAX;
|
||||
errno = ERANGE;
|
||||
}
|
||||
else if (neg)
|
||||
acc = -acc;
|
||||
if (endptr != 0)
|
||||
*endptr = any ? (char *) s - 1 : (char *) nptr;
|
||||
return (acc);
|
||||
}
|
||||
#else
|
||||
static
|
||||
unsigned long long strtoull(const char *ptr, char ** endp, int base)
|
||||
{
|
||||
size_t inlen = strlen(ptr);
|
||||
unsigned long long result;
|
||||
|
||||
assert(base==0);
|
||||
|
||||
try {
|
||||
std::istringstream strm(ptr);
|
||||
|
||||
assert(strm.rdbuf()->in_avail()>=0
|
||||
&& inlen==(size_t)strm.rdbuf()->in_avail());
|
||||
|
||||
if(ptr[0]=='0') {
|
||||
if(ptr[1]=='x')
|
||||
strm >> std::hex;
|
||||
else
|
||||
strm >> std::oct;
|
||||
}
|
||||
|
||||
strm >> result;
|
||||
if(strm.fail())
|
||||
goto noconvert;
|
||||
|
||||
assert(strm.rdbuf()->in_avail()>=0
|
||||
&& inlen>=(size_t)strm.rdbuf()->in_avail());
|
||||
|
||||
size_t consumed = inlen - strm.rdbuf()->in_avail();
|
||||
*endp = (char*)ptr + consumed;
|
||||
|
||||
return result;
|
||||
|
||||
} catch(...) {
|
||||
goto noconvert;
|
||||
}
|
||||
|
||||
return result;
|
||||
noconvert:
|
||||
*endp = (char*)ptr;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* do we need long long? */
|
||||
#ifdef NEED_LONGLONG
|
||||
static int
|
||||
|
||||
@@ -117,6 +117,7 @@ public:
|
||||
|
||||
//! 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);
|
||||
@@ -147,7 +148,7 @@ public:
|
||||
#endif
|
||||
|
||||
//! Reset internal state.
|
||||
//! Added after 7.0.0
|
||||
//! @version Added after 7.0.0
|
||||
//! @post empty()==true
|
||||
void clear();
|
||||
|
||||
@@ -174,14 +175,16 @@ public:
|
||||
|
||||
//! 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>() = 42;
|
||||
assert(v.ref<uint32>() = 43);
|
||||
v.ref<uint32>() = 43;
|
||||
assert(v.ref<uint32>() == 43);
|
||||
@endcode
|
||||
*/
|
||||
template<typename T>
|
||||
@@ -199,9 +202,10 @@ public:
|
||||
|
||||
/** 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
|
||||
AnyScalar v(42);
|
||||
assert(v.ref<uint32>() = 42);
|
||||
const AnyScalar v(42);
|
||||
assert(v.ref<uint32>() == 42);
|
||||
@endcode
|
||||
*/
|
||||
template<typename T>
|
||||
@@ -217,7 +221,10 @@ public:
|
||||
return reinterpret_cast<typename meta::decorate_const<TT>::type&>(_wrap.blob);
|
||||
}
|
||||
|
||||
/** copy out wrapped value, with a value conversion. */
|
||||
/** copy out wrapped value, with a value conversion.
|
||||
*
|
||||
* @throws bad_cast when empty()==true
|
||||
*/
|
||||
template<typename T>
|
||||
inline
|
||||
T as() const {
|
||||
|
||||
@@ -50,6 +50,8 @@ 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:
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <epicsEndian.h>
|
||||
#include <shareLib.h>
|
||||
#include <epicsAssert.h>
|
||||
#include <compilerDependencies.h>
|
||||
|
||||
#include <pv/templateMeta.h>
|
||||
#include <pv/pvType.h>
|
||||
@@ -695,7 +696,7 @@ public:
|
||||
EPICS_ALWAYS_INLINE double getDouble (std::size_t index) { return get<double>(index); }
|
||||
|
||||
// TODO remove
|
||||
EPICS_ALWAYS_INLINE const char* getArray() const
|
||||
EPICS_ALWAYS_INLINE const char* getArray() const EPICS_DEPRECATED
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
@@ -803,8 +804,8 @@ private:
|
||||
assert(n<=getRemaining());
|
||||
|
||||
if (reverse<T>()) {
|
||||
for(std::size_t i=0; i<n; i+=sizeof(T)) {
|
||||
detail::store_unaligned(_position+i, swap<T>(values[i]));
|
||||
for(std::size_t i=0; i<count; i++) {
|
||||
detail::store_unaligned(_position+i*sizeof(T), swap<T>(values[i]));
|
||||
}
|
||||
} else {
|
||||
memcpy(_position, values, n);
|
||||
@@ -819,8 +820,8 @@ private:
|
||||
assert(n<=getRemaining());
|
||||
|
||||
if (reverse<T>()) {
|
||||
for(std::size_t i=0; i<n; i+=sizeof(T)) {
|
||||
values[i] = swap<T>(detail::load_unaligned<T>(_position+i));
|
||||
for(std::size_t i=0; i<count; i++) {
|
||||
values[i] = swap<T>(detail::load_unaligned<T>(_position+i*sizeof(T)));
|
||||
}
|
||||
} else {
|
||||
memcpy(values, _position, n);
|
||||
|
||||
@@ -33,10 +33,6 @@
|
||||
#ifndef EPICSEXCEPTION_H_
|
||||
#define EPICSEXCEPTION_H_
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
@@ -211,7 +207,7 @@ public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
explicit BaseException(const std::string msg) : std::logic_error(msg) {}
|
||||
explicit BaseException(const std::string& msg) : std::logic_error(msg) {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
/* 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 <compilerDependencies.h>
|
||||
|
||||
#include <pv/lock.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
epicsShareExtern epics::pvData::Mutex& getLocalStaticInitMutex() EPICS_DEPRECATED;
|
||||
|
||||
#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 */
|
||||
@@ -28,7 +28,7 @@
|
||||
public:
|
||||
...
|
||||
};
|
||||
@code
|
||||
@endcode
|
||||
*
|
||||
* @note This macro contains 'private:'.
|
||||
*/
|
||||
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
explicit testPassx(bool r) :dotest(true), pass(r), alive(true) {}
|
||||
~testPassx();
|
||||
template<typename T>
|
||||
inline testPassx& operator<<(T v) {
|
||||
inline testPassx& operator<<(const T& v) {
|
||||
strm<<v;
|
||||
return *this;
|
||||
}
|
||||
@@ -52,13 +52,13 @@ private:
|
||||
};
|
||||
|
||||
template<typename LHS, typename RHS>
|
||||
inline testPassx testEqualx(const char *nLHS, const char *nRHS, LHS l, RHS r)
|
||||
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, LHS l, RHS r)
|
||||
inline testPassx testNotEqualx(const char *nLHS, const char *nRHS, const LHS& l, const RHS& r)
|
||||
{
|
||||
return testPassx(l!=r)<<nLHS<<" ("<<l<<") != "<<nRHS<<" ("<<r<<")";
|
||||
}
|
||||
|
||||
@@ -5,6 +5,38 @@
|
||||
#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>
|
||||
@@ -14,27 +46,10 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <epicsVersion.h>
|
||||
#include <epicsAtomic.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
|
||||
#define REFTRACE_INCREMENT(counter) ::epics::atomic::increment(counter)
|
||||
#define REFTRACE_DECREMENT(counter) ::epics::atomic::decrement(counter)
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
|
||||
@@ -62,15 +62,21 @@
|
||||
#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)) || (__clang__ && __APPLE__)
|
||||
// c++11 or MSVC 2010
|
||||
// clang on linux has tr1/memory, clang on OSX doesn't
|
||||
#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)
|
||||
// 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
|
||||
@@ -120,6 +126,9 @@ namespace std {
|
||||
|
||||
#endif // DEBUG_SHARED_PTR
|
||||
|
||||
#elif defined(SHARED_TR1_FROM_STD)
|
||||
# include <memory>
|
||||
|
||||
#elif defined(SHARED_FROM_TR1)
|
||||
# include <tr1/memory>
|
||||
|
||||
@@ -192,7 +201,8 @@ inline std::ostream& operator<<(std::ostream& strm, const ::detail::ref_shower<T
|
||||
typedef std::tr1::weak_ptr<clazz> weak_pointer; \
|
||||
typedef std::tr1::weak_ptr<const clazz> const_weak_pointer
|
||||
|
||||
/* A semi-hack to help with migration from std::auto_ptr to std::unique_ptr,
|
||||
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.
|
||||
*
|
||||
@@ -205,7 +215,6 @@ inline std::ostream& operator<<(std::ostream& strm, const ::detail::ref_shower<T
|
||||
* copy/assignment/return are not supported
|
||||
* (use auto_ptr or unique_ptr explicitly).
|
||||
*/
|
||||
namespace epics{
|
||||
#if __cplusplus>=201103L
|
||||
template<typename T>
|
||||
using auto_ptr = std::unique_ptr<T>;
|
||||
|
||||
@@ -6,10 +6,6 @@
|
||||
#ifndef SHAREDVECTOR_H
|
||||
#define SHAREDVECTOR_H
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <ostream>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
@@ -256,7 +252,9 @@ namespace detail {
|
||||
|
||||
m_total -= offset;
|
||||
|
||||
m_count = std::min(length, max_count);
|
||||
if(length > max_count)
|
||||
length = max_count;
|
||||
m_count = length;
|
||||
}
|
||||
|
||||
// Access to members.
|
||||
@@ -430,7 +428,9 @@ public:
|
||||
void reserve(size_t i) {
|
||||
if(this->unique() && i<=this->m_total)
|
||||
return;
|
||||
size_t new_count = std::min(this->m_count, i);
|
||||
size_t new_count = this->m_count;
|
||||
if(new_count > i)
|
||||
new_count = i;
|
||||
_E_non_const* temp=new _E_non_const[i];
|
||||
try{
|
||||
std::copy(begin(), begin()+new_count, temp);
|
||||
@@ -468,13 +468,18 @@ public:
|
||||
}
|
||||
}
|
||||
// must re-allocate :(
|
||||
size_t new_total = std::max(this->m_total, i);
|
||||
size_t new_total = this->m_total;
|
||||
if(new_total < i)
|
||||
new_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()+std::min(i,this->size()),
|
||||
begin()+n,
|
||||
temp);
|
||||
this->m_sdata.reset(temp, detail::default_array_deleter<pointer>());
|
||||
}catch(...){
|
||||
|
||||
@@ -77,13 +77,13 @@ namespace epics { namespace pvData {
|
||||
* Get error message describing an error. Required if error status.
|
||||
* @return error message.
|
||||
*/
|
||||
inline std::string getMessage() const { return m_message; }
|
||||
inline const std::string& getMessage() const { return m_message; }
|
||||
|
||||
/**
|
||||
* Get stack dump where error (exception) happened. Optional.
|
||||
* @return stack dump.
|
||||
*/
|
||||
inline std::string getStackDump() const { return m_stackDump; }
|
||||
inline const std::string& getStackDump() const { return m_stackDump; }
|
||||
|
||||
/**
|
||||
* Convenient OK test. Same as <code>(getType() == StatusType.OK)</code>.
|
||||
|
||||
@@ -42,38 +42,7 @@ 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 RunnableMethod : public Runnable
|
||||
{
|
||||
EPICS_NOT_COPYABLE(RunnableMethod)
|
||||
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
|
||||
{
|
||||
@@ -87,26 +56,13 @@ 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 Thread : public epicsThread {
|
||||
class epicsShareClass Thread : public epicsThread {
|
||||
EPICS_NOT_COPYABLE(Thread)
|
||||
public:
|
||||
/** @brief Holds all the configuration necessary to launch a @class Thread
|
||||
@@ -136,7 +92,7 @@ public:
|
||||
<<"example"<<1);
|
||||
@endcode
|
||||
*/
|
||||
class Config
|
||||
class epicsShareClass Config
|
||||
{
|
||||
unsigned int p_prio, p_stack;
|
||||
std::ostringstream p_strm;
|
||||
@@ -145,49 +101,28 @@ public:
|
||||
typedef epics::auto_ptr<Runnable> p_owned_runner_t;
|
||||
p_owned_runner_t p_owned_runner;
|
||||
friend class Thread;
|
||||
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);
|
||||
}
|
||||
Runnable& x_getrunner();
|
||||
void x_setdefault();
|
||||
|
||||
public:
|
||||
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);}
|
||||
Config();
|
||||
Config(Runnable *r);
|
||||
Config(void(*fn)(void*), void *ptr);
|
||||
template<typename C>
|
||||
Config(C* inst, void(C::*meth)()) {this->x_setdefault();this->run(inst, meth);}
|
||||
#if __cplusplus>=201103L
|
||||
Config(const std::function<void()>& fn) {this->x_setdefault();this->run(fn);}
|
||||
Config(std::function<void()>&& fn);
|
||||
#endif
|
||||
|
||||
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; }
|
||||
Config& name(const std::string& n);
|
||||
Config& prio(unsigned int p);
|
||||
Config& stack(epicsThreadStackSizeClass s);
|
||||
Config& autostart(bool a);
|
||||
|
||||
//! Thread will execute Runnable::run()
|
||||
Config& run(Runnable* r)
|
||||
{ this->p_runner = r; return *this; }
|
||||
Config& run(Runnable* r);
|
||||
//! Thread will execute (*fn)(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;
|
||||
}
|
||||
Config& run(void(*fn)(void*), void *ptr);
|
||||
//! Thread will execute (inst->*meth)()
|
||||
template<typename C>
|
||||
Config& run(C* inst, void(C::*meth)())
|
||||
@@ -197,12 +132,7 @@ public:
|
||||
return *this;
|
||||
}
|
||||
#if __cplusplus>=201103L
|
||||
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;
|
||||
}
|
||||
Config& run(std::function<void()>&& fn);
|
||||
#endif
|
||||
|
||||
//! Append to thread name string. Argument must be understood by std::ostream::operator<<
|
||||
@@ -228,14 +158,7 @@ public:
|
||||
Thread(std::string name,
|
||||
ThreadPriority priority,
|
||||
Runnable *runnable,
|
||||
epicsThreadStackSizeClass stkcls=epicsThreadStackSmall)
|
||||
:epicsThread(*runnable,
|
||||
name.c_str(),
|
||||
epicsThreadGetStackSize(stkcls),
|
||||
priority)
|
||||
{
|
||||
this->start();
|
||||
}
|
||||
epicsThreadStackSizeClass stkcls=epicsThreadStackBig);
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -255,38 +178,19 @@ public:
|
||||
Thread(Runnable &runnable,
|
||||
std::string name,
|
||||
unsigned int stksize,
|
||||
unsigned int priority=lowestPriority)
|
||||
:epicsThread(runnable,
|
||||
name.c_str(),
|
||||
stksize,
|
||||
priority)
|
||||
{
|
||||
this->start();
|
||||
}
|
||||
unsigned int priority=lowestPriority);
|
||||
|
||||
//! @brief Create a new thread using the given @class Config
|
||||
//! @throws std::logic_error for improper @class Config (ie. missing runner)
|
||||
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();
|
||||
}
|
||||
Thread(Config& c);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Thread()
|
||||
{
|
||||
this->exitWait();
|
||||
}
|
||||
~Thread();
|
||||
|
||||
static size_t num_instances;
|
||||
private:
|
||||
Config::p_owned_runner_t p_owned;
|
||||
};
|
||||
|
||||
|
||||
@@ -126,6 +126,7 @@ private:
|
||||
mutable Mutex mutex;
|
||||
queue_t queue;
|
||||
Event waitForWork;
|
||||
bool waiting;
|
||||
bool alive;
|
||||
Thread thread;
|
||||
};
|
||||
|
||||
@@ -208,6 +208,35 @@ 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
|
||||
|
||||
@@ -25,15 +25,6 @@
|
||||
|
||||
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;
|
||||
|
||||
@@ -88,11 +79,7 @@ size_t readRefCounter(const char *name)
|
||||
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&
|
||||
@@ -119,11 +106,7 @@ void RefSnapshot::update()
|
||||
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);
|
||||
}
|
||||
@@ -223,7 +206,7 @@ void RefMonitor::start(double period)
|
||||
impl->period = period;
|
||||
impl->worker.reset(new epicsThread(*impl,
|
||||
"RefMonitor",
|
||||
epicsThreadGetStackSize(epicsThreadStackSmall),
|
||||
epicsThreadGetStackSize(epicsThreadStackBig),
|
||||
epicsThreadPriorityMin));
|
||||
impl->worker->start();
|
||||
}
|
||||
|
||||
@@ -109,7 +109,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->getArray()+pos, size);
|
||||
string str(buffer->getBuffer()+pos, size);
|
||||
buffer->setPosition(pos+size);
|
||||
return str;
|
||||
}
|
||||
@@ -122,7 +122,7 @@ namespace epics {
|
||||
while(true) {
|
||||
std::size_t toRead = min(size-i, buffer->getRemaining());
|
||||
std::size_t pos = buffer->getPosition();
|
||||
str.append(buffer->getArray()+pos, toRead);
|
||||
str.append(buffer->getBuffer()+pos, toRead);
|
||||
buffer->setPosition(pos+toRead);
|
||||
i += toRead;
|
||||
if(i<size)
|
||||
|
||||
147
src/misc/thread.cpp
Normal file
147
src/misc/thread.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
/* 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
|
||||
@@ -6,10 +6,6 @@
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
@@ -32,9 +28,10 @@ TimerCallback::TimerCallback()
|
||||
}
|
||||
|
||||
Timer::Timer(string threadName,ThreadPriority priority)
|
||||
: waitForWork(false),
|
||||
alive(true),
|
||||
thread(threadName,priority,this)
|
||||
:waitForWork(false)
|
||||
,waiting(false)
|
||||
,alive(true)
|
||||
,thread(threadName,priority,this)
|
||||
{}
|
||||
|
||||
struct TimerCallback::IncreasingTime {
|
||||
@@ -64,13 +61,16 @@ bool Timer::cancel(TimerCallbackPtr const &timerCallback)
|
||||
{
|
||||
Lock xx(mutex);
|
||||
if(!timerCallback->onList) return false;
|
||||
if(!alive) {
|
||||
timerCallback->onList = false;
|
||||
return true;
|
||||
}
|
||||
for(queue_t::iterator it(queue.begin()), end(queue.end()); it != end; ++it)
|
||||
{
|
||||
TimerCallbackPtr& cur = *it;
|
||||
if(cur.get() == timerCallback.get()) {
|
||||
queue.erase(it);
|
||||
cur->onList = false;
|
||||
// iteration now invalid
|
||||
queue.erase(it); // invalidates cur and it
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -95,6 +95,7 @@ void Timer::run()
|
||||
|
||||
if(queue.empty()) {
|
||||
// no jobs, just go to sleep
|
||||
waiting = true;
|
||||
epicsGuardRelease<epicsMutex> U(G);
|
||||
|
||||
waitForWork.wait();
|
||||
@@ -114,7 +115,7 @@ void Timer::run()
|
||||
work->callback();
|
||||
}
|
||||
|
||||
if(work->period > 0.0) {
|
||||
if(work->period > 0.0 && alive) {
|
||||
work->timeToRun += work->period;
|
||||
addElement(work);
|
||||
}
|
||||
@@ -122,12 +123,14 @@ void Timer::run()
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +172,7 @@ void Timer::schedulePeriodic(
|
||||
{
|
||||
epicsTime now(epicsTime::getCurrent());
|
||||
|
||||
bool wasempty;
|
||||
bool wakeup;
|
||||
{
|
||||
Lock xx(mutex);
|
||||
if(timerCallback->onList) {
|
||||
@@ -185,10 +188,10 @@ void Timer::schedulePeriodic(
|
||||
timerCallback->timeToRun = now + delay;
|
||||
timerCallback->period = period;
|
||||
|
||||
wasempty = queue.empty();
|
||||
addElement(timerCallback);
|
||||
wakeup = waiting && queue.front()==timerCallback;
|
||||
}
|
||||
if(wasempty) waitForWork.signal();
|
||||
if(wakeup) waitForWork.signal();
|
||||
}
|
||||
|
||||
void Timer::dump(std::ostream& o) const
|
||||
|
||||
272
src/pv/pvData.h
272
src/pv/pvData.h
@@ -9,10 +9,6 @@
|
||||
#ifndef PVDATA_H
|
||||
#define PVDATA_H
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
@@ -21,6 +17,8 @@
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
#include <epicsAssert.h>
|
||||
|
||||
#include <pv/pvIntrospect.h>
|
||||
#include <pv/typeCast.h>
|
||||
#include <pv/anyscalar.h>
|
||||
@@ -241,6 +239,7 @@ public:
|
||||
void copyUnchecked(const PVField& from);
|
||||
|
||||
static size_t num_instances; // use atomic::get() or volatile* access
|
||||
enum {isPVField=1};
|
||||
protected:
|
||||
PVField::shared_pointer getPtrSelf()
|
||||
{
|
||||
@@ -260,6 +259,7 @@ private:
|
||||
PostHandlerPtr postHandler;
|
||||
friend class PVDataCreate;
|
||||
friend class PVStructure;
|
||||
EPICS_NOT_COPYABLE(PVField)
|
||||
};
|
||||
|
||||
epicsShareExtern std::ostream& operator<<(std::ostream& o, const PVField& f);
|
||||
@@ -340,6 +340,7 @@ public:
|
||||
|
||||
protected:
|
||||
explicit PVScalar(ScalarConstPtr const & scalar);
|
||||
EPICS_NOT_COPYABLE(PVScalar)
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
@@ -397,31 +398,22 @@ public:
|
||||
* Put a new value into the PVScalar.
|
||||
* @param value The value.
|
||||
*/
|
||||
void put(typename storage_t::arg_type v) {
|
||||
inline void put(typename storage_t::arg_type v) {
|
||||
storage.store(v);
|
||||
PVField::postPut();
|
||||
}
|
||||
|
||||
std::ostream& dumpValue(std::ostream& o) const OVERRIDE
|
||||
{
|
||||
return o << get();
|
||||
}
|
||||
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE;
|
||||
|
||||
// get operator
|
||||
// double value; doubleField >>= value;
|
||||
// NOTE: virtual is needed for MS C++ compiler to get this operator exported
|
||||
virtual void operator>>=(T& value) const
|
||||
{
|
||||
value = get();
|
||||
}
|
||||
virtual void operator>>=(T& value) const;
|
||||
|
||||
// put operator
|
||||
// double value = 12.8; doubleField <<= value;
|
||||
// NOTE: virtual is needed for MS C++ compiler to get this operator exported
|
||||
virtual void operator<<=(typename storage_t::arg_type value)
|
||||
{
|
||||
put(value);
|
||||
}
|
||||
virtual void operator<<=(typename storage_t::arg_type value);
|
||||
|
||||
template<typename T1>
|
||||
inline T1 getAs() const {
|
||||
@@ -439,44 +431,29 @@ public:
|
||||
PVScalar::putFrom(v);
|
||||
}
|
||||
|
||||
virtual void assign(const PVScalar& scalar) OVERRIDE
|
||||
{
|
||||
if(isImmutable())
|
||||
throw std::invalid_argument("destination is immutable");
|
||||
copyUnchecked(scalar);
|
||||
}
|
||||
virtual void copy(const PVScalar& from) OVERRIDE
|
||||
{
|
||||
assign(from);
|
||||
}
|
||||
virtual void copyUnchecked(const PVScalar& from) OVERRIDE
|
||||
{
|
||||
if(this==&from)
|
||||
return;
|
||||
T result;
|
||||
from.getAs((void*)&result, typeCode);
|
||||
put(result);
|
||||
}
|
||||
virtual void assign(const PVScalar& scalar) OVERRIDE FINAL;
|
||||
virtual void copy(const PVScalar& from) OVERRIDE FINAL;
|
||||
virtual void copyUnchecked(const PVScalar& from) OVERRIDE FINAL;
|
||||
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const OVERRIDE;
|
||||
virtual void deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pflusher) OVERRIDE;
|
||||
DeserializableControl *pflusher) OVERRIDE FINAL;
|
||||
|
||||
protected:
|
||||
explicit PVScalarValue(ScalarConstPtr const & scalar)
|
||||
: PVScalar(scalar), storage() {}
|
||||
virtual void getAs(void * result, ScalarType rtype) const OVERRIDE
|
||||
virtual void getAs(void * result, ScalarType rtype) const OVERRIDE FINAL
|
||||
{
|
||||
const T src = get();
|
||||
castUnsafeV(1, rtype, result, typeCode, (const void*)&src);
|
||||
}
|
||||
public:
|
||||
virtual void getAs(AnyScalar& v) const
|
||||
virtual void getAs(AnyScalar& v) const OVERRIDE FINAL
|
||||
{
|
||||
v = get();
|
||||
}
|
||||
virtual void putFrom(const void *src, ScalarType stype) OVERRIDE
|
||||
virtual void putFrom(const void *src, ScalarType stype) OVERRIDE FINAL
|
||||
{
|
||||
T result;
|
||||
castUnsafeV(1, typeCode, (void*)&result, stype, src);
|
||||
@@ -486,6 +463,7 @@ protected:
|
||||
|
||||
friend class PVDataCreate;
|
||||
storage_t storage;
|
||||
EPICS_NOT_COPYABLE(PVScalarValue)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -547,14 +525,17 @@ public:
|
||||
*/
|
||||
virtual ~PVString() {}
|
||||
|
||||
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
|
||||
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const OVERRIDE;
|
||||
SerializableControl *pflusher) const OVERRIDE FINAL;
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const OVERRIDE;
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const OVERRIDE FINAL;
|
||||
protected:
|
||||
explicit PVString(ScalarConstPtr const & scalar);
|
||||
|
||||
friend class PVDataCreate;
|
||||
EPICS_NOT_COPYABLE(PVString)
|
||||
};
|
||||
typedef std::tr1::shared_ptr<PVString> PVStringPtr;
|
||||
|
||||
@@ -624,6 +605,7 @@ protected:
|
||||
private:
|
||||
bool capacityMutable;
|
||||
friend class PVDataCreate;
|
||||
EPICS_NOT_COPYABLE(PVArray)
|
||||
};
|
||||
|
||||
epicsShareExtern std::ostream& operator<<(format::array_at_internal const& manip, const PVArray& array);
|
||||
@@ -718,6 +700,7 @@ protected:
|
||||
explicit PVScalarArray(ScalarArrayConstPtr const & scalarArray);
|
||||
private:
|
||||
friend class PVDataCreate;
|
||||
EPICS_NOT_COPYABLE(PVScalarArray)
|
||||
};
|
||||
|
||||
|
||||
@@ -740,17 +723,17 @@ public:
|
||||
* Set the field to be immutable, i.e. it can no longer be modified.
|
||||
* This is permanent, i.e. once done the field cannot be made mutable.
|
||||
*/
|
||||
virtual void setImmutable() OVERRIDE;
|
||||
virtual void setImmutable() OVERRIDE FINAL;
|
||||
/**
|
||||
* Get the introspection interface
|
||||
* @return The interface.
|
||||
*/
|
||||
const StructureConstPtr &getStructure() const;
|
||||
inline const StructureConstPtr &getStructure() const { return structurePtr; }
|
||||
/**
|
||||
* Get the array of pointers to the subfields in the structure.
|
||||
* @return The array.
|
||||
*/
|
||||
const PVFieldPtrArray & getPVFields() const;
|
||||
inline const PVFieldPtrArray & getPVFields() const { return pvFields; }
|
||||
|
||||
/**
|
||||
* Get the subfield with the specified offset.
|
||||
@@ -782,12 +765,14 @@ public:
|
||||
template<typename PVD, typename A>
|
||||
inline std::tr1::shared_ptr<PVD> getSubField(A a)
|
||||
{
|
||||
STATIC_ASSERT(PVD::isPVField); // only allow cast from PVField sub-class
|
||||
return std::tr1::dynamic_pointer_cast<PVD>(getSubFieldImpl(a, false));
|
||||
}
|
||||
|
||||
template<typename PVD, typename A>
|
||||
inline std::tr1::shared_ptr<const PVD> getSubField(A a) const
|
||||
{
|
||||
STATIC_ASSERT(PVD::isPVField); // only allow cast from PVField sub-class
|
||||
return std::tr1::dynamic_pointer_cast<const PVD>(getSubFieldImpl(a, false));
|
||||
}
|
||||
|
||||
@@ -820,6 +805,7 @@ public:
|
||||
template<typename PVD, typename A>
|
||||
inline std::tr1::shared_ptr<PVD> getSubFieldT(A a)
|
||||
{
|
||||
STATIC_ASSERT(PVD::isPVField); // only allow cast from PVField sub-class
|
||||
std::tr1::shared_ptr<PVD> ret(std::tr1::dynamic_pointer_cast<PVD>(getSubFieldImpl(a, true)));
|
||||
if(!ret)
|
||||
throwBadFieldType(a);
|
||||
@@ -829,6 +815,7 @@ public:
|
||||
template<typename PVD, typename A>
|
||||
inline std::tr1::shared_ptr<const PVD> getSubFieldT(A a) const
|
||||
{
|
||||
STATIC_ASSERT(PVD::isPVField); // only allow cast from PVField sub-class
|
||||
std::tr1::shared_ptr<const PVD> ret(std::tr1::dynamic_pointer_cast<const PVD>(getSubFieldImpl(a, true)));
|
||||
if(!ret)
|
||||
throwBadFieldType(a);
|
||||
@@ -841,14 +828,14 @@ public:
|
||||
* @param pflusher Interface to call when buffer is full.
|
||||
*/
|
||||
virtual void serialize(
|
||||
ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE;
|
||||
ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE FINAL;
|
||||
/**
|
||||
* Deserialize
|
||||
* @param pbuffer The byte buffer.
|
||||
* @param pflusher Interface to call when buffer is empty.
|
||||
*/
|
||||
virtual void deserialize(
|
||||
ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE;
|
||||
ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE FINAL;
|
||||
/**
|
||||
* Serialize.
|
||||
* @param pbuffer The byte buffer.
|
||||
@@ -856,7 +843,7 @@ public:
|
||||
* @param pbitSet A bitset the specifies which fields to serialize.
|
||||
*/
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher,BitSet *pbitSet) const OVERRIDE;
|
||||
SerializableControl *pflusher,BitSet *pbitSet) const OVERRIDE FINAL;
|
||||
/**
|
||||
* Deserialize
|
||||
* @param pbuffer The byte buffer.
|
||||
@@ -864,7 +851,7 @@ public:
|
||||
* @param pbitSet A bitset the specifies which fields to deserialize.
|
||||
*/
|
||||
virtual void deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl*pflusher,BitSet *pbitSet) OVERRIDE;
|
||||
DeserializableControl*pflusher,BitSet *pbitSet) OVERRIDE FINAL;
|
||||
/**
|
||||
* Constructor
|
||||
* @param structure The introspection interface.
|
||||
@@ -877,14 +864,56 @@ public:
|
||||
*/
|
||||
PVStructure(StructureConstPtr const & structure,PVFieldPtrArray const & pvFields);
|
||||
|
||||
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE;
|
||||
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
|
||||
|
||||
void copy(const PVStructure& from);
|
||||
|
||||
void copyUnchecked(const PVStructure& from);
|
||||
void copyUnchecked(const PVStructure& from, const BitSet& maskBitSet, bool inverse = false);
|
||||
|
||||
struct Formatter {
|
||||
enum mode_t {
|
||||
Auto,
|
||||
Plain,
|
||||
ANSI,
|
||||
};
|
||||
enum format_t {
|
||||
Raw,
|
||||
NT,
|
||||
JSON,
|
||||
};
|
||||
private:
|
||||
const PVStructure& xtop;
|
||||
const BitSet* xshow;
|
||||
const BitSet* xhighlight;
|
||||
mode_t xmode;
|
||||
format_t xfmt;
|
||||
public:
|
||||
explicit Formatter(const PVStructure& top)
|
||||
:xtop(top)
|
||||
,xshow(0)
|
||||
,xhighlight(0)
|
||||
,xmode(Auto)
|
||||
,xfmt(NT)
|
||||
{}
|
||||
|
||||
// those fields (and their parents) to be printed. non-NT mode.
|
||||
FORCE_INLINE Formatter& show(const BitSet& set) { xshow = &set; return *this; }
|
||||
// those fields (and not their parents) to be specially highlighted. non-NT mode.
|
||||
FORCE_INLINE Formatter& highlight(const BitSet& set) { xhighlight = &set; return *this; }
|
||||
|
||||
FORCE_INLINE Formatter& mode(mode_t m) { xmode = m; return *this; }
|
||||
|
||||
FORCE_INLINE Formatter& format(format_t f) { xfmt = f; return *this; }
|
||||
|
||||
friend epicsShareFunc std::ostream& operator<<(std::ostream& strm, const Formatter& format);
|
||||
friend void printRaw(std::ostream& strm, const PVStructure::Formatter& format, const PVStructure& cur);
|
||||
};
|
||||
|
||||
FORCE_INLINE Formatter stream() const { return Formatter(*this); }
|
||||
|
||||
private:
|
||||
|
||||
inline PVFieldPtr getSubFieldImpl(const std::string& name, bool throws) const {
|
||||
return getSubFieldImpl(name.c_str(), throws);
|
||||
}
|
||||
@@ -895,8 +924,12 @@ private:
|
||||
StructureConstPtr structurePtr;
|
||||
std::string extendsStructureName;
|
||||
friend class PVDataCreate;
|
||||
EPICS_NOT_COPYABLE(PVStructure)
|
||||
};
|
||||
|
||||
epicsShareFunc
|
||||
std::ostream& operator<<(std::ostream& strm, const PVStructure::Formatter& format);
|
||||
|
||||
/**
|
||||
* @brief PVUnion has a single subfield.
|
||||
*
|
||||
@@ -937,11 +970,13 @@ public:
|
||||
|
||||
template<typename PVT>
|
||||
inline std::tr1::shared_ptr<PVT> get() {
|
||||
STATIC_ASSERT(PVT::isPVField); // only allow cast from PVField sub-class
|
||||
return std::tr1::dynamic_pointer_cast<PVT>(get());
|
||||
}
|
||||
|
||||
template<typename PVT>
|
||||
inline std::tr1::shared_ptr<const PVT> get() const {
|
||||
STATIC_ASSERT(PVT::isPVField); // only allow cast from PVField sub-class
|
||||
return std::tr1::dynamic_pointer_cast<const PVT>(get());
|
||||
}
|
||||
|
||||
@@ -955,6 +990,7 @@ public:
|
||||
|
||||
template<typename PVT>
|
||||
inline std::tr1::shared_ptr<PVT> select(int32 index) {
|
||||
STATIC_ASSERT(PVT::isPVField); // only allow cast from PVField sub-class
|
||||
return std::tr1::dynamic_pointer_cast<PVT>(select(index));
|
||||
}
|
||||
|
||||
@@ -1020,21 +1056,21 @@ public:
|
||||
* @param pflusher Interface to call when buffer is full.
|
||||
*/
|
||||
virtual void serialize(
|
||||
ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE;
|
||||
ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE FINAL;
|
||||
/**
|
||||
* Deserialize
|
||||
* @param pbuffer The byte buffer.
|
||||
* @param pflusher Interface to call when buffer is empty.
|
||||
*/
|
||||
virtual void deserialize(
|
||||
ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE;
|
||||
ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE FINAL;
|
||||
/**
|
||||
* Constructor
|
||||
* @param punion The introspection interface.
|
||||
*/
|
||||
explicit PVUnion(UnionConstPtr const & punion);
|
||||
|
||||
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE;
|
||||
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
|
||||
|
||||
void copy(const PVUnion& from);
|
||||
void copyUnchecked(const PVUnion& from);
|
||||
@@ -1048,6 +1084,7 @@ private:
|
||||
int32 selector;
|
||||
PVFieldPtr value;
|
||||
bool variant;
|
||||
EPICS_NOT_COPYABLE(PVUnion)
|
||||
};
|
||||
|
||||
|
||||
@@ -1122,6 +1159,7 @@ namespace detail {
|
||||
return thaw(result);
|
||||
}
|
||||
|
||||
EPICS_NOT_COPYABLE(PVVectorStorage)
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
@@ -1152,67 +1190,40 @@ public:
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~PVValueArray() {}
|
||||
virtual ~PVValueArray();
|
||||
|
||||
/**
|
||||
* Get introspection interface.
|
||||
*/
|
||||
virtual ArrayConstPtr getArray() const OVERRIDE
|
||||
{
|
||||
return std::tr1::static_pointer_cast<const Array>(this->getField());
|
||||
}
|
||||
virtual ArrayConstPtr getArray() const OVERRIDE FINAL;
|
||||
|
||||
std::ostream& dumpValue(std::ostream& o) const OVERRIDE
|
||||
{
|
||||
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);
|
||||
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
|
||||
virtual std::ostream& dumpValue(std::ostream& o, size_t index) const OVERRIDE FINAL;
|
||||
|
||||
}
|
||||
return o << ']';
|
||||
}
|
||||
virtual size_t getLength() const OVERRIDE FINAL {return value.size();}
|
||||
virtual size_t getCapacity() const OVERRIDE FINAL {return value.capacity();}
|
||||
|
||||
virtual size_t getLength() const OVERRIDE {return value.size();}
|
||||
virtual size_t getCapacity() const OVERRIDE {return value.capacity();}
|
||||
virtual void setCapacity(size_t capacity) OVERRIDE FINAL;
|
||||
virtual void setLength(size_t length) OVERRIDE FINAL;
|
||||
|
||||
virtual void setCapacity(size_t capacity) OVERRIDE;
|
||||
virtual void setLength(size_t length) OVERRIDE;
|
||||
|
||||
virtual const_svector view() const OVERRIDE {return value;}
|
||||
virtual void swap(const_svector &other) OVERRIDE;
|
||||
virtual void replace(const const_svector& next) OVERRIDE;
|
||||
virtual const_svector view() const OVERRIDE FINAL {return value;}
|
||||
virtual void swap(const_svector &other) OVERRIDE FINAL;
|
||||
virtual void replace(const const_svector& next) OVERRIDE FINAL;
|
||||
|
||||
// from Serializable
|
||||
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE;
|
||||
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE;
|
||||
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE FINAL;
|
||||
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE FINAL;
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const OVERRIDE;
|
||||
|
||||
std::ostream& dumpValue(std::ostream& o, size_t index) const OVERRIDE
|
||||
{
|
||||
return o << print_cast(this->view().at(index));
|
||||
}
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const OVERRIDE FINAL;
|
||||
|
||||
protected:
|
||||
virtual void _getAsVoid(epics::pvData::shared_vector<const void>& out) const OVERRIDE
|
||||
{
|
||||
out = static_shared_vector_cast<const void>(this->view());
|
||||
}
|
||||
|
||||
virtual void _putFromVoid(const epics::pvData::shared_vector<const void>& in) OVERRIDE
|
||||
{
|
||||
// TODO: try to re-use storage
|
||||
this->replace(shared_vector_convert<const T>(in));
|
||||
}
|
||||
virtual void _getAsVoid(epics::pvData::shared_vector<const void>& out) const OVERRIDE FINAL;
|
||||
virtual void _putFromVoid(const epics::pvData::shared_vector<const void>& in) OVERRIDE FINAL;
|
||||
|
||||
explicit PVValueArray(ScalarArrayConstPtr const & scalar);
|
||||
const_svector value;
|
||||
friend class PVDataCreate;
|
||||
EPICS_NOT_COPYABLE(PVValueArray)
|
||||
};
|
||||
|
||||
|
||||
@@ -1241,24 +1252,24 @@ public:
|
||||
*/
|
||||
virtual ~PVValueArray() {}
|
||||
|
||||
virtual ArrayConstPtr getArray() const OVERRIDE
|
||||
virtual ArrayConstPtr getArray() const OVERRIDE FINAL
|
||||
{
|
||||
return std::tr1::static_pointer_cast<const Array>(structureArray);
|
||||
}
|
||||
|
||||
virtual size_t getLength() const OVERRIDE {return value.size();}
|
||||
virtual size_t getCapacity() const OVERRIDE {return value.capacity();}
|
||||
virtual size_t getLength() const OVERRIDE FINAL {return value.size();}
|
||||
virtual size_t getCapacity() const OVERRIDE FINAL {return value.capacity();}
|
||||
|
||||
/**
|
||||
* Set the array capacity.
|
||||
* @param capacity The length.
|
||||
*/
|
||||
virtual void setCapacity(size_t capacity) OVERRIDE;
|
||||
virtual void setCapacity(size_t capacity) OVERRIDE FINAL;
|
||||
/**
|
||||
* Set the array length.
|
||||
* @param length The length.
|
||||
*/
|
||||
virtual void setLength(std::size_t length) OVERRIDE;
|
||||
virtual void setLength(std::size_t length) OVERRIDE FINAL;
|
||||
|
||||
/**
|
||||
* Get the introspection interface
|
||||
@@ -1270,36 +1281,36 @@ public:
|
||||
* @param number The number of elements to add.
|
||||
* @return the new length of the array.
|
||||
*/
|
||||
virtual std::size_t append(std::size_t number) FINAL;
|
||||
std::size_t append(std::size_t number);
|
||||
/**
|
||||
* Remove elements from the array.
|
||||
* @param offset The offset of the first element to remove.
|
||||
* @param number The number of elements to remove.
|
||||
* @return (false,true) if the elements were removed.
|
||||
*/
|
||||
virtual bool remove(std::size_t offset,std::size_t number) FINAL;
|
||||
bool remove(std::size_t offset,std::size_t number);
|
||||
/**
|
||||
* Compress. This removes all null elements from the array.
|
||||
*/
|
||||
virtual void compress() FINAL;
|
||||
void compress();
|
||||
|
||||
virtual const_svector view() const OVERRIDE { return value; }
|
||||
virtual void swap(const_svector &other) OVERRIDE;
|
||||
virtual void replace(const const_svector &other) OVERRIDE {
|
||||
virtual const_svector view() const OVERRIDE FINAL { return value; }
|
||||
virtual void swap(const_svector &other) OVERRIDE FINAL;
|
||||
virtual void replace(const const_svector &other) OVERRIDE FINAL {
|
||||
checkLength(other.size());
|
||||
value = other;
|
||||
PVField::postPut();
|
||||
}
|
||||
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const OVERRIDE;
|
||||
SerializableControl *pflusher) const OVERRIDE FINAL;
|
||||
virtual void deserialize(ByteBuffer *buffer,
|
||||
DeserializableControl *pflusher) OVERRIDE;
|
||||
DeserializableControl *pflusher) OVERRIDE FINAL;
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, std::size_t offset, std::size_t count) const OVERRIDE;
|
||||
SerializableControl *pflusher, std::size_t offset, std::size_t count) const OVERRIDE FINAL;
|
||||
|
||||
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE;
|
||||
virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const OVERRIDE;
|
||||
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
|
||||
virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const OVERRIDE FINAL;
|
||||
|
||||
void copy(const PVStructureArray& from);
|
||||
void copyUnchecked(const PVStructureArray& from);
|
||||
@@ -1310,6 +1321,7 @@ private:
|
||||
StructureArrayConstPtr structureArray;
|
||||
const_svector value;
|
||||
friend class PVDataCreate;
|
||||
EPICS_NOT_COPYABLE(PVValueArray)
|
||||
};
|
||||
|
||||
|
||||
@@ -1339,24 +1351,24 @@ public:
|
||||
*/
|
||||
virtual ~PVValueArray() {}
|
||||
|
||||
virtual ArrayConstPtr getArray() const OVERRIDE
|
||||
virtual ArrayConstPtr getArray() const OVERRIDE FINAL
|
||||
{
|
||||
return std::tr1::static_pointer_cast<const Array>(unionArray);
|
||||
}
|
||||
|
||||
virtual size_t getLength() const OVERRIDE {return value.size();}
|
||||
virtual size_t getCapacity() const OVERRIDE {return value.capacity();}
|
||||
virtual size_t getLength() const OVERRIDE FINAL {return value.size();}
|
||||
virtual size_t getCapacity() const OVERRIDE FINAL {return value.capacity();}
|
||||
|
||||
/**
|
||||
* Set the array capacity.
|
||||
* @param capacity The length.
|
||||
*/
|
||||
virtual void setCapacity(size_t capacity) OVERRIDE;
|
||||
virtual void setCapacity(size_t capacity) OVERRIDE FINAL;
|
||||
/**
|
||||
* Set the array length.
|
||||
* @param length The length.
|
||||
*/
|
||||
virtual void setLength(std::size_t length) OVERRIDE;
|
||||
virtual void setLength(std::size_t length) OVERRIDE FINAL;
|
||||
|
||||
/**
|
||||
* Get the introspection interface
|
||||
@@ -1368,36 +1380,36 @@ public:
|
||||
* @param number The number of elements to add.
|
||||
* @return the new length of the array.
|
||||
*/
|
||||
virtual std::size_t append(std::size_t number) FINAL;
|
||||
std::size_t append(std::size_t number);
|
||||
/**
|
||||
* Remove elements from the array.
|
||||
* @param offset The offset of the first element to remove.
|
||||
* @param number The number of elements to remove.
|
||||
* @return (false,true) if the elements were removed.
|
||||
*/
|
||||
virtual bool remove(std::size_t offset,std::size_t number) FINAL;
|
||||
bool remove(std::size_t offset,std::size_t number);
|
||||
/**
|
||||
* Compress. This removes all null elements from the array.
|
||||
*/
|
||||
virtual void compress() FINAL;
|
||||
void compress();
|
||||
|
||||
virtual const_svector view() const OVERRIDE { return value; }
|
||||
virtual void swap(const_svector &other) OVERRIDE;
|
||||
virtual void replace(const const_svector &other) OVERRIDE {
|
||||
virtual void replace(const const_svector &other) OVERRIDE FINAL {
|
||||
checkLength(other.size());
|
||||
value = other;
|
||||
PVField::postPut();
|
||||
}
|
||||
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const OVERRIDE;
|
||||
SerializableControl *pflusher) const OVERRIDE FINAL;
|
||||
virtual void deserialize(ByteBuffer *buffer,
|
||||
DeserializableControl *pflusher) OVERRIDE;
|
||||
DeserializableControl *pflusher) OVERRIDE FINAL;
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, std::size_t offset, std::size_t count) const OVERRIDE;
|
||||
SerializableControl *pflusher, std::size_t offset, std::size_t count) const OVERRIDE FINAL;
|
||||
|
||||
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE;
|
||||
virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const OVERRIDE;
|
||||
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
|
||||
virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const OVERRIDE FINAL;
|
||||
|
||||
void copy(const PVUnionArray& from);
|
||||
void copyUnchecked(const PVUnionArray& from);
|
||||
@@ -1408,6 +1420,7 @@ private:
|
||||
UnionArrayConstPtr unionArray;
|
||||
const_svector value;
|
||||
friend class PVDataCreate;
|
||||
EPICS_NOT_COPYABLE(PVValueArray)
|
||||
};
|
||||
|
||||
|
||||
@@ -1623,6 +1636,7 @@ public:
|
||||
private:
|
||||
PVDataCreate();
|
||||
FieldCreatePtr fieldCreate;
|
||||
EPICS_NOT_COPYABLE(PVDataCreate)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1650,9 +1664,3 @@ namespace std{
|
||||
}
|
||||
|
||||
#endif /* PVDATA_H */
|
||||
|
||||
/** @page Overview Documentation
|
||||
*
|
||||
* <a href = "pvDataCPP.html">pvData.html</a>
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -12,7 +12,11 @@
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
#include <epicsAssert.h>
|
||||
|
||||
#include <pv/lock.h>
|
||||
#include <pv/noDefaultMethods.h>
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/byteBuffer.h>
|
||||
@@ -21,6 +25,15 @@
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
#if defined(PVD_INTERNAL)
|
||||
# define PVD_DEPRECATED(msg)
|
||||
#elif __GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 5
|
||||
# define PVD_DEPRECATED(msg) __attribute__((deprecated(msg)))
|
||||
#else
|
||||
# define PVD_DEPRECATED(msg) EPICS_DEPRECATED
|
||||
#endif
|
||||
#define PVD_DEPRECATED_52 PVD_DEPRECATED("See https://github.com/epics-base/pvDataCPP/issues/52")
|
||||
|
||||
/* C++11 keywords
|
||||
@code
|
||||
struct Base {
|
||||
@@ -116,6 +129,13 @@ class UnionArray;
|
||||
|
||||
class BoundedString;
|
||||
|
||||
class PVField;
|
||||
class PVScalar;
|
||||
class PVScalarArray;
|
||||
class PVStructure;
|
||||
class PVUnion;
|
||||
template<typename T> class PVValueArray;
|
||||
|
||||
/**
|
||||
* typedef for a shared pointer to an immutable Field.
|
||||
*/
|
||||
@@ -339,14 +359,24 @@ public:
|
||||
*/
|
||||
virtual std::ostream& dump(std::ostream& o) const = 0;
|
||||
|
||||
//! Allocate a new instance
|
||||
//! @version Added after 7.0.0
|
||||
std::tr1::shared_ptr<PVField> build() const;
|
||||
|
||||
enum {isField=1};
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructor
|
||||
* @param type The field type.
|
||||
*/
|
||||
Field(Type type);
|
||||
void cacheCleanup();
|
||||
private:
|
||||
const Type m_fieldType;
|
||||
unsigned int m_hash;
|
||||
struct Helper;
|
||||
friend struct Helper;
|
||||
|
||||
friend class StructureArray;
|
||||
friend class Structure;
|
||||
@@ -354,8 +384,7 @@ private:
|
||||
friend class StandardField;
|
||||
friend class BasePVStructureArray;
|
||||
friend class FieldCreate;
|
||||
|
||||
struct Deleter{void operator()(Field *p){delete p;}};
|
||||
EPICS_NOT_COPYABLE(Field)
|
||||
};
|
||||
|
||||
epicsShareExtern std::ostream& operator<<(std::ostream& o, const Field& field);
|
||||
@@ -383,6 +412,10 @@ public:
|
||||
|
||||
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE;
|
||||
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
|
||||
|
||||
//! Allocate a new instance
|
||||
//! @version Added after 7.0.0
|
||||
std::tr1::shared_ptr<PVScalar> build() const;
|
||||
|
||||
protected:
|
||||
Scalar(ScalarType scalarType);
|
||||
@@ -394,6 +427,7 @@ private:
|
||||
friend class BoundedScalarArray;
|
||||
friend class FixedScalarArray;
|
||||
friend class BoundedString;
|
||||
EPICS_NOT_COPYABLE(Scalar)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -418,6 +452,7 @@ protected:
|
||||
private:
|
||||
std::size_t maxLength;
|
||||
friend class FieldCreate;
|
||||
EPICS_NOT_COPYABLE(BoundedString)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -452,6 +487,7 @@ protected:
|
||||
*/
|
||||
Array(Type type);
|
||||
|
||||
EPICS_NOT_COPYABLE(Array)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -485,13 +521,17 @@ public:
|
||||
|
||||
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE;
|
||||
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
|
||||
|
||||
//! Allocate a new instance
|
||||
//! @version Added after 7.0.0
|
||||
std::tr1::shared_ptr<PVScalarArray> build() const;
|
||||
|
||||
protected:
|
||||
virtual ~ScalarArray();
|
||||
private:
|
||||
const std::string getIDScalarArrayLUT() const;
|
||||
ScalarType elementType;
|
||||
friend class FieldCreate;
|
||||
EPICS_NOT_COPYABLE(ScalarArray)
|
||||
};
|
||||
|
||||
|
||||
@@ -521,11 +561,11 @@ public:
|
||||
|
||||
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
|
||||
|
||||
protected:
|
||||
virtual ~BoundedScalarArray();
|
||||
private:
|
||||
std::size_t size;
|
||||
friend class FieldCreate;
|
||||
EPICS_NOT_COPYABLE(BoundedScalarArray)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -553,11 +593,11 @@ public:
|
||||
|
||||
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
|
||||
|
||||
protected:
|
||||
virtual ~FixedScalarArray();
|
||||
private:
|
||||
std::size_t size;
|
||||
friend class FieldCreate;
|
||||
EPICS_NOT_COPYABLE(FixedScalarArray)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -587,16 +627,22 @@ public:
|
||||
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
|
||||
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
|
||||
|
||||
//! Allocate a new instance
|
||||
//! @version Added after 7.0.0
|
||||
std::tr1::shared_ptr<PVValueArray<std::tr1::shared_ptr<PVStructure> > > build() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructor.
|
||||
* @param structure The introspection interface for the elements.
|
||||
*/
|
||||
StructureArray(StructureConstPtr const & structure);
|
||||
public:
|
||||
virtual ~StructureArray();
|
||||
private:
|
||||
StructureConstPtr pstructure;
|
||||
friend class FieldCreate;
|
||||
EPICS_NOT_COPYABLE(StructureArray)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -626,16 +672,22 @@ public:
|
||||
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
|
||||
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
|
||||
|
||||
//! Allocate a new instance
|
||||
//! @version Added after 7.0.0
|
||||
std::tr1::shared_ptr<PVValueArray<std::tr1::shared_ptr<PVUnion> > > build() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructor.
|
||||
* @param _punion The introspection interface for the elements.
|
||||
*/
|
||||
UnionArray(UnionConstPtr const & _punion);
|
||||
public:
|
||||
virtual ~UnionArray();
|
||||
private:
|
||||
UnionConstPtr punion;
|
||||
friend class FieldCreate;
|
||||
EPICS_NOT_COPYABLE(UnionArray)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -666,40 +718,96 @@ public:
|
||||
* @return The number of fields.
|
||||
*/
|
||||
std::size_t getNumberFields() const {return fieldNames.size();}
|
||||
|
||||
/**
|
||||
* Get the field for the specified fieldName.
|
||||
* @param fieldName The name of the field to get;
|
||||
* @return The introspection interface.
|
||||
* This will hold a null pointer if the field is not in the structure.
|
||||
* Lookup Field by name
|
||||
* @param fieldName Member field name. May not contain '.'
|
||||
* @return NULL if no member by this name.
|
||||
*/
|
||||
FieldConstPtr getField(std::string const &fieldName) const;
|
||||
|
||||
/** Lookup Field by name and cast to Field sub-class.
|
||||
* @param fieldName Member field name. May not contain '.'
|
||||
* @return NULL If no member by this name, or member exists, but has type other than FT.
|
||||
*/
|
||||
template<typename FT>
|
||||
std::tr1::shared_ptr<const FT> getField(std::string const &fieldName) const
|
||||
{
|
||||
FieldConstPtr field(getField(fieldName));
|
||||
if (field)
|
||||
return std::tr1::dynamic_pointer_cast<const FT>(field);
|
||||
else
|
||||
return std::tr1::shared_ptr<const FT>();
|
||||
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
|
||||
return std::tr1::dynamic_pointer_cast<const FT>(getField(fieldName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the field for the specified fieldName.
|
||||
* @param index The index of the field to get;
|
||||
* @return The introspection interface.
|
||||
* This will hold a null pointer if the field is not in the structure.
|
||||
* Lookup Field by name
|
||||
* @param fieldName Member field name. May not contain '.'
|
||||
* @return Field pointer (never NULL)
|
||||
* @throws std::runtime_error If no member by this name
|
||||
*/
|
||||
FieldConstPtr getFieldT(std::string const &fieldName) const {return getFieldImpl(fieldName, true);};
|
||||
|
||||
/** Lookup Field by name and cast to Field sub-class.
|
||||
* @param fieldName Member field name. May not contain '.'
|
||||
* @return Field pointer (never NULL)
|
||||
* @throws std::runtime_error If no member by this name, or member exists, but has type other than FT.
|
||||
*/
|
||||
template<typename FT>
|
||||
std::tr1::shared_ptr<const FT> getFieldT(std::string const &fieldName) const
|
||||
{
|
||||
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
|
||||
std::tr1::shared_ptr<const FT> result(
|
||||
std::tr1::dynamic_pointer_cast<const FT>(getFieldT(fieldName))
|
||||
);
|
||||
|
||||
if (!result)
|
||||
throw std::runtime_error("Wrong Field type");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Lookup Field by index, within this Structure.
|
||||
* @param index Index of member in this structure. @code index>=0 && index<getNumberFields() @endcode
|
||||
* @return Field pointer (never NULL)
|
||||
* @throws std::out_of_range If index >= getNumberFields()
|
||||
*/
|
||||
const FieldConstPtr& getField(std::size_t index) const {return fields.at(index);}
|
||||
|
||||
/** Lookup Field by index, within this Structure.
|
||||
* @param index Index of member in this structure. @code index>=0 && index<getNumberFields() @endcode
|
||||
* @return NULL if member is not a sub-class of FT
|
||||
* @throws std::out_of_range If index >= getNumberFields()
|
||||
*/
|
||||
template<typename FT>
|
||||
std::tr1::shared_ptr<const FT> getField(std::size_t index) const
|
||||
{
|
||||
const FieldConstPtr& field(getField(index));
|
||||
if (field)
|
||||
return std::tr1::dynamic_pointer_cast<const FT>(field);
|
||||
else
|
||||
return std::tr1::shared_ptr<const FT>();
|
||||
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
|
||||
return std::tr1::dynamic_pointer_cast<const FT>(getField(index));
|
||||
}
|
||||
|
||||
/** Lookup Field by index, within this Structure.
|
||||
* @param index Index of member in this structure. @code index>=0 && index<getNumberFields() @endcode
|
||||
* @return Field pointer (never NULL)
|
||||
* @throws std::out_of_range If index >= getNumberFields()
|
||||
*/
|
||||
FieldConstPtr getFieldT(std::size_t index) const {return fields.at(index);}
|
||||
|
||||
/** Lookup Field by index, within this Structure.
|
||||
* @param index Index of member in this structure. @code index>=0 && index<getNumberFields() @endcode
|
||||
* @return Field pointer (never NULL)
|
||||
* @throws std::out_of_range If index >= getNumberFields()
|
||||
* @throws std::runtime_error If member is not a sub-class of FT
|
||||
*/
|
||||
template<typename FT>
|
||||
std::tr1::shared_ptr<const FT> getFieldT(std::size_t index) const
|
||||
{
|
||||
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
|
||||
std::tr1::shared_ptr<const FT> result(
|
||||
std::tr1::dynamic_pointer_cast<const FT>(getFieldT(index))
|
||||
);
|
||||
|
||||
if (!result)
|
||||
throw std::runtime_error("Wrong Field type");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -731,7 +839,11 @@ public:
|
||||
|
||||
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
|
||||
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
|
||||
|
||||
|
||||
//! Allocate a new instance
|
||||
//! @version Added after 7.0.0
|
||||
std::tr1::shared_ptr<PVStructure> build() const;
|
||||
|
||||
protected:
|
||||
Structure(StringArray const & fieldNames, FieldConstPtrArray const & fields, std::string const & id = defaultId());
|
||||
private:
|
||||
@@ -739,10 +851,12 @@ private:
|
||||
FieldConstPtrArray fields;
|
||||
std::string id;
|
||||
|
||||
FieldConstPtr getFieldImpl(const std::string& fieldName, bool throws) const;
|
||||
void dumpFields(std::ostream& o) const;
|
||||
|
||||
friend class FieldCreate;
|
||||
friend class Union;
|
||||
EPICS_NOT_COPYABLE(Structure)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -784,40 +898,96 @@ public:
|
||||
* @return The number of fields.
|
||||
*/
|
||||
std::size_t getNumberFields() const {return fieldNames.size();}
|
||||
|
||||
/**
|
||||
* Get the field for the specified fieldName.
|
||||
* @param fieldName The name of the field to get;
|
||||
* @return The introspection interface.
|
||||
* This will hold a null pointer if the field is not in the union.
|
||||
* Lookup Field by name
|
||||
* @param fieldName Member field name. May not contain '.'
|
||||
* @return NULL if no member by this name.
|
||||
*/
|
||||
FieldConstPtr getField(std::string const &fieldName) const;
|
||||
|
||||
/** Lookup Field by name and cast to Field sub-class.
|
||||
* @param fieldName Member field name. May not contain '.'
|
||||
* @return NULL If no member by this name, or member exists, but has type other than FT.
|
||||
*/
|
||||
template<typename FT>
|
||||
std::tr1::shared_ptr<const FT> getField(std::string const &fieldName) const
|
||||
{
|
||||
FieldConstPtr field = getField(fieldName);
|
||||
if (field)
|
||||
return std::tr1::dynamic_pointer_cast<const FT>(field);
|
||||
else
|
||||
return std::tr1::shared_ptr<const FT>();
|
||||
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
|
||||
return std::tr1::dynamic_pointer_cast<const FT>(getField(fieldName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the field for the specified fieldName.
|
||||
* @param index The index of the field to get;
|
||||
* @return The introspection interface.
|
||||
* This will hold a null pointer if the field is not in the union.
|
||||
* Lookup Field by name
|
||||
* @param fieldName Member field name. May not contain '.'
|
||||
* @return Field pointer (never NULL)
|
||||
* @throws std::runtime_error If no member by this name
|
||||
*/
|
||||
FieldConstPtr getFieldT(std::string const &fieldName) const {return getFieldImpl(fieldName, true);};
|
||||
|
||||
/** Lookup Field by name and cast to Field sub-class.
|
||||
* @param fieldName Member field name. May not contain '.'
|
||||
* @return Field pointer (never NULL)
|
||||
* @throws std::runtime_error If no member by this name, or member exists, but has type other than FT.
|
||||
*/
|
||||
template<typename FT>
|
||||
std::tr1::shared_ptr<const FT> getFieldT(std::string const &fieldName) const
|
||||
{
|
||||
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
|
||||
std::tr1::shared_ptr<const FT> result(
|
||||
std::tr1::dynamic_pointer_cast<const FT>(getFieldT(fieldName))
|
||||
);
|
||||
|
||||
if (!result)
|
||||
throw std::runtime_error("Wrong Field type");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Lookup Field by index, within this Union.
|
||||
* @param index Index of member in this union. @code index>=0 && index<getNumberFields() @endcode
|
||||
* @return Field pointer (never NULL)
|
||||
* @throws std::out_of_range If index >= getNumberFields()
|
||||
*/
|
||||
FieldConstPtr getField(std::size_t index) const {return fields.at(index);}
|
||||
|
||||
/** Lookup Field by index, within this Union.
|
||||
* @param index Index of member in this union. @code index>=0 && index<getNumberFields() @endcode
|
||||
* @return NULL if member is not a sub-class of FT
|
||||
* @throws std::out_of_range If index >= getNumberFields()
|
||||
*/
|
||||
template<typename FT>
|
||||
std::tr1::shared_ptr<const FT> getField(std::size_t index) const
|
||||
{
|
||||
FieldConstPtr field = getField(index);
|
||||
if (field)
|
||||
return std::tr1::dynamic_pointer_cast<const FT>(field);
|
||||
else
|
||||
return std::tr1::shared_ptr<const FT>();
|
||||
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
|
||||
return std::tr1::dynamic_pointer_cast<const FT>(getField(index));
|
||||
}
|
||||
|
||||
/** Lookup Field by index, within this Union.
|
||||
* @param index Index of member in this union. @code index>=0 && index<getNumberFields() @endcode
|
||||
* @return Field pointer (never NULL)
|
||||
* @throws std::out_of_range If index >= getNumberFields()
|
||||
*/
|
||||
FieldConstPtr getFieldT(std::size_t index) const {return fields.at(index);}
|
||||
|
||||
/** Lookup Field by index, within this Structure.
|
||||
* @param index Index of member in this structure. @code index>=0 && index<getNumberFields() @endcode
|
||||
* @return Field pointer (never NULL)
|
||||
* @throws std::out_of_range If index >= getNumberFields()
|
||||
* @throws std::runtime_error If member is not a sub-class of FT
|
||||
*/
|
||||
template<typename FT>
|
||||
std::tr1::shared_ptr<const FT> getFieldT(std::size_t index) const
|
||||
{
|
||||
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
|
||||
std::tr1::shared_ptr<const FT> result(
|
||||
std::tr1::dynamic_pointer_cast<const FT>(getFieldT(index))
|
||||
);
|
||||
|
||||
if (!result)
|
||||
throw std::runtime_error("Wrong Field type");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -865,6 +1035,10 @@ public:
|
||||
|
||||
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
|
||||
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
|
||||
|
||||
//! Allocate a new instance
|
||||
//! @version Added after 7.0.0
|
||||
std::tr1::shared_ptr<PVUnion> build() const;
|
||||
|
||||
protected:
|
||||
Union();
|
||||
@@ -873,11 +1047,13 @@ private:
|
||||
StringArray fieldNames;
|
||||
FieldConstPtrArray fields;
|
||||
std::string id;
|
||||
|
||||
|
||||
FieldConstPtr getFieldImpl(const std::string& fieldName, bool throws) const;
|
||||
void dumpFields(std::ostream& o) const;
|
||||
|
||||
friend class FieldCreate;
|
||||
friend class Structure;
|
||||
EPICS_NOT_COPYABLE(Union)
|
||||
};
|
||||
|
||||
class FieldCreate;
|
||||
@@ -897,6 +1073,12 @@ class epicsShareClass FieldBuilder :
|
||||
public std::tr1::enable_shared_from_this<FieldBuilder>
|
||||
{
|
||||
public:
|
||||
//! Create a new instance of in-line @c Field builder.
|
||||
//! @version Added after 7.0.0
|
||||
static FieldBuilderPtr begin();
|
||||
//! Create a new instance of in-line @c Field builder pre-initialized with and existing Structure
|
||||
static FieldBuilderPtr begin(StructureConstPtr S);
|
||||
|
||||
/**
|
||||
* Set ID of an object to be created.
|
||||
* @param id id to be set.
|
||||
@@ -918,7 +1100,7 @@ public:
|
||||
* @param maxLength a string maximum length.
|
||||
* @return this instance of a @c FieldBuilder.
|
||||
*/
|
||||
FieldBuilderPtr addBoundedString(std::string const & name, std::size_t maxLength);
|
||||
FieldBuilderPtr addBoundedString(std::string const & name, std::size_t maxLength) PVD_DEPRECATED_52;
|
||||
|
||||
/**
|
||||
* Add a @c Field (e.g. @c Structure, @c Union).
|
||||
@@ -943,7 +1125,7 @@ public:
|
||||
* @param size Array fixed size.
|
||||
* @return this instance of a @c FieldBuilder.
|
||||
*/
|
||||
FieldBuilderPtr addFixedArray(std::string const & name, ScalarType scalarType, std::size_t size);
|
||||
FieldBuilderPtr addFixedArray(std::string const & name, ScalarType scalarType, std::size_t size) PVD_DEPRECATED_52;
|
||||
|
||||
/**
|
||||
* Add bounded-size array of @c Scalar elements.
|
||||
@@ -952,7 +1134,7 @@ public:
|
||||
* @param bound Array maximum capacity (size).
|
||||
* @return this instance of a @c FieldBuilder.
|
||||
*/
|
||||
FieldBuilderPtr addBoundedArray(std::string const & name, ScalarType scalarType, std::size_t bound);
|
||||
FieldBuilderPtr addBoundedArray(std::string const & name, ScalarType scalarType, std::size_t bound) PVD_DEPRECATED_52;
|
||||
|
||||
/**
|
||||
* Add array of @c Field elements.
|
||||
@@ -1092,7 +1274,7 @@ public:
|
||||
* @return a @c BoundedString interface for the newly created object.
|
||||
* @throws IllegalArgumentException if maxLength == 0.
|
||||
*/
|
||||
BoundedStringConstPtr createBoundedString(std::size_t maxLength) const;
|
||||
BoundedStringConstPtr createBoundedString(std::size_t maxLength) const PVD_DEPRECATED_52;
|
||||
/**
|
||||
* Create an @c Array field, variable size array.
|
||||
* @param elementType The @c ScalarType for array elements
|
||||
@@ -1105,14 +1287,14 @@ public:
|
||||
* @param size Fixed array size.
|
||||
* @return An @c Array Interface for the newly created object.
|
||||
*/
|
||||
ScalarArrayConstPtr createFixedScalarArray(ScalarType elementType, std::size_t size) const;
|
||||
ScalarArrayConstPtr createFixedScalarArray(ScalarType elementType, std::size_t size) const PVD_DEPRECATED_52;
|
||||
/**
|
||||
* Create an @c Array field, bounded size array.
|
||||
* @param elementType The @c ScalarType for array elements
|
||||
* @param bound Array maximum capacity.
|
||||
* @return An @c Array Interface for the newly created object.
|
||||
*/
|
||||
ScalarArrayConstPtr createBoundedScalarArray(ScalarType elementType, std::size_t bound) const;
|
||||
ScalarArrayConstPtr createBoundedScalarArray(ScalarType elementType, std::size_t bound) const PVD_DEPRECATED_52;
|
||||
/**
|
||||
* Create an @c Array field that is has element type @c Structure
|
||||
* @param structure The @c Structure for each array element.
|
||||
@@ -1211,11 +1393,20 @@ public:
|
||||
|
||||
private:
|
||||
FieldCreate();
|
||||
|
||||
|
||||
// const after ctor
|
||||
std::vector<ScalarConstPtr> scalars;
|
||||
std::vector<ScalarArrayConstPtr> scalarArrays;
|
||||
UnionConstPtr variantUnion;
|
||||
UnionArrayConstPtr variantUnionArray;
|
||||
|
||||
mutable Mutex mutex;
|
||||
typedef std::multimap<unsigned int, Field*> cache_t;
|
||||
mutable cache_t cache;
|
||||
|
||||
struct Helper;
|
||||
friend class Field;
|
||||
EPICS_NOT_COPYABLE(FieldCreate)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1267,32 +1458,35 @@ OP(pvDouble, double)
|
||||
OP(pvString, std::string)
|
||||
#undef OP
|
||||
|
||||
bool epicsShareExtern operator==(const Field&, const Field&);
|
||||
bool epicsShareExtern operator==(const Scalar&, const Scalar&);
|
||||
bool epicsShareExtern operator==(const ScalarArray&, const ScalarArray&);
|
||||
bool epicsShareExtern operator==(const Structure&, const Structure&);
|
||||
bool epicsShareExtern operator==(const StructureArray&, const StructureArray&);
|
||||
bool epicsShareExtern operator==(const Union&, const Union&);
|
||||
bool epicsShareExtern operator==(const UnionArray&, const UnionArray&);
|
||||
bool epicsShareExtern operator==(const BoundedString&, const BoundedString&);
|
||||
bool epicsShareExtern compare(const Field&, const Field&);
|
||||
bool epicsShareExtern compare(const Scalar&, const Scalar&);
|
||||
bool epicsShareExtern compare(const ScalarArray&, const ScalarArray&);
|
||||
bool epicsShareExtern compare(const Structure&, const Structure&);
|
||||
bool epicsShareExtern compare(const StructureArray&, const StructureArray&);
|
||||
bool epicsShareExtern compare(const Union&, const Union&);
|
||||
bool epicsShareExtern compare(const UnionArray&, const UnionArray&);
|
||||
bool epicsShareExtern compare(const BoundedString&, const BoundedString&);
|
||||
|
||||
static inline bool operator!=(const Field& a, const Field& b)
|
||||
{return !(a==b);}
|
||||
static inline bool operator!=(const Scalar& a, const Scalar& b)
|
||||
{return !(a==b);}
|
||||
static inline bool operator!=(const ScalarArray& a, const ScalarArray& b)
|
||||
{return !(a==b);}
|
||||
static inline bool operator!=(const Structure& a, const Structure& b)
|
||||
{return !(a==b);}
|
||||
static inline bool operator!=(const StructureArray& a, const StructureArray& b)
|
||||
{return !(a==b);}
|
||||
static inline bool operator!=(const Union& a, const Union& b)
|
||||
{return !(a==b);}
|
||||
static inline bool operator!=(const UnionArray& a, const UnionArray& b)
|
||||
{return !(a==b);}
|
||||
static inline bool operator!=(const BoundedString& a, const BoundedString& b)
|
||||
{return !(a==b);}
|
||||
/** Equality with other Field
|
||||
*
|
||||
* The creation process of class FieldCreate ensures that identical field definitions
|
||||
* will share the same instance. So pointer equality is sufficient to show defintion
|
||||
* equality. If in doubt, compare() will do an full test.
|
||||
*/
|
||||
#define MAKE_COMPARE(CLASS) \
|
||||
static FORCE_INLINE bool operator==(const CLASS& a, const CLASS& b) {return (void*)&a==(void*)&b;} \
|
||||
static FORCE_INLINE bool operator!=(const CLASS& a, const CLASS& b) {return !(a==b);}
|
||||
|
||||
MAKE_COMPARE(Field)
|
||||
MAKE_COMPARE(Scalar)
|
||||
MAKE_COMPARE(ScalarArray)
|
||||
MAKE_COMPARE(Structure)
|
||||
MAKE_COMPARE(StructureArray)
|
||||
MAKE_COMPARE(Union)
|
||||
MAKE_COMPARE(UnionArray)
|
||||
MAKE_COMPARE(BoundedString)
|
||||
|
||||
#undef MAKE_COMPARE
|
||||
}}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,10 +14,6 @@
|
||||
#ifndef PVTYPE_H
|
||||
#define PVTYPE_H
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && !defined(_MINGW)
|
||||
#pragma warning( push )
|
||||
#pragma warning(disable: 4251)
|
||||
@@ -34,6 +30,16 @@ typedef unsigned int uintptr_t;
|
||||
#define INT64_MAX (0x7fffffffffffffffLL)
|
||||
#define UINT64_MAX (0xffffffffffffffffULL)
|
||||
#endif
|
||||
#elif _MSC_VER==1500
|
||||
#include <epicsTypes.h>
|
||||
typedef epicsUInt8 uint8_t;
|
||||
typedef epicsInt8 int8_t;
|
||||
typedef epicsUInt16 uint16_t;
|
||||
typedef epicsInt16 int16_t;
|
||||
typedef epicsUInt32 uint32_t;
|
||||
typedef epicsInt32 int32_t;
|
||||
typedef epicsUInt64 uint64_t;
|
||||
typedef epicsInt64 int64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
@@ -61,6 +61,7 @@ typedef std::tr1::shared_ptr<StandardField> StandardFieldPtr;
|
||||
* }
|
||||
*/
|
||||
class epicsShareClass StandardField {
|
||||
static void once(void*);
|
||||
public:
|
||||
/**
|
||||
* getStandardField returns the singleton.
|
||||
@@ -131,123 +132,105 @@ public:
|
||||
* create an alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr alarm();
|
||||
inline const StructureConstPtr& alarm() const { return alarmField; }
|
||||
/**
|
||||
* create a timeStamp structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr timeStamp();
|
||||
inline const StructureConstPtr& timeStamp() const { return timeStampField; }
|
||||
/**
|
||||
* create a display structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr display();
|
||||
inline const StructureConstPtr& display() const { return displayField; }
|
||||
/**
|
||||
* create a control structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr control();
|
||||
inline const StructureConstPtr& control() const { return controlField; }
|
||||
/**
|
||||
* create a boolean alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr booleanAlarm();
|
||||
inline const StructureConstPtr& booleanAlarm() const { return booleanAlarmField; }
|
||||
/**
|
||||
* create a byte alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr byteAlarm();
|
||||
inline const StructureConstPtr& byteAlarm() const { return byteAlarmField; }
|
||||
/**
|
||||
* create a unsigned byte alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr ubyteAlarm();
|
||||
inline const StructureConstPtr& ubyteAlarm() const { return ubyteAlarmField; }
|
||||
/**
|
||||
* create a short alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr shortAlarm();
|
||||
inline const StructureConstPtr& shortAlarm() const { return shortAlarmField; }
|
||||
/**
|
||||
* create a unsigned short alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr ushortAlarm();
|
||||
inline const StructureConstPtr& ushortAlarm() const { return ushortAlarmField; }
|
||||
/**
|
||||
* create an int alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr intAlarm();
|
||||
inline const StructureConstPtr& intAlarm() const { return intAlarmField; }
|
||||
/**
|
||||
* create a unsigned int alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr uintAlarm();
|
||||
inline const StructureConstPtr& uintAlarm() const { return uintAlarmField; }
|
||||
/**
|
||||
* create a long alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr longAlarm();
|
||||
inline const StructureConstPtr& longAlarm() const { return longAlarmField; }
|
||||
/**
|
||||
* create a unsigned long alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr ulongAlarm();
|
||||
inline const StructureConstPtr& ulongAlarm() const { return ulongAlarmField; }
|
||||
/**
|
||||
* create a float alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr floatAlarm();
|
||||
inline const StructureConstPtr& floatAlarm() const { return floatAlarmField; }
|
||||
/**
|
||||
* create a double alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr doubleAlarm();
|
||||
inline const StructureConstPtr& doubleAlarm() const { return doubleAlarmField; }
|
||||
/**
|
||||
* create an enumerated alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr enumeratedAlarm();
|
||||
inline const StructureConstPtr& enumeratedAlarm() const { return enumeratedAlarmField; }
|
||||
private:
|
||||
StandardField();
|
||||
void init();
|
||||
StructureConstPtr createProperties(
|
||||
std::string id,FieldConstPtr field,std::string properties);
|
||||
FieldCreatePtr fieldCreate;
|
||||
std::string notImplemented;
|
||||
std::string valueFieldName;
|
||||
StructureConstPtr alarmField;
|
||||
StructureConstPtr timeStampField;
|
||||
StructureConstPtr displayField;
|
||||
StructureConstPtr controlField;
|
||||
StructureConstPtr booleanAlarmField;
|
||||
StructureConstPtr byteAlarmField;
|
||||
StructureConstPtr shortAlarmField;
|
||||
StructureConstPtr intAlarmField;
|
||||
StructureConstPtr longAlarmField;
|
||||
StructureConstPtr ubyteAlarmField;
|
||||
StructureConstPtr ushortAlarmField;
|
||||
StructureConstPtr uintAlarmField;
|
||||
StructureConstPtr ulongAlarmField;
|
||||
StructureConstPtr floatAlarmField;
|
||||
StructureConstPtr doubleAlarmField;
|
||||
StructureConstPtr enumeratedAlarmField;
|
||||
void createAlarm();
|
||||
void createTimeStamp();
|
||||
void createDisplay();
|
||||
void createControl();
|
||||
void createBooleanAlarm();
|
||||
void createByteAlarm();
|
||||
void createShortAlarm();
|
||||
void createIntAlarm();
|
||||
void createLongAlarm();
|
||||
void createUByteAlarm();
|
||||
void createUShortAlarm();
|
||||
void createUIntAlarm();
|
||||
void createULongAlarm();
|
||||
void createFloatAlarm();
|
||||
void createDoubleAlarm();
|
||||
void createEnumeratedAlarm();
|
||||
//friend StandardFieldPtr getStandardField();
|
||||
const FieldCreatePtr fieldCreate;
|
||||
const std::string notImplemented;
|
||||
const std::string valueFieldName;
|
||||
const StructureConstPtr alarmField;
|
||||
const StructureConstPtr timeStampField;
|
||||
const StructureConstPtr displayField;
|
||||
const StructureConstPtr controlField;
|
||||
const StructureConstPtr booleanAlarmField;
|
||||
const StructureConstPtr byteAlarmField;
|
||||
const StructureConstPtr shortAlarmField;
|
||||
const StructureConstPtr intAlarmField;
|
||||
const StructureConstPtr longAlarmField;
|
||||
const StructureConstPtr ubyteAlarmField;
|
||||
const StructureConstPtr ushortAlarmField;
|
||||
const StructureConstPtr uintAlarmField;
|
||||
const StructureConstPtr ulongAlarmField;
|
||||
const StructureConstPtr floatAlarmField;
|
||||
const StructureConstPtr doubleAlarmField;
|
||||
const StructureConstPtr enumeratedAlarmField;
|
||||
};
|
||||
|
||||
FORCE_INLINE const StandardFieldPtr& getStandardField() {
|
||||
|
||||
@@ -74,7 +74,7 @@ struct ValueBuilder::child_scalar_array : public ValueBuilder::child
|
||||
{
|
||||
builder->addArray(name, array.original_type());
|
||||
}
|
||||
virtual void store(const PVFieldPtr& val)
|
||||
virtual void store(const PVFieldPtr& val) OVERRIDE FINAL
|
||||
{
|
||||
if(val->getField()->getType()!=scalarArray)
|
||||
THROW_EXCEPTION2(std::logic_error, "Scalar Array type mis-match");
|
||||
@@ -285,7 +285,7 @@ PVStructure::shared_pointer ValueBuilder::buildPVStructure() const
|
||||
type = tbuild->createStructure();
|
||||
}
|
||||
|
||||
PVStructure::shared_pointer root(getPVDataCreate()->createPVStructure(type));
|
||||
PVStructure::shared_pointer root(type->build());
|
||||
|
||||
child_struct::storeStruct(*this, root);
|
||||
|
||||
|
||||
@@ -6,8 +6,3 @@ TESTPROD_HOST += testCreateRequest
|
||||
testCreateRequest_SRCS = testCreateRequest.cpp
|
||||
testHarness_SRCS += testCreateRequest.cpp
|
||||
TESTS += testCreateRequest
|
||||
|
||||
TESTPROD_HOST += testPVCopy
|
||||
testPVCopy_SRCS += testPVCopy.cpp
|
||||
testHarness_SRCS += testPVCopy.cpp
|
||||
TESTS += testPVCopy
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
#include <pv/pvUnitTest.h>
|
||||
#include <testMain.h>
|
||||
|
||||
#include <pv/current_function.h>
|
||||
#include <pv/createRequest.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -335,15 +337,397 @@ static void testBadRequest()
|
||||
|
||||
testOk1(!!C->createRequest("field(value)"));
|
||||
testOk1(C->getMessage().empty());
|
||||
|
||||
// duplicate fieldName C
|
||||
// correct is: "field(A,C{D,E.F})"
|
||||
testThrows(std::invalid_argument, createRequest("field(A,C.D,C.E.F)"));
|
||||
}
|
||||
|
||||
static
|
||||
StructureConstPtr maskingType = getFieldCreate()->createFieldBuilder()
|
||||
->add("A", pvInt)
|
||||
->add("B", pvInt)
|
||||
->addNestedStructure("C")
|
||||
->add("D", pvInt)
|
||||
->addNestedStructure("E")
|
||||
->add("F", pvInt)
|
||||
->endNested()
|
||||
->endNested()
|
||||
->createStructure();
|
||||
|
||||
static
|
||||
void testMapper(PVRequestMapper::mode_t mode)
|
||||
{
|
||||
testDiag("=== %s mode==%d", CURRENT_FUNCTION, (int)mode);
|
||||
{
|
||||
testDiag("Map full structure");
|
||||
|
||||
PVStructurePtr base(getPVDataCreate()->createPVStructure(maskingType));
|
||||
PVRequestMapper mapper(*base, *createRequest(""), mode);
|
||||
|
||||
testEqual(mapper.requested(), maskingType);
|
||||
|
||||
testEqual(mapper.requestedMask(), BitSet().set(0)
|
||||
.set(base->getSubFieldT("A")->getFieldOffset())
|
||||
.set(base->getSubFieldT("B")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.D")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
|
||||
PVStructurePtr req(getPVDataCreate()->createPVStructure(mapper.requested()));
|
||||
|
||||
base->getSubFieldT<PVInt>("A")->put(1);
|
||||
base->getSubFieldT<PVInt>("B")->put(42);
|
||||
|
||||
BitSet output;
|
||||
mapper.copyBaseToRequested(*base, BitSet().set(0), *req, output);
|
||||
|
||||
testFieldEqual<PVInt>(req, "A", 1);
|
||||
testFieldEqual<PVInt>(req, "B", 42);
|
||||
|
||||
req->getSubFieldT<PVInt>("A")->put(2);
|
||||
req->getSubFieldT<PVInt>("B")->put(43);
|
||||
|
||||
mapper.copyBaseFromRequested(*base, output, *req, BitSet().set(0));
|
||||
|
||||
testFieldEqual<PVInt>(req, "A", 2);
|
||||
testFieldEqual<PVInt>(req, "B", 43);
|
||||
testEqual(mapper.requestedMask(), BitSet().set(0).set(1).set(2).set(3).set(4).set(5).set(6));
|
||||
}
|
||||
{
|
||||
testDiag("Map single leaf field");
|
||||
|
||||
PVStructurePtr base(getPVDataCreate()->createPVStructure(maskingType));
|
||||
PVRequestMapper mapper(*base, *createRequest("field(B)"), mode);
|
||||
|
||||
if(mode==PVRequestMapper::Slice)
|
||||
testNotEqual(mapper.requested(), maskingType);
|
||||
else
|
||||
testEqual(mapper.requested(), maskingType);
|
||||
|
||||
testEqual(mapper.requestedMask(), BitSet().set(0)
|
||||
.set(base->getSubFieldT("B")->getFieldOffset()));
|
||||
|
||||
PVStructurePtr req(getPVDataCreate()->createPVStructure(mapper.requested()));
|
||||
|
||||
if(mode==PVRequestMapper::Slice)
|
||||
testOk1(!req->getSubField("A"));
|
||||
|
||||
base->getSubFieldT<PVScalar>("A")->putFrom<int32>(11);
|
||||
base->getSubFieldT<PVScalar>("B")->putFrom<int32>(42);
|
||||
|
||||
BitSet output;
|
||||
mapper.copyBaseToRequested(*base, BitSet().set(0), *req, output);
|
||||
|
||||
if(mode!=PVRequestMapper::Slice)
|
||||
testFieldEqual<PVInt>(req, "A", 0);
|
||||
testFieldEqual<PVInt>(req, "B", 42);
|
||||
|
||||
req->getSubFieldT<PVScalar>("B")->putFrom<int32>(43);
|
||||
|
||||
mapper.copyBaseFromRequested(*base, output, *req, BitSet().set(0));
|
||||
|
||||
testFieldEqual<PVInt>(req, "B", 43);
|
||||
testEqual(mapper.requestedMask(), BitSet().set(0)
|
||||
.set(base->getSubFieldT("B")->getFieldOffset()));
|
||||
|
||||
BitSet cmp;
|
||||
mapper.maskBaseToRequested(BitSet().set(base->getSubFieldT("B")->getFieldOffset()), cmp);
|
||||
testEqual(cmp, BitSet()
|
||||
.set(req->getSubFieldT("B")->getFieldOffset()));
|
||||
|
||||
cmp.clear();
|
||||
mapper.maskBaseFromRequested(cmp, BitSet()
|
||||
.set(req->getSubFieldT("B")->getFieldOffset()));
|
||||
testEqual(cmp, BitSet()
|
||||
.set(base->getSubFieldT("B")->getFieldOffset()));
|
||||
}
|
||||
{
|
||||
testDiag("Map two sub-fields");
|
||||
|
||||
PVStructurePtr base(getPVDataCreate()->createPVStructure(maskingType));
|
||||
PVRequestMapper mapper(*base, *createRequest("B,C.D"), mode);
|
||||
|
||||
if(mode==PVRequestMapper::Slice)
|
||||
testNotEqual(mapper.requested(), maskingType);
|
||||
else
|
||||
testEqual(mapper.requested(), maskingType);
|
||||
|
||||
testEqual(mapper.requestedMask(), BitSet().set(0)
|
||||
.set(base->getSubFieldT("B")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.D")->getFieldOffset()));
|
||||
|
||||
PVStructurePtr req(getPVDataCreate()->createPVStructure(mapper.requested()));
|
||||
|
||||
if(mode==PVRequestMapper::Slice)
|
||||
testOk1(!req->getSubField("A"));
|
||||
|
||||
base->getSubFieldT<PVScalar>("A")->putFrom<int32>(11);
|
||||
base->getSubFieldT<PVScalar>("B")->putFrom<int32>(1);
|
||||
base->getSubFieldT<PVScalar>("C.D")->putFrom<int32>(42);
|
||||
|
||||
BitSet output;
|
||||
mapper.copyBaseToRequested(*base, BitSet().set(0), *req, output);
|
||||
|
||||
if(mode!=PVRequestMapper::Slice)
|
||||
testFieldEqual<PVInt>(req, "A", 0);
|
||||
testFieldEqual<PVInt>(req, "B", 1);
|
||||
testFieldEqual<PVInt>(req, "C.D", 42);
|
||||
|
||||
req->getSubFieldT<PVScalar>("B")->putFrom<int32>(2);
|
||||
req->getSubFieldT<PVScalar>("C.D")->putFrom<int32>(43);
|
||||
|
||||
mapper.copyBaseFromRequested(*base, output, *req, BitSet().set(0));
|
||||
|
||||
testFieldEqual<PVInt>(req, "B", 2);
|
||||
testFieldEqual<PVInt>(req, "C.D", 43);
|
||||
testEqual(mapper.requestedMask(), BitSet().set(0)
|
||||
.set(base->getSubFieldT("B")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.D")->getFieldOffset()));
|
||||
}
|
||||
{
|
||||
testDiag("Map entire sub-structure");
|
||||
|
||||
PVStructurePtr base(getPVDataCreate()->createPVStructure(maskingType));
|
||||
PVRequestMapper mapper(*base, *createRequest("field(C.E)"), mode);
|
||||
|
||||
if(mode==PVRequestMapper::Slice)
|
||||
testNotEqual(mapper.requested(), maskingType);
|
||||
else
|
||||
testEqual(mapper.requested(), maskingType);
|
||||
|
||||
testEqual(mapper.requestedMask(), BitSet().set(0)
|
||||
.set(base->getSubFieldT("C")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
|
||||
|
||||
PVStructurePtr req(getPVDataCreate()->createPVStructure(mapper.requested()));
|
||||
|
||||
if(mode==PVRequestMapper::Slice)
|
||||
testOk1(!req->getSubField("A"));
|
||||
|
||||
base->getSubFieldT<PVScalar>("A")->putFrom<int32>(11);
|
||||
base->getSubFieldT<PVScalar>("C.E.F")->putFrom<int32>(42);
|
||||
|
||||
BitSet output;
|
||||
mapper.copyBaseToRequested(*base, BitSet().set(0), *req, output);
|
||||
|
||||
if(mode!=PVRequestMapper::Slice)
|
||||
testFieldEqual<PVInt>(req, "A", 0);
|
||||
testFieldEqual<PVInt>(req, "C.E.F", 42);
|
||||
|
||||
req->getSubFieldT<PVScalar>("C.E.F")->putFrom<int32>(43);
|
||||
|
||||
mapper.copyBaseFromRequested(*base, output, *req, BitSet().set(0));
|
||||
|
||||
testFieldEqual<PVInt>(req, "C.E.F", 43);
|
||||
testEqual(mapper.requestedMask(), BitSet().set(0)
|
||||
.set(base->getSubFieldT("C")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
|
||||
BitSet cmp;
|
||||
mapper.maskBaseToRequested(BitSet()
|
||||
.set(base->getSubFieldT("C")->getFieldOffset()), cmp);
|
||||
testEqual(cmp, BitSet()
|
||||
.set(req->getSubFieldT("C")->getFieldOffset())
|
||||
.set(req->getSubFieldT("C.E")->getFieldOffset())
|
||||
.set(req->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
|
||||
cmp.clear();
|
||||
mapper.maskBaseFromRequested(cmp, BitSet()
|
||||
.set(req->getSubFieldT("C")->getFieldOffset()));
|
||||
testEqual(cmp, BitSet()
|
||||
.set(base->getSubFieldT("C")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
}
|
||||
}
|
||||
|
||||
struct MapperMask {
|
||||
PVStructurePtr base, req;
|
||||
BitSet bmask, rmask;
|
||||
PVRequestMapper mapper;
|
||||
|
||||
MapperMask(PVRequestMapper::mode_t mode) {
|
||||
base = getPVDataCreate()->createPVStructure(maskingType);
|
||||
mapper.compute(*base, *createRequest("field(B,C.E)"), mode);
|
||||
req = getPVDataCreate()->createPVStructure(mapper.requested());
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
base->getSubFieldT<PVScalar>("B")->putFrom<int32>(1);
|
||||
base->getSubFieldT<PVScalar>("C.E.F")->putFrom<int32>(3);
|
||||
req->getSubFieldT<PVScalar>("B")->putFrom<int32>(11);
|
||||
req->getSubFieldT<PVScalar>("C.E.F")->putFrom<int32>(13);
|
||||
}
|
||||
|
||||
void check(int32 bB, int32 bCEF, int32 rB, int32 rCEF) {
|
||||
testFieldEqual<PVInt>(base, "B", bB);
|
||||
testFieldEqual<PVInt>(base, "C.E.F", bCEF);
|
||||
testFieldEqual<PVInt>(req, "B", rB);
|
||||
testFieldEqual<PVInt>(req, "C.E.F", rCEF);
|
||||
}
|
||||
|
||||
void testEmptyMaskB2R() {
|
||||
mapper.copyBaseToRequested(*base, bmask, *req, rmask);
|
||||
check(1, 3, 11, 13);
|
||||
testEqual(bmask, BitSet());
|
||||
testEqual(rmask, BitSet());
|
||||
}
|
||||
|
||||
void testEmptyMaskR2B() {
|
||||
mapper.copyBaseFromRequested(*base, bmask, *req, rmask);
|
||||
check(1, 3, 11, 13);
|
||||
testEqual(bmask, BitSet());
|
||||
testEqual(rmask, BitSet());
|
||||
}
|
||||
|
||||
void testAllMaskB2R() {
|
||||
bmask.set(0);
|
||||
mapper.copyBaseToRequested(*base, bmask, *req, rmask);
|
||||
check(1, 3, 1, 3);
|
||||
testEqual(rmask, BitSet()
|
||||
.set(0)
|
||||
.set(req->getSubFieldT("B")->getFieldOffset())
|
||||
.set(req->getSubFieldT("C")->getFieldOffset())
|
||||
.set(req->getSubFieldT("C.E")->getFieldOffset())
|
||||
.set(req->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
}
|
||||
|
||||
void testAllMaskR2B() {
|
||||
rmask.set(0);
|
||||
mapper.copyBaseFromRequested(*base, bmask, *req, rmask);
|
||||
check(11, 13, 11, 13);
|
||||
testEqual(bmask, BitSet()
|
||||
.set(0)
|
||||
.set(base->getSubFieldT("B")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
}
|
||||
|
||||
void testMaskOneB2R() {
|
||||
bmask.set(base->getSubFieldT("B")->getFieldOffset());
|
||||
mapper.copyBaseToRequested(*base, bmask, *req, rmask);
|
||||
check(1, 3, 1, 13);
|
||||
testEqual(rmask, BitSet()
|
||||
.set(req->getSubFieldT("B")->getFieldOffset()));
|
||||
}
|
||||
|
||||
void testMaskOneR2B() {
|
||||
rmask.set(req->getSubFieldT("B")->getFieldOffset());
|
||||
mapper.copyBaseFromRequested(*base, bmask, *req, rmask);
|
||||
check(11, 3, 11, 13);
|
||||
testEqual(bmask, BitSet()
|
||||
.set(base->getSubFieldT("B")->getFieldOffset()));
|
||||
}
|
||||
|
||||
void testMaskOtherB2R() {
|
||||
bmask.set(base->getSubFieldT("C.E.F")->getFieldOffset());
|
||||
mapper.copyBaseToRequested(*base, bmask, *req, rmask);
|
||||
check(1, 3, 11, 3);
|
||||
testEqual(rmask, BitSet()
|
||||
.set(req->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
}
|
||||
|
||||
void testMaskOtherR2B() {
|
||||
rmask.set(req->getSubFieldT("C.E.F")->getFieldOffset());
|
||||
mapper.copyBaseFromRequested(*base, bmask, *req, rmask);
|
||||
check(1, 13, 11, 13);
|
||||
testEqual(bmask, BitSet()
|
||||
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
}
|
||||
|
||||
void testMaskSub1B2R() {
|
||||
bmask.set(base->getSubFieldT("C.E")->getFieldOffset());
|
||||
mapper.copyBaseToRequested(*base, bmask, *req, rmask);
|
||||
check(1, 3, 11, 3);
|
||||
testEqual(rmask, BitSet()
|
||||
.set(req->getSubFieldT("C.E")->getFieldOffset())
|
||||
.set(req->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
}
|
||||
|
||||
void testMaskSub1R2B() {
|
||||
rmask.set(req->getSubFieldT("C.E")->getFieldOffset());
|
||||
mapper.copyBaseFromRequested(*base, bmask, *req, rmask);
|
||||
check(1, 13, 11, 13);
|
||||
testEqual(bmask, BitSet()
|
||||
.set(base->getSubFieldT("C.E")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
}
|
||||
|
||||
void testMaskSub2B2R() {
|
||||
bmask.set(base->getSubFieldT("C")->getFieldOffset());
|
||||
mapper.copyBaseToRequested(*base, bmask, *req, rmask);
|
||||
check(1, 3, 11, 3);
|
||||
testEqual(rmask, BitSet()
|
||||
.set(req->getSubFieldT("C")->getFieldOffset())
|
||||
.set(req->getSubFieldT("C.E")->getFieldOffset())
|
||||
.set(req->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
}
|
||||
|
||||
void testMaskSub2R2B() {
|
||||
rmask.set(req->getSubFieldT("C")->getFieldOffset());
|
||||
mapper.copyBaseFromRequested(*base, bmask, *req, rmask);
|
||||
check(1, 13, 11, 13);
|
||||
testEqual(bmask, BitSet()
|
||||
.set(base->getSubFieldT("C")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
}
|
||||
};
|
||||
|
||||
void testMaskWarn()
|
||||
{
|
||||
PVStructurePtr base(getPVDataCreate()->createPVStructure(maskingType));
|
||||
PVRequestMapper mapper(*base, *createRequest("field(B,invalid)"), PVRequestMapper::Slice);
|
||||
|
||||
testEqual(mapper.warnings(), "No field 'invalid' ");
|
||||
}
|
||||
|
||||
void testMaskErr()
|
||||
{
|
||||
PVStructurePtr base(getPVDataCreate()->createPVStructure(maskingType));
|
||||
testThrows(std::runtime_error, PVRequestMapper mapper(*base, *createRequest("field(invalid)"), PVRequestMapper::Slice));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MAIN(testCreateRequest)
|
||||
{
|
||||
testPlan(126);
|
||||
testPlan(315);
|
||||
testCreateRequestInternal();
|
||||
testBadRequest();
|
||||
testMapper(PVRequestMapper::Slice);
|
||||
testMapper(PVRequestMapper::Mask);
|
||||
#undef TEST_METHOD
|
||||
#define TEST_METHOD(KLASS, METHOD) \
|
||||
{ \
|
||||
testDiag("------- %s::%s Mask --------", #KLASS, #METHOD); \
|
||||
{ KLASS inst(PVRequestMapper::Mask); inst.METHOD(); } \
|
||||
testDiag("------- %s::%s Slice --------", #KLASS, #METHOD); \
|
||||
{ KLASS inst(PVRequestMapper::Slice); inst.METHOD(); } \
|
||||
}
|
||||
TEST_METHOD(MapperMask, testEmptyMaskB2R);
|
||||
TEST_METHOD(MapperMask, testEmptyMaskR2B);
|
||||
TEST_METHOD(MapperMask, testAllMaskB2R);
|
||||
TEST_METHOD(MapperMask, testAllMaskR2B);
|
||||
TEST_METHOD(MapperMask, testMaskOneB2R);
|
||||
TEST_METHOD(MapperMask, testMaskOneR2B);
|
||||
TEST_METHOD(MapperMask, testMaskOtherB2R);
|
||||
TEST_METHOD(MapperMask, testMaskOtherR2B);
|
||||
TEST_METHOD(MapperMask, testMaskSub1B2R);
|
||||
TEST_METHOD(MapperMask, testMaskSub1R2B);
|
||||
TEST_METHOD(MapperMask, testMaskSub2B2R);
|
||||
TEST_METHOD(MapperMask, testMaskSub2R2B);
|
||||
testMaskWarn();
|
||||
testMaskErr();
|
||||
return testDone();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,295 +0,0 @@
|
||||
/*testPVCopyMain.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
|
||||
/* Author: Marty Kraimer */
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
|
||||
#include <epicsUnitTest.h>
|
||||
#include <testMain.h>
|
||||
|
||||
#include <pv/standardField.h>
|
||||
#include <pv/standardPVField.h>
|
||||
#include <pv/convert.h>
|
||||
#include <pv/pvCopy.h>
|
||||
#include <pv/createRequest.h>
|
||||
|
||||
|
||||
using namespace std;
|
||||
using std::tr1::static_pointer_cast;
|
||||
using namespace epics::pvData;
|
||||
|
||||
static void testPVScalar(
|
||||
string const & valueNameMaster,
|
||||
string const & valueNameCopy,
|
||||
PVStructurePtr const & pvMaster,
|
||||
PVCopyPtr const & pvCopy)
|
||||
{
|
||||
PVStructurePtr pvStructureCopy;
|
||||
PVScalarPtr pvValueMaster;
|
||||
PVScalarPtr pvValueCopy;
|
||||
BitSetPtr bitSet;
|
||||
ConvertPtr convert = getConvert();
|
||||
|
||||
pvValueMaster = pvMaster->getSubField<PVScalar>(valueNameMaster);
|
||||
convert->fromDouble(pvValueMaster,.04);
|
||||
StructureConstPtr structure = pvCopy->getStructure();
|
||||
pvStructureCopy = pvCopy->createPVStructure();
|
||||
pvValueCopy = pvStructureCopy->getSubField<PVScalar>(valueNameCopy);
|
||||
bitSet = BitSetPtr(new BitSet(pvStructureCopy->getNumberFields()));
|
||||
pvCopy->initCopy(pvStructureCopy, bitSet);
|
||||
|
||||
convert->fromDouble(pvValueMaster,.06);
|
||||
testOk1(convert->toDouble(pvValueCopy)==.04);
|
||||
pvCopy->updateCopySetBitSet(pvStructureCopy,bitSet);
|
||||
testOk1(convert->toDouble(pvValueCopy)==.06);
|
||||
testOk1(bitSet->get(pvValueCopy->getFieldOffset()));
|
||||
|
||||
pvCopy->getCopyOffset(pvValueMaster);
|
||||
|
||||
bitSet->clear();
|
||||
convert->fromDouble(pvValueMaster,1.0);
|
||||
|
||||
bitSet->set(0);
|
||||
testOk1(convert->toDouble(pvValueCopy)==0.06);
|
||||
pvCopy->updateCopyFromBitSet(pvStructureCopy,bitSet);
|
||||
testOk1(convert->toDouble(pvValueCopy)==1.0);
|
||||
|
||||
convert->fromDouble(pvValueCopy,2.0);
|
||||
bitSet->set(0);
|
||||
|
||||
testOk1(convert->toDouble(pvValueMaster)==1.0);
|
||||
pvCopy->updateMaster(pvStructureCopy,bitSet);
|
||||
testOk1(convert->toDouble(pvValueMaster)==2.0);
|
||||
|
||||
}
|
||||
|
||||
static void testPVScalarArray(
|
||||
string const & valueNameMaster,
|
||||
string const & valueNameCopy,
|
||||
PVStructurePtr const & pvMaster,
|
||||
PVCopyPtr const & pvCopy)
|
||||
{
|
||||
PVStructurePtr pvStructureCopy;
|
||||
PVScalarArrayPtr pvValueMaster;
|
||||
PVScalarArrayPtr pvValueCopy;
|
||||
BitSetPtr bitSet;
|
||||
size_t n = 5;
|
||||
shared_vector<double> values(n);
|
||||
shared_vector<const double> cvalues;
|
||||
|
||||
pvValueMaster = pvMaster->getSubField<PVScalarArray>(valueNameMaster);
|
||||
for(size_t i=0; i<n; i++) values[i] = i;
|
||||
const shared_vector<const double> xxx(freeze(values));
|
||||
pvValueMaster->putFrom(xxx);
|
||||
StructureConstPtr structure = pvCopy->getStructure();
|
||||
|
||||
pvStructureCopy = pvCopy->createPVStructure();
|
||||
pvValueCopy = pvStructureCopy->getSubField<PVScalarArray>(valueNameCopy);
|
||||
bitSet = BitSetPtr(new BitSet(pvStructureCopy->getNumberFields()));
|
||||
pvCopy->initCopy(pvStructureCopy, bitSet);
|
||||
|
||||
values.resize(n);
|
||||
for(size_t i=0; i<n; i++) values[i] = i + .06;
|
||||
const shared_vector<const double> yyy(freeze(values));
|
||||
pvValueMaster->putFrom(yyy);
|
||||
pvValueCopy->getAs(cvalues);
|
||||
testOk1(cvalues[0]==0.0);
|
||||
pvCopy->updateCopySetBitSet(pvStructureCopy,bitSet);
|
||||
pvValueCopy->getAs(cvalues);
|
||||
testOk1(cvalues[0]==0.06);
|
||||
|
||||
pvCopy->getCopyOffset(pvValueMaster);
|
||||
|
||||
bitSet->clear();
|
||||
values.resize(n);
|
||||
for(size_t i=0; i<n; i++) values[i] = i + 1.0;
|
||||
const shared_vector<const double> zzz(freeze(values));
|
||||
pvValueMaster->putFrom(zzz);
|
||||
|
||||
bitSet->set(0);
|
||||
pvValueCopy->getAs(cvalues);
|
||||
testOk1(cvalues[0]==0.06);
|
||||
pvCopy->updateCopyFromBitSet(pvStructureCopy,bitSet);
|
||||
pvValueCopy->getAs(cvalues);
|
||||
testOk1(cvalues[0]==1.0);
|
||||
|
||||
values.resize(n);
|
||||
for(size_t i=0; i<n; i++) values[i] = i + 2.0;
|
||||
const shared_vector<const double> ttt(freeze(values));
|
||||
pvValueMaster->putFrom(ttt);
|
||||
bitSet->set(0);
|
||||
|
||||
pvValueMaster->getAs(cvalues);
|
||||
testOk1(cvalues[0]==2.0);
|
||||
pvCopy->updateMaster(pvStructureCopy,bitSet);
|
||||
pvValueMaster->getAs(cvalues);
|
||||
testOk1(cvalues[0]==1.0);
|
||||
}
|
||||
|
||||
static void scalarTest()
|
||||
{
|
||||
testDiag("scalarTest()");
|
||||
PVStructurePtr pvMaster;
|
||||
string request;
|
||||
PVStructurePtr pvRequest;
|
||||
PVFieldPtr pvMasterField;
|
||||
PVCopyPtr pvCopy;
|
||||
string valueNameMaster;
|
||||
string valueNameCopy;
|
||||
|
||||
StandardPVFieldPtr standardPVField = getStandardPVField();
|
||||
pvMaster = standardPVField->scalar(pvDouble,"alarm,timeStamp,display");
|
||||
|
||||
valueNameMaster = request = "value";
|
||||
CreateRequest::shared_pointer createRequest = CreateRequest::create();
|
||||
pvRequest = createRequest->createRequest(request);
|
||||
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
|
||||
valueNameCopy = "value";
|
||||
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
|
||||
request = "";
|
||||
valueNameMaster = "value";
|
||||
pvRequest = createRequest->createRequest(request);
|
||||
|
||||
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
|
||||
valueNameCopy = "value";
|
||||
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
|
||||
request = "alarm,timeStamp,value";
|
||||
valueNameMaster = "value";
|
||||
pvRequest = createRequest->createRequest(request);
|
||||
|
||||
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
|
||||
valueNameCopy = "value";
|
||||
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
|
||||
}
|
||||
|
||||
static void arrayTest()
|
||||
{
|
||||
testDiag("arrayTest");
|
||||
|
||||
PVStructurePtr pvMaster;
|
||||
string request;
|
||||
PVStructurePtr pvRequest;
|
||||
PVFieldPtr pvMasterField;
|
||||
PVCopyPtr pvCopy;
|
||||
string valueNameMaster;
|
||||
string valueNameCopy;
|
||||
|
||||
CreateRequest::shared_pointer createRequest = CreateRequest::create();
|
||||
StandardPVFieldPtr standardPVField = getStandardPVField();
|
||||
pvMaster = standardPVField->scalarArray(pvDouble,"alarm,timeStamp");
|
||||
valueNameMaster = request = "value";
|
||||
pvRequest = createRequest->createRequest(request);
|
||||
|
||||
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
|
||||
valueNameCopy = "value";
|
||||
testPVScalarArray(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
|
||||
request = "";
|
||||
valueNameMaster = "value";
|
||||
pvRequest = createRequest->createRequest(request);
|
||||
|
||||
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
|
||||
valueNameCopy = "value";
|
||||
testPVScalarArray(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
|
||||
request = "alarm,timeStamp,value";
|
||||
valueNameMaster = "value";
|
||||
pvRequest = createRequest->createRequest(request);
|
||||
|
||||
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
|
||||
valueNameCopy = "value";
|
||||
testPVScalarArray(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
|
||||
}
|
||||
|
||||
static PVStructurePtr createPowerSupply()
|
||||
{
|
||||
FieldCreatePtr fieldCreate = getFieldCreate();
|
||||
StandardFieldPtr standardField = getStandardField();
|
||||
PVDataCreatePtr pvDataCreate = getPVDataCreate();
|
||||
|
||||
size_t nfields = 5;
|
||||
StringArray names;
|
||||
names.reserve(nfields);
|
||||
FieldConstPtrArray powerSupply;
|
||||
powerSupply.reserve(nfields);
|
||||
names.push_back("alarm");
|
||||
powerSupply.push_back(standardField->alarm());
|
||||
names.push_back("timeStamp");
|
||||
powerSupply.push_back(standardField->timeStamp());
|
||||
string properties("alarm,display");
|
||||
names.push_back("voltage");
|
||||
powerSupply.push_back(standardField->scalar(pvDouble,properties));
|
||||
names.push_back("power");
|
||||
powerSupply.push_back(standardField->scalar(pvDouble,properties));
|
||||
names.push_back("current");
|
||||
powerSupply.push_back(standardField->scalar(pvDouble,properties));
|
||||
return pvDataCreate->createPVStructure(
|
||||
fieldCreate->createStructure(names,powerSupply));
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void powerSupplyTest()
|
||||
{
|
||||
testDiag("powerSupplyTest");
|
||||
|
||||
PVStructurePtr pvMaster;
|
||||
string request;
|
||||
PVStructurePtr pvRequest;
|
||||
PVFieldPtr pvMasterField;
|
||||
PVCopyPtr pvCopy;
|
||||
string builder;
|
||||
string valueNameMaster;
|
||||
string valueNameCopy;
|
||||
|
||||
CreateRequest::shared_pointer createRequest = CreateRequest::create();
|
||||
pvMaster = createPowerSupply();
|
||||
valueNameMaster = request = "power.value";
|
||||
pvRequest = createRequest->createRequest(request);
|
||||
|
||||
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
|
||||
valueNameCopy = "power.value";
|
||||
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
|
||||
request = "";
|
||||
valueNameMaster = "power.value";
|
||||
pvRequest = createRequest->createRequest(request);
|
||||
|
||||
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
|
||||
valueNameCopy = "power.value";
|
||||
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
|
||||
request = "alarm,timeStamp,voltage.value,power.value,current.value";
|
||||
valueNameMaster = "power.value";
|
||||
pvRequest = createRequest->createRequest(request);
|
||||
|
||||
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
|
||||
valueNameCopy = "power.value";
|
||||
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
|
||||
request = "alarm,timeStamp,voltage{value,alarm},power{value,alarm,display},current.value";
|
||||
valueNameMaster = "power.value";
|
||||
pvRequest = createRequest->createRequest(request);
|
||||
|
||||
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
|
||||
valueNameCopy = "power.value";
|
||||
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
|
||||
}
|
||||
|
||||
MAIN(testPVCopy)
|
||||
{
|
||||
testPlan(67);
|
||||
scalarTest();
|
||||
arrayTest();
|
||||
powerSupplyTest();
|
||||
return testDone();
|
||||
}
|
||||
|
||||
@@ -79,3 +79,7 @@ TESTS += test_reftrack
|
||||
TESTPROD_HOST += testanyscalar
|
||||
testanyscalar_SRCS += testanyscalar.cpp
|
||||
TESTS += testanyscalar
|
||||
|
||||
TESTPROD_HOST += testprinter
|
||||
testprinter_SRCS += testprinter.cpp
|
||||
TESTS += testprinter
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
*/
|
||||
/* Author: Matej Sekoranja Date: 2010.10.18 */
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -14,15 +14,13 @@
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include <epicsUnitTest.h>
|
||||
#include <testMain.h>
|
||||
|
||||
#include <pv/pvUnitTest.h>
|
||||
#include <pv/byteBuffer.h>
|
||||
#include <pv/pvIntrospect.h>
|
||||
|
||||
using namespace epics::pvData;
|
||||
using std::string;
|
||||
using std::cout;
|
||||
|
||||
static
|
||||
void testBasicOperations() {
|
||||
@@ -175,7 +173,7 @@ void testBasicOperations() {
|
||||
testOk1(buff->getPosition()==6);
|
||||
testOk1(strncmp(&src[2],&dst[2],6)==0);
|
||||
|
||||
cout<<"# First 10 characters of destination: >>"<<string(dst, 10)<<"<<\n";
|
||||
testShow()<<"First 10 characters of destination: >>"<<std::string(dst, 10)<<"<<\n";
|
||||
}
|
||||
|
||||
static const char expect_be[] = "abcdef";
|
||||
@@ -253,14 +251,68 @@ void testUnaligned()
|
||||
testOk1(memcmp(buf.getBuffer(), "\x42\x12\x34\x56\x78\x90\xab\xcd\xef\x41\x41\x41", 8)==0);
|
||||
}
|
||||
|
||||
static
|
||||
void testArrayLE()
|
||||
{
|
||||
testDiag("testArray() LE");
|
||||
|
||||
ByteBuffer buf(8, EPICS_ENDIAN_LITTLE);
|
||||
|
||||
std::vector<uint32> vals;
|
||||
vals.push_back(0x12345678);
|
||||
vals.push_back(0x01020304);
|
||||
|
||||
buf.putArray(&vals[0], vals.size());
|
||||
testEqual(buf.getPosition(), 8u);
|
||||
|
||||
testOk1(memcmp(buf.getBuffer(), "\x78\x56\x34\x12\x04\x03\x02\x01", 8)==0);
|
||||
|
||||
buf.clear();
|
||||
buf.put("\x40\x30\x20\x10\xa4\xa3\xa2\xa1", 0, 8);
|
||||
buf.flip();
|
||||
|
||||
buf.getArray(&vals[0], 2);
|
||||
|
||||
testEqual(vals[0], 0x10203040u);
|
||||
testEqual(vals[1], 0xa1a2a3a4u);
|
||||
}
|
||||
|
||||
static
|
||||
void testArrayBE()
|
||||
{
|
||||
testDiag("testArray() BE");
|
||||
|
||||
ByteBuffer buf(8, EPICS_ENDIAN_BIG);
|
||||
|
||||
std::vector<uint32> vals;
|
||||
vals.push_back(0x12345678u);
|
||||
vals.push_back(0x01020304u);
|
||||
|
||||
buf.putArray(&vals[0], vals.size());
|
||||
testEqual(buf.getPosition(), 8u);
|
||||
|
||||
testOk1(memcmp(buf.getBuffer(), "\x12\x34\x56\x78\x01\x02\x03\x04", 8)==0);
|
||||
|
||||
buf.clear();
|
||||
buf.put("\x10\x20\x30\x40\xa1\xa2\xa3\xa4", 0, 8);
|
||||
buf.flip();
|
||||
|
||||
buf.getArray(&vals[0], 2);
|
||||
|
||||
testEqual(vals[0], 0x10203040u);
|
||||
testEqual(vals[1], 0xa1a2a3a4u);
|
||||
}
|
||||
|
||||
MAIN(testByteBuffer)
|
||||
{
|
||||
testPlan(96);
|
||||
testPlan(104);
|
||||
testDiag("Tests byteBuffer");
|
||||
testBasicOperations();
|
||||
testInverseEndianness(EPICS_ENDIAN_BIG, expect_be);
|
||||
testInverseEndianness(EPICS_ENDIAN_LITTLE, expect_le);
|
||||
testSwap();
|
||||
testUnaligned();
|
||||
testArrayLE();
|
||||
testArrayBE();
|
||||
return testDone();
|
||||
}
|
||||
|
||||
@@ -9,10 +9,6 @@
|
||||
* Author: Miha Vitorovic
|
||||
*/
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
@@ -28,6 +24,8 @@
|
||||
#include <pv/noDefaultMethods.h>
|
||||
#include <pv/byteBuffer.h>
|
||||
#include <pv/convert.h>
|
||||
#include <pv/pvUnitTest.h>
|
||||
#include <pv/current_function.h>
|
||||
|
||||
#include <pv/standardField.h>
|
||||
|
||||
@@ -633,6 +631,8 @@ void testStructureId() {
|
||||
|
||||
void serializationFieldTest(FieldConstPtr const & field)
|
||||
{
|
||||
testShow()<<CURRENT_FUNCTION<<"\n"<<field;
|
||||
|
||||
buffer->clear();
|
||||
|
||||
// serialize
|
||||
@@ -643,7 +643,7 @@ void serializationFieldTest(FieldConstPtr const & field)
|
||||
|
||||
FieldConstPtr deserializedField = getFieldCreate()->deserialize(buffer, control);
|
||||
|
||||
// must equal
|
||||
testShow()<<" after "<<(void*)field.get()<<" == "<<(void*)deserializedField.get();
|
||||
testOk1(*field == *deserializedField);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,15 +17,14 @@
|
||||
|
||||
#include "pv/sharedVector.h"
|
||||
|
||||
using std::string;
|
||||
using namespace epics::pvData;
|
||||
|
||||
namespace {
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
|
||||
void testEmpty()
|
||||
{
|
||||
testDiag("Test empty vector");
|
||||
epics::pvData::shared_vector<int32> empty, empty2, empty3(0u);
|
||||
pvd::shared_vector<pvd::int32> empty, empty2, empty3(0u);
|
||||
|
||||
testOk1(empty.size()==0);
|
||||
testOk1(empty.empty());
|
||||
@@ -47,7 +46,7 @@ void testInternalAlloc()
|
||||
{
|
||||
testDiag("Test vector alloc w/ new[]");
|
||||
|
||||
epics::pvData::shared_vector<int32> internal(5);
|
||||
pvd::shared_vector<pvd::int32> internal(5);
|
||||
|
||||
testOk1(internal.size()==5);
|
||||
testOk1(!internal.empty());
|
||||
@@ -62,7 +61,7 @@ void testInternalAlloc()
|
||||
internal[2] = 42;
|
||||
testOk1(internal[2]==42);
|
||||
|
||||
epics::pvData::shared_vector<int32> internal2(15, 500);
|
||||
pvd::shared_vector<pvd::int32> internal2(15, 500);
|
||||
|
||||
testOk1(internal2.size()==15);
|
||||
testOk1(internal2[1]==500);
|
||||
@@ -83,8 +82,8 @@ void testInternalAlloc()
|
||||
//Note: STL shared_ptr requires that deletors be copy constructable
|
||||
template<typename E>
|
||||
struct callCounter {
|
||||
std::tr1::shared_ptr<int32> count;
|
||||
callCounter():count(new int32){*count=0;}
|
||||
std::tr1::shared_ptr<pvd::int32> count;
|
||||
callCounter():count(new pvd::int32){*count=0;}
|
||||
callCounter(const callCounter& o):count(o.count) {}
|
||||
callCounter& operator=(const callCounter& o){count=o.count;}
|
||||
void operator()(E){(*count)++;}
|
||||
@@ -95,8 +94,8 @@ void testExternalAlloc()
|
||||
testDiag("Test vector external alloc");
|
||||
|
||||
// Simulate a failed malloc() or similar
|
||||
int32 *oops=0;
|
||||
epics::pvData::shared_vector<int32> nullPtr(oops, 42, 100);
|
||||
pvd::int32 *oops=0;
|
||||
pvd::shared_vector<pvd::int32> nullPtr(oops, 42, 100);
|
||||
|
||||
testOk1(nullPtr.size()==0);
|
||||
testOk1(nullPtr.empty());
|
||||
@@ -105,8 +104,8 @@ void testExternalAlloc()
|
||||
|
||||
testOk1(nullPtr.data()==NULL);
|
||||
|
||||
int32 *raw=new int32[5];
|
||||
epics::pvData::shared_vector<int32> newData(raw, 1, 4);
|
||||
pvd::int32 *raw=new pvd::int32[5];
|
||||
pvd::shared_vector<pvd::int32> newData(raw, 1, 4);
|
||||
|
||||
testOk1(newData.size()==4);
|
||||
testOk1(!newData.empty());
|
||||
@@ -116,11 +115,11 @@ void testExternalAlloc()
|
||||
testOk1(newData[0]==14);
|
||||
|
||||
// Check use of custom deleter
|
||||
int32 localVar[4] = {1,2,3,4};
|
||||
callCounter<int32*> tracker;
|
||||
pvd::int32 localVar[4] = {1,2,3,4};
|
||||
callCounter<pvd::int32*> tracker;
|
||||
testOk1(*tracker.count==0);
|
||||
|
||||
epics::pvData::shared_vector<int32> locvar(localVar,
|
||||
pvd::shared_vector<pvd::int32> locvar(localVar,
|
||||
tracker,
|
||||
0, 4);
|
||||
|
||||
@@ -140,8 +139,8 @@ void testShare()
|
||||
{
|
||||
testDiag("Test vector Sharing");
|
||||
|
||||
epics::pvData::shared_vector<int32> one, two(15);
|
||||
epics::pvData::shared_vector<int32> three(two);
|
||||
pvd::shared_vector<pvd::int32> one, two(15);
|
||||
pvd::shared_vector<pvd::int32> three(two);
|
||||
|
||||
testOk1(one.unique());
|
||||
testOk1(!two.unique());
|
||||
@@ -202,22 +201,22 @@ void testConst()
|
||||
{
|
||||
testDiag("Test constant vector");
|
||||
|
||||
epics::pvData::shared_vector<int32> writable(15, 100);
|
||||
pvd::shared_vector<pvd::int32> writable(15, 100);
|
||||
|
||||
epics::pvData::shared_vector<int32>::reference wr = writable[0];
|
||||
epics::pvData::shared_vector<int32>::const_reference ror = writable[0];
|
||||
pvd::shared_vector<pvd::int32>::reference wr = writable[0];
|
||||
pvd::shared_vector<pvd::int32>::const_reference ror = writable[0];
|
||||
|
||||
testOk1(wr==ror);
|
||||
|
||||
int32 *compare = writable.data();
|
||||
pvd::int32 *compare = writable.data();
|
||||
|
||||
testOk1(writable.unique());
|
||||
|
||||
// can re-target container, but data is R/O
|
||||
epics::pvData::shared_vector<const int32> rodata(freeze(writable));
|
||||
pvd::shared_vector<const pvd::int32> rodata(freeze(writable));
|
||||
|
||||
epics::pvData::shared_vector<const int32>::reference wcr = rodata[0];
|
||||
epics::pvData::shared_vector<const int32>::const_reference rocr = rodata[0];
|
||||
pvd::shared_vector<const pvd::int32>::reference wcr = rodata[0];
|
||||
pvd::shared_vector<const pvd::int32>::const_reference rocr = rodata[0];
|
||||
|
||||
testOk1(wcr==rocr);
|
||||
|
||||
@@ -230,7 +229,7 @@ void testConst()
|
||||
|
||||
testOk1(rodata.data()==compare);
|
||||
|
||||
epics::pvData::shared_vector<const int32> rodata2(rodata);
|
||||
pvd::shared_vector<const pvd::int32> rodata2(rodata);
|
||||
|
||||
testOk1(rodata.data()==rodata2.data());
|
||||
|
||||
@@ -243,9 +242,9 @@ void testSlice()
|
||||
{
|
||||
testDiag("Test vector slicing");
|
||||
|
||||
epics::pvData::shared_vector<int32> original(10, 100);
|
||||
pvd::shared_vector<pvd::int32> original(10, 100);
|
||||
|
||||
epics::pvData::shared_vector<int32> half1(original), half2(original), half2a(original);
|
||||
pvd::shared_vector<pvd::int32> half1(original), half2(original), half2a(original);
|
||||
|
||||
half1.slice(0, 5);
|
||||
half2.slice(5, 5);
|
||||
@@ -293,9 +292,9 @@ void testCapacity()
|
||||
{
|
||||
testDiag("Test vector capacity");
|
||||
|
||||
epics::pvData::shared_vector<int32> vect(10, 100);
|
||||
pvd::shared_vector<pvd::int32> vect(10, 100);
|
||||
|
||||
int32 *peek = vect.dataPtr().get();
|
||||
pvd::int32 *peek = vect.dataPtr().get();
|
||||
|
||||
vect.slice(0, 5);
|
||||
|
||||
@@ -333,7 +332,7 @@ void testCapacity()
|
||||
|
||||
void testPush()
|
||||
{
|
||||
epics::pvData::shared_vector<int32> vect;
|
||||
pvd::shared_vector<pvd::int32> vect;
|
||||
|
||||
testDiag("Test push_back optimizations");
|
||||
|
||||
@@ -360,16 +359,16 @@ void testVoid()
|
||||
{
|
||||
testDiag("Test vector cast to/from void");
|
||||
|
||||
epics::pvData::shared_vector<int32> IV(4);
|
||||
pvd::shared_vector<pvd::int32> IV(4);
|
||||
|
||||
epics::pvData::shared_vector<void> VV(epics::pvData::static_shared_vector_cast<void>(IV));
|
||||
pvd::shared_vector<void> VV(pvd::static_shared_vector_cast<void>(IV));
|
||||
|
||||
testOk1(IV.dataPtr().get()==VV.dataPtr().get());
|
||||
testOk1(IV.size()*sizeof(int)==VV.size());
|
||||
testOk1(IV.size()*sizeof(pvd::int32)==VV.size());
|
||||
|
||||
VV.slice(sizeof(int), 2*sizeof(int));
|
||||
VV.slice(sizeof(pvd::int32), 2*sizeof(pvd::int32));
|
||||
|
||||
IV = epics::pvData::static_shared_vector_cast<int32>(VV);
|
||||
IV = pvd::static_shared_vector_cast<pvd::int32>(VV);
|
||||
|
||||
testOk1(IV.dataOffset()==1);
|
||||
testOk1(IV.size()==2);
|
||||
@@ -380,23 +379,23 @@ void testConstVoid()
|
||||
{
|
||||
testDiag("Test vector cast to/from const void");
|
||||
|
||||
epics::pvData::shared_vector<const int32> CIV(4);
|
||||
pvd::shared_vector<const pvd::int32> CIV(4);
|
||||
|
||||
epics::pvData::shared_vector<const void> CVV(epics::pvData::static_shared_vector_cast<const void>(CIV));
|
||||
pvd::shared_vector<const void> CVV(pvd::static_shared_vector_cast<const void>(CIV));
|
||||
// case const void to const void
|
||||
epics::pvData::shared_vector<const void> CVV2(epics::pvData::static_shared_vector_cast<const void>(CVV));
|
||||
pvd::shared_vector<const void> CVV2(pvd::static_shared_vector_cast<const void>(CVV));
|
||||
|
||||
testOk1(CIV.dataPtr().get()==CVV2.dataPtr().get());
|
||||
testOk1(CIV.size()*sizeof(int)==CVV2.size());
|
||||
|
||||
CVV2.slice(sizeof(int), 2*sizeof(int));
|
||||
|
||||
CIV = epics::pvData::static_shared_vector_cast<const int32>(CVV2);
|
||||
CIV = pvd::static_shared_vector_cast<const pvd::int32>(CVV2);
|
||||
|
||||
testOk1(CIV.dataOffset()==1);
|
||||
testOk1(CIV.size()==2);
|
||||
|
||||
epics::pvData::shared_vector<void> VV;
|
||||
pvd::shared_vector<void> VV;
|
||||
// not possible to thaw() void as shared_vector<void> has no make_unique()
|
||||
//VV = thaw(CVV);
|
||||
CVV = freeze(VV);
|
||||
@@ -408,8 +407,8 @@ void testNonPOD()
|
||||
{
|
||||
testDiag("Test vector of non-POD types");
|
||||
|
||||
epics::pvData::shared_vector<string> strings(6);
|
||||
epics::pvData::shared_vector<std::tr1::shared_ptr<dummyStruct> > structs(5);
|
||||
pvd::shared_vector<std::string> strings(6);
|
||||
pvd::shared_vector<std::tr1::shared_ptr<dummyStruct> > structs(5);
|
||||
|
||||
testOk1(strings[0].empty());
|
||||
testOk1(structs[0].get()==NULL);
|
||||
@@ -417,7 +416,7 @@ void testNonPOD()
|
||||
structs[1].reset(new dummyStruct);
|
||||
dummyStruct *temp = structs[1].get();
|
||||
|
||||
epics::pvData::shared_vector<std::tr1::shared_ptr<dummyStruct> > structs2(structs);
|
||||
pvd::shared_vector<std::tr1::shared_ptr<dummyStruct> > structs2(structs);
|
||||
|
||||
testOk1(!structs.unique());
|
||||
testOk1(structs[1].unique());
|
||||
@@ -436,22 +435,22 @@ void testVectorConvert()
|
||||
{
|
||||
testDiag("Test shared_vector_convert");
|
||||
|
||||
epics::pvData::shared_vector<int32> ints(6, 42), moreints;
|
||||
epics::pvData::shared_vector<float> floats;
|
||||
epics::pvData::shared_vector<string> strings;
|
||||
epics::pvData::shared_vector<void> voids;
|
||||
pvd::shared_vector<pvd::int32> ints(6, 42), moreints;
|
||||
pvd::shared_vector<float> floats;
|
||||
pvd::shared_vector<std::string> strings;
|
||||
pvd::shared_vector<void> voids;
|
||||
|
||||
testOk1(ints.unique());
|
||||
|
||||
// no-op convert. Just returns another reference
|
||||
moreints = epics::pvData::shared_vector_convert<int32>(ints);
|
||||
moreints = pvd::shared_vector_convert<pvd::int32>(ints);
|
||||
|
||||
testOk1(!ints.unique());
|
||||
moreints.clear();
|
||||
|
||||
// conversion when both types are known.
|
||||
// returns a new vector
|
||||
floats = epics::pvData::shared_vector_convert<float>(ints);
|
||||
floats = pvd::shared_vector_convert<float>(ints);
|
||||
|
||||
testOk1(ints.unique());
|
||||
testOk1(floats.size()==ints.size());
|
||||
@@ -459,16 +458,16 @@ void testVectorConvert()
|
||||
|
||||
// convert to void is static_shared_vector_cast<void>()
|
||||
// returns a reference
|
||||
voids = epics::pvData::shared_vector_convert<void>(ints);
|
||||
voids = pvd::shared_vector_convert<void>(ints);
|
||||
|
||||
testOk1(!ints.unique());
|
||||
testOk1(voids.size()==ints.size()*sizeof(int32));
|
||||
testOk1(voids.size()==ints.size()*sizeof(pvd::int32));
|
||||
|
||||
// convert from void uses shared_vector<void>::original_type()
|
||||
// to find that the actual type is 'int32'.
|
||||
// returns a new vector
|
||||
testOk1(voids.original_type()==epics::pvData::pvInt);
|
||||
strings = epics::pvData::shared_vector_convert<string>(voids);
|
||||
testOk1(voids.original_type()==pvd::pvInt);
|
||||
strings = pvd::shared_vector_convert<std::string>(voids);
|
||||
|
||||
voids.clear();
|
||||
|
||||
@@ -481,11 +480,11 @@ void testWeak()
|
||||
{
|
||||
testDiag("Test weak_ptr counting");
|
||||
|
||||
epics::pvData::shared_vector<int32> data(6);
|
||||
pvd::shared_vector<pvd::int32> data(6);
|
||||
|
||||
testOk1(data.unique());
|
||||
|
||||
std::tr1::shared_ptr<int32> pdata(data.dataPtr());
|
||||
std::tr1::shared_ptr<pvd::int32> pdata(data.dataPtr());
|
||||
|
||||
testOk1(!data.unique());
|
||||
|
||||
@@ -493,7 +492,7 @@ void testWeak()
|
||||
|
||||
testOk1(data.unique());
|
||||
|
||||
std::tr1::weak_ptr<int32> wdata(data.dataPtr());
|
||||
std::tr1::weak_ptr<pvd::int32> wdata(data.dataPtr());
|
||||
|
||||
testOk1(data.unique()); // True, but I wish it wasn't!!!
|
||||
|
||||
@@ -506,16 +505,16 @@ void testICE()
|
||||
{
|
||||
testDiag("Test freeze and thaw");
|
||||
|
||||
epics::pvData::shared_vector<int32> A(6, 42), C;
|
||||
epics::pvData::shared_vector<const int32> B, D;
|
||||
pvd::shared_vector<pvd::int32> A(6, 42), C;
|
||||
pvd::shared_vector<const pvd::int32> B, D;
|
||||
|
||||
int32 *check = A.data();
|
||||
pvd::int32 *check = A.data();
|
||||
|
||||
// check freeze w/ unique reference
|
||||
|
||||
// clears A and moves reference to B
|
||||
// no copy
|
||||
B = epics::pvData::freeze(A);
|
||||
B = pvd::freeze(A);
|
||||
|
||||
testOk1(A.unique());
|
||||
testOk1(B.unique());
|
||||
@@ -528,7 +527,7 @@ void testICE()
|
||||
|
||||
// clears D, but reference to B
|
||||
// remains, so a copy is made
|
||||
C = epics::pvData::thaw(D);
|
||||
C = pvd::thaw(D);
|
||||
|
||||
testOk1(B.unique());
|
||||
testOk1(C.unique());
|
||||
@@ -542,7 +541,7 @@ void testICE()
|
||||
|
||||
// clears B and moves reference to A
|
||||
// no copy
|
||||
A = epics::pvData::thaw(B);
|
||||
A = pvd::thaw(B);
|
||||
|
||||
testOk1(A.unique());
|
||||
testOk1(B.unique());
|
||||
@@ -559,7 +558,7 @@ void testICE()
|
||||
// would clear A, but remaining reference C
|
||||
// fails operation. A not cleared
|
||||
// and exception thrown
|
||||
B = epics::pvData::freeze(A);
|
||||
B = pvd::freeze(A);
|
||||
testFail("Froze non-unique vector!");
|
||||
} catch(std::runtime_error& e) {
|
||||
testPass("freeze of non-unique throws runtime_error as expected");
|
||||
@@ -568,12 +567,12 @@ void testICE()
|
||||
|
||||
void testBad()
|
||||
{
|
||||
epics::pvData::shared_vector<int> I;
|
||||
epics::pvData::shared_vector<const int> CI;
|
||||
epics::pvData::shared_vector<float> F;
|
||||
epics::pvData::shared_vector<const float> CF;
|
||||
epics::pvData::shared_vector<void> V;
|
||||
epics::pvData::shared_vector<const void> CV;
|
||||
pvd::shared_vector<int> I;
|
||||
pvd::shared_vector<const int> CI;
|
||||
pvd::shared_vector<float> F;
|
||||
pvd::shared_vector<const float> CF;
|
||||
pvd::shared_vector<void> V;
|
||||
pvd::shared_vector<const void> CV;
|
||||
(void)I;
|
||||
(void)CI;
|
||||
(void)F;
|
||||
@@ -588,38 +587,38 @@ void testBad()
|
||||
// No copy from const to non-const
|
||||
//CI = I;
|
||||
//I = CI;
|
||||
//epics::pvData::shared_vector<const int> CI2(I);
|
||||
//epics::pvData::shared_vector<int> I2(CI);
|
||||
//pvd::shared_vector<const int> CI2(I);
|
||||
//pvd::shared_vector<int> I2(CI);
|
||||
|
||||
// shared_vector_convert can't thaw()
|
||||
//I = epics::pvData::shared_vector_convert<int>(CI);
|
||||
//V = epics::pvData::shared_vector_convert<void>(CV);
|
||||
//I = pvd::shared_vector_convert<int>(CI);
|
||||
//V = pvd::shared_vector_convert<void>(CV);
|
||||
|
||||
// shared_vector_convert can't freeze()
|
||||
//CI = epics::pvData::shared_vector_convert<const int>(I);
|
||||
//CV = epics::pvData::shared_vector_convert<const void>(V);
|
||||
//CI = pvd::shared_vector_convert<const int>(I);
|
||||
//CV = pvd::shared_vector_convert<const void>(V);
|
||||
|
||||
// static_shared_vector_cast can't thaw()
|
||||
//I = epics::pvData::static_shared_vector_cast<int>(CI);
|
||||
//V = epics::pvData::static_shared_vector_cast<void>(CV);
|
||||
//I = pvd::static_shared_vector_cast<int>(CI);
|
||||
//V = pvd::static_shared_vector_cast<void>(CV);
|
||||
|
||||
// static_shared_vector_cast can't freeze()
|
||||
//CI = epics::pvData::static_shared_vector_cast<const int>(I);
|
||||
//CV = epics::pvData::static_shared_vector_cast<const void>(V);
|
||||
//CI = pvd::static_shared_vector_cast<const int>(I);
|
||||
//CV = pvd::static_shared_vector_cast<const void>(V);
|
||||
|
||||
// freeze() can't change type.
|
||||
// the error here will be with the assignment
|
||||
//I = epics::pvData::freeze(CV);
|
||||
//I = epics::pvData::freeze(CF);
|
||||
//CI = epics::pvData::freeze(V);
|
||||
//CI = epics::pvData::freeze(F);
|
||||
//I = pvd::freeze(CV);
|
||||
//I = pvd::freeze(CF);
|
||||
//CI = pvd::freeze(V);
|
||||
//CI = pvd::freeze(F);
|
||||
|
||||
// that() can't change type.
|
||||
// the error here will be with the assignment
|
||||
//CI = epics::pvData::thaw(V);
|
||||
//CI = epics::pvData::thaw(F);
|
||||
//I = epics::pvData::thaw(CV);
|
||||
//I = epics::pvData::that(CF);
|
||||
//CI = pvd::thaw(V);
|
||||
//CI = pvd::thaw(F);
|
||||
//I = pvd::thaw(CV);
|
||||
//I = pvd::that(CF);
|
||||
}
|
||||
|
||||
void testAutoSwap()
|
||||
@@ -636,8 +635,8 @@ void testCXX11Move()
|
||||
{
|
||||
#if __cplusplus>=201103L
|
||||
testDiag("Check std::move()");
|
||||
shared_vector<int32> A(4, 42),
|
||||
B(std::move(A));
|
||||
pvd::shared_vector<pvd::int32> A(4, 42),
|
||||
B(std::move(A));
|
||||
|
||||
testOk1(A.unique());
|
||||
testOk1(B.unique());
|
||||
@@ -653,8 +652,8 @@ void testCXX11Move()
|
||||
testOk1(A.size()==4);
|
||||
testOk1(!A.empty() && A[0]==42);
|
||||
|
||||
shared_vector<void> C(shared_vector_convert<void>(A)),
|
||||
D(std::move(C));
|
||||
pvd::shared_vector<void> C(pvd::shared_vector_convert<void>(A)),
|
||||
D(std::move(C));
|
||||
A.clear();
|
||||
|
||||
testOk1(C.unique());
|
||||
@@ -678,11 +677,11 @@ void testCXX11Init()
|
||||
#if __cplusplus>=201103L
|
||||
testDiag("Check c++11 style array initialization");
|
||||
|
||||
shared_vector<const int32> A = {1.0, 2.0, 3.0};
|
||||
pvd::shared_vector<const pvd::int32> A = {1.0, 2.0, 3.0};
|
||||
|
||||
testOk1(A.size()==3);
|
||||
|
||||
int32 sum = 0;
|
||||
pvd::int32 sum = 0;
|
||||
for (auto V: A) {
|
||||
sum += V;
|
||||
}
|
||||
@@ -699,8 +698,8 @@ MAIN(testSharedVector)
|
||||
testPlan(191);
|
||||
testDiag("Tests for shared_vector");
|
||||
|
||||
testDiag("sizeof(shared_vector<int32>)=%lu",
|
||||
(unsigned long)sizeof(epics::pvData::shared_vector<int32>));
|
||||
testDiag("sizeof(shared_vector<pvd::int32>)=%lu",
|
||||
(unsigned long)sizeof(pvd::shared_vector<pvd::int32>));
|
||||
|
||||
testEmpty();
|
||||
testInternalAlloc();
|
||||
|
||||
@@ -4,10 +4,6 @@
|
||||
*/
|
||||
/* Author: Michael Davidsaver */
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
|
||||
#include <pv/pvdVersion.h>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
|
||||
#include <pv/json.h>
|
||||
#include <pv/bitSet.h>
|
||||
#include <pv/valueBuilder.h>
|
||||
@@ -290,15 +288,3 @@ MAIN(testjson)
|
||||
}
|
||||
return testDone();
|
||||
}
|
||||
|
||||
#else // EPICS_VERSION_INT
|
||||
|
||||
#include <epicsUnitTest.h>
|
||||
|
||||
MAIN(testjson)
|
||||
{
|
||||
testPlan(1);
|
||||
testSkip(1, "JSON parser requires Base >=3.15.0.1");
|
||||
return testDone();
|
||||
}
|
||||
#endif //EPICS_VERSION_INT
|
||||
|
||||
364
testApp/misc/testprinter.cpp
Normal file
364
testApp/misc/testprinter.cpp
Normal file
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <testMain.h>
|
||||
#include <epicsString.h>
|
||||
|
||||
#include <pv/pvUnitTest.h>
|
||||
#include <pv/current_function.h>
|
||||
|
||||
#include <pv/bitSet.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/standardField.h>
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
|
||||
typedef std::vector<std::string> lines_t;
|
||||
|
||||
namespace {
|
||||
|
||||
struct SB {
|
||||
std::ostringstream strm;
|
||||
operator std::string() { return strm.str(); }
|
||||
template<typename T>
|
||||
SB& operator<<(const T& v) {
|
||||
strm<<v;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
lines_t lines(const std::string& str)
|
||||
{
|
||||
lines_t ret;
|
||||
size_t p = 0;
|
||||
while(true) {
|
||||
size_t next = str.find_first_of('\n', p);
|
||||
ret.push_back(str.substr(p, next-p)); // exclude trailing '\n'
|
||||
if(next==str.npos)
|
||||
break;
|
||||
else
|
||||
p = next+1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string print(const pvd::PVStructure::Formatter& fmt)
|
||||
{
|
||||
std::ostringstream strm;
|
||||
strm<<fmt;
|
||||
return strm.str();
|
||||
}
|
||||
|
||||
struct point {
|
||||
size_t L, R;
|
||||
};
|
||||
|
||||
// really primative diff
|
||||
// expect -> actual
|
||||
::detail::testPassx
|
||||
testDiff(const std::string& expect, const std::string& actual, const std::string& msg = std::string())
|
||||
{
|
||||
bool match = expect==actual;
|
||||
::detail::testPassx ret(match);
|
||||
ret<<msg<<'\n';
|
||||
|
||||
lines_t lhs(lines(expect)), rhs(lines(actual));
|
||||
|
||||
size_t L=0, R=0;
|
||||
|
||||
while(L<lhs.size() && R<rhs.size()) {
|
||||
if(lhs[L]==rhs[R]) {
|
||||
ret<<" "<<pvd::escape(lhs[L])<<'\n';
|
||||
L++;
|
||||
R++;
|
||||
|
||||
} else {
|
||||
// ugly... diagonalization hardcoded...
|
||||
static const point search[] = {
|
||||
{1,0},
|
||||
{0,1},
|
||||
{1,1},
|
||||
{2,1},
|
||||
{1,2},
|
||||
{2,2},
|
||||
};
|
||||
|
||||
|
||||
size_t Lp, Rp;
|
||||
for(size_t n=0, N=sizeof(search)/sizeof(search[0]); n<N; n++) {
|
||||
Lp = L+search[n].L;
|
||||
Rp = R+search[n].R;
|
||||
|
||||
if(Lp<lhs.size() && Rp<rhs.size() && lhs[Lp]==rhs[Rp])
|
||||
break;
|
||||
}
|
||||
if(Lp>=lhs.size() || Rp>=rhs.size()) {
|
||||
// reached end without match
|
||||
Lp = lhs.size();
|
||||
Rp = rhs.size();
|
||||
}
|
||||
|
||||
for(size_t l=L; l<Lp; l++)
|
||||
ret<<"- "<<pvd::escape(lhs[l])<<'\n';
|
||||
for(size_t r=R; r<Rp; r++)
|
||||
ret<<"+ "<<pvd::escape(rhs[r])<<'\n';
|
||||
assert(Lp>L); // must make progress
|
||||
assert(Rp>R);
|
||||
L = Lp;
|
||||
R = Rp;
|
||||
// loop around and print matching line
|
||||
}
|
||||
}
|
||||
|
||||
for(; L<lhs.size(); L++)
|
||||
ret<<"- "<<pvd::escape(lhs[L])<<'\n';
|
||||
for(; R<rhs.size(); R++)
|
||||
ret<<"+ "<<pvd::escape(rhs[R])<<'\n';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const pvd::StructureConstPtr scalarNumeric(pvd::getFieldCreate()->createFieldBuilder()
|
||||
->setId("epics:nt/NTScalar:1.0")
|
||||
->add("value", pvd::pvInt)
|
||||
->add("alarm", pvd::getStandardField()->alarm())
|
||||
->add("timeStamp", pvd::getStandardField()->timeStamp())
|
||||
->createStructure());
|
||||
|
||||
void showNTScalarNumeric()
|
||||
{
|
||||
testDiag("%s", CURRENT_FUNCTION);
|
||||
pvd::PVStructurePtr input(pvd::getPVDataCreate()->createPVStructure(scalarNumeric));
|
||||
input->getSubFieldT<pvd::PVScalar>("value")->putFrom(-42);
|
||||
|
||||
testDiff("<undefined> -42 \n", print(input->stream()));
|
||||
|
||||
input->getSubFieldT<pvd::PVScalar>("alarm.severity")->putFrom(1);
|
||||
input->getSubFieldT<pvd::PVScalar>("alarm.status")->putFrom(1);
|
||||
input->getSubFieldT<pvd::PVString>("alarm.message")->put("FOO");
|
||||
|
||||
testDiff("<undefined> -42 MINOR DEVICE FOO \n", print(input->stream()));
|
||||
}
|
||||
|
||||
static const pvd::StructureConstPtr scalarString(pvd::getFieldCreate()->createFieldBuilder()
|
||||
->setId("epics:nt/NTScalar:1.0")
|
||||
->add("value", pvd::pvString)
|
||||
->add("alarm", pvd::getStandardField()->alarm())
|
||||
->add("timeStamp", pvd::getStandardField()->timeStamp())
|
||||
->createStructure());
|
||||
|
||||
void showNTScalarString()
|
||||
{
|
||||
testDiag("%s", CURRENT_FUNCTION);
|
||||
pvd::PVStructurePtr input(pvd::getPVDataCreate()->createPVStructure(scalarString));
|
||||
testDiff("<undefined> \n", print(input->stream()));
|
||||
|
||||
input->getSubFieldT<pvd::PVString>("value")->put("bar");
|
||||
|
||||
testDiff("<undefined> bar \n", print(input->stream()));
|
||||
|
||||
input->getSubFieldT<pvd::PVScalar>("alarm.severity")->putFrom(1);
|
||||
input->getSubFieldT<pvd::PVScalar>("alarm.status")->putFrom(1);
|
||||
input->getSubFieldT<pvd::PVString>("alarm.message")->put("FOO");
|
||||
|
||||
testDiff("<undefined> bar MINOR DEVICE FOO \n", print(input->stream()));
|
||||
}
|
||||
|
||||
static const pvd::StructureConstPtr ntenum(pvd::getFieldCreate()->createFieldBuilder()
|
||||
->setId("epics:nt/NTEnum:1.0")
|
||||
->addNestedStructure("value")
|
||||
->setId("enum_t")
|
||||
->add("index", pvd::pvInt)
|
||||
->addArray("choices", pvd::pvString)
|
||||
->endNested()
|
||||
->add("alarm", pvd::getStandardField()->alarm())
|
||||
->add("timeStamp", pvd::getStandardField()->timeStamp())
|
||||
->createStructure());
|
||||
|
||||
void showNTEnum()
|
||||
{
|
||||
testDiag("%s", CURRENT_FUNCTION);
|
||||
pvd::PVStructurePtr input(pvd::getPVDataCreate()->createPVStructure(ntenum));
|
||||
testDiff("<undefined> (0) <undefined>\n", print(input->stream()), "empty");
|
||||
|
||||
pvd::PVStringArray::svector sarr;
|
||||
sarr.push_back("one");
|
||||
sarr.push_back("a two");
|
||||
input->getSubFieldT<pvd::PVStringArray>("value.choices")->replace(pvd::freeze(sarr));
|
||||
|
||||
input->getSubFieldT<pvd::PVInt>("value.index")->put(0);
|
||||
|
||||
testDiff("<undefined> (0) one\n", print(input->stream()), "one");
|
||||
|
||||
input->getSubFieldT<pvd::PVInt>("value.index")->put(1);
|
||||
|
||||
testDiff("<undefined> (1) a two\n", print(input->stream()), "two");
|
||||
|
||||
testDiff("epics:nt/NTEnum:1.0 \n"
|
||||
" enum_t value (1) a two\n"
|
||||
" int index 1\n"
|
||||
" string[] choices [\"one\", \"a two\"]\n"
|
||||
" alarm_t alarm \n"
|
||||
" int severity 0\n"
|
||||
" int status 0\n"
|
||||
" string message \n"
|
||||
" time_t timeStamp <undefined> \n"
|
||||
" long secondsPastEpoch 0\n"
|
||||
" int nanoseconds 0\n"
|
||||
" int userTag 0\n",
|
||||
print(input->stream().format(pvd::PVStructure::Formatter::Raw)), "two raw");
|
||||
}
|
||||
|
||||
static const pvd::StructureConstPtr table(pvd::getFieldCreate()->createFieldBuilder()
|
||||
->setId("epics:nt/NTTable:1.0")
|
||||
->addArray("labels", pvd::pvString)
|
||||
->addNestedStructure("value")
|
||||
->addArray("colA", pvd::pvInt)
|
||||
->addArray("colB", pvd::pvString)
|
||||
->endNested()
|
||||
->add("alarm", pvd::getStandardField()->alarm())
|
||||
->add("timeStamp", pvd::getStandardField()->timeStamp())
|
||||
->createStructure());
|
||||
void showNTTable()
|
||||
{
|
||||
testDiag("%s", CURRENT_FUNCTION);
|
||||
pvd::PVStructurePtr input(pvd::getPVDataCreate()->createPVStructure(table));
|
||||
|
||||
testDiff("<undefined> \n"
|
||||
"colA colB\n"
|
||||
, print(input->stream()),
|
||||
"empty table");
|
||||
|
||||
|
||||
pvd::PVStringArray::svector sarr;
|
||||
sarr.push_back("labelA");
|
||||
sarr.push_back("label B");
|
||||
input->getSubFieldT<pvd::PVStringArray>("labels")->replace(pvd::freeze(sarr));
|
||||
|
||||
pvd::PVIntArray::svector iarr;
|
||||
iarr.push_back(1);
|
||||
iarr.push_back(2);
|
||||
iarr.push_back(3);
|
||||
iarr.push_back(42); // will not be shown
|
||||
input->getSubFieldT<pvd::PVIntArray>("value.colA")->replace(pvd::freeze(iarr));
|
||||
|
||||
sarr.push_back("one\x7f");
|
||||
sarr.push_back("two words");
|
||||
sarr.push_back("A '\"'");
|
||||
input->getSubFieldT<pvd::PVStringArray>("value.colB")->replace(pvd::freeze(sarr));
|
||||
|
||||
|
||||
testDiff("<undefined> \n"
|
||||
"labelA \"label B\"\n"
|
||||
" 1 one\\x7F\n"
|
||||
" 2 \"two words\"\n"
|
||||
" 3 \"A \\'\"\"\\'\"\n"
|
||||
, print(input->stream()),
|
||||
"with data");
|
||||
}
|
||||
|
||||
static const pvd::StructureConstPtr everything(pvd::getFieldCreate()->createFieldBuilder()
|
||||
->setId("omg")
|
||||
->add("scalar", pvd::pvString)
|
||||
->addArray("scalarArray", pvd::pvString)
|
||||
->addNestedStructure("below")
|
||||
->add("A", pvd::pvInt)
|
||||
->addNestedUnion("select")
|
||||
->add("one", pvd::pvInt)
|
||||
->add("two", pvd::pvInt)
|
||||
->endNested()
|
||||
->addNestedUnionArray("arrselect")
|
||||
->add("foo", pvd::pvInt)
|
||||
->add("bar", pvd::pvInt)
|
||||
->endNested()
|
||||
->addNestedStructureArray("astruct")
|
||||
->add("red", pvd::pvInt)
|
||||
->add("blue", pvd::pvInt)
|
||||
->endNested()
|
||||
->endNested()
|
||||
->add("anything", pvd::getFieldCreate()->createVariantUnion())
|
||||
->add("arrayany", pvd::getFieldCreate()->createVariantUnionArray())
|
||||
->createStructure());
|
||||
|
||||
void testRaw()
|
||||
{
|
||||
testDiag("%s", CURRENT_FUNCTION);
|
||||
pvd::PVStructurePtr input(pvd::getPVDataCreate()->createPVStructure(everything));
|
||||
|
||||
{
|
||||
pvd::PVStringArray::svector temp;
|
||||
temp.push_back("hello");
|
||||
temp.push_back("world\x7f");
|
||||
input->getSubFieldT<pvd::PVStringArray>("scalarArray")->replace(pvd::freeze(temp));
|
||||
}
|
||||
|
||||
testDiff("omg \n"
|
||||
" string scalar \n" // bit 1
|
||||
" string[] scalarArray [\"hello\", \"world\\x7F\"]\n"
|
||||
" structure below\n"
|
||||
" int A 0\n" // bit 4
|
||||
" union select\n"
|
||||
" (none)\n"
|
||||
" union[] arrselect\n"
|
||||
" structure[] astruct\n"
|
||||
" any anything\n"
|
||||
" (none)\n"
|
||||
" any[] arrayany\n"
|
||||
, print(input->stream()));
|
||||
|
||||
testDiff("omg \n"
|
||||
" string scalar \n"
|
||||
" structure below\n"
|
||||
" int A 0\n"
|
||||
, print(input->stream().show(pvd::BitSet().set(1).set(4))));
|
||||
|
||||
testDiff("omg \n"
|
||||
"\033[1m string scalar \n"
|
||||
"\033[0m\033[1m string[] scalarArray [\"hello\", \"world\\x7F\"]\n"
|
||||
"\033[0m structure below\n"
|
||||
"\033[1m int A 0\n"
|
||||
"\033[0m union select\n"
|
||||
" (none)\n"
|
||||
" union[] arrselect\n"
|
||||
" structure[] astruct\n"
|
||||
" any anything\n"
|
||||
" (none)\n"
|
||||
" any[] arrayany\n"
|
||||
, print(input->stream()
|
||||
.mode(pvd::PVStructure::Formatter::ANSI) // force use of escapes
|
||||
.highlight(pvd::BitSet().set(1).set(2).set(4))
|
||||
));
|
||||
}
|
||||
|
||||
void testEscape()
|
||||
{
|
||||
testDiag("%s", CURRENT_FUNCTION);
|
||||
|
||||
testEqual("hello world", std::string(SB()<<pvd::escape("hello world")));
|
||||
testEqual("hello\\nworld", std::string(SB()<<pvd::escape("hello\nworld")));
|
||||
testEqual("hello\\\"world", std::string(SB()<<pvd::escape("hello\"world")));
|
||||
testEqual("hello\\x7Fworld", std::string(SB()<<pvd::escape("hello\x7Fworld")));
|
||||
|
||||
testEqual("hello\"\"world", std::string(SB()<<pvd::escape("hello\"world").style(pvd::escape::CSV)));
|
||||
|
||||
testEqual("hello\"\"world", pvd::escape("hello\"world").style(pvd::escape::CSV).str());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MAIN(testprinter)
|
||||
{
|
||||
testPlan(20);
|
||||
showNTScalarNumeric();
|
||||
showNTScalarString();
|
||||
showNTEnum();
|
||||
showNTTable();
|
||||
testRaw();
|
||||
testEscape();
|
||||
return testDone();
|
||||
}
|
||||
@@ -65,3 +65,7 @@ TESTS += testFieldBuilder
|
||||
TESTPROD_HOST += testValueBuilder
|
||||
testValueBuilder_SRCS += testValueBuilder.cpp
|
||||
TESTS += testValueBuilder
|
||||
|
||||
TESTPROD_Linux += performstruct
|
||||
performstruct_SRCS += performstruct.cpp
|
||||
performstruct_SYS_LIBS_Linux += rt
|
||||
|
||||
115
testApp/pv/performstruct.cpp
Normal file
115
testApp/pv/performstruct.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
// Attempt to qualtify the effects of de-duplication on the time need to allocate a PVStructure
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <testMain.h>
|
||||
#include <epicsUnitTest.h>
|
||||
|
||||
#include <pv/current_function.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/standardField.h>
|
||||
|
||||
namespace {
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
|
||||
struct TimeIt {
|
||||
struct timespec m_start;
|
||||
double sum, sum2;
|
||||
size_t count;
|
||||
TimeIt() { reset(); }
|
||||
void reset() {
|
||||
sum = sum2 = 0.0;
|
||||
count = 0;
|
||||
}
|
||||
void start() {
|
||||
clock_gettime(CLOCK_MONOTONIC, &m_start);
|
||||
}
|
||||
void end() {
|
||||
struct timespec end;
|
||||
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||
double diff = (end.tv_sec-m_start.tv_sec) + (end.tv_nsec-m_start.tv_nsec)*1e-9;
|
||||
sum += diff;
|
||||
sum2 += diff*diff;
|
||||
count++;
|
||||
}
|
||||
void report(const char *unit ="s", double mult=1.0) const {
|
||||
double mean = sum/count;
|
||||
double mean2 = sum2/count;
|
||||
double std = sqrt(mean2 - mean*mean);
|
||||
printf("# %zu sample %f +- %f %s\n", count, mean/mult, std/mult, unit);
|
||||
}
|
||||
};
|
||||
|
||||
void buildMiss()
|
||||
{
|
||||
testDiag("%s", CURRENT_FUNCTION);
|
||||
TimeIt record;
|
||||
|
||||
pvd::FieldCreatePtr create(pvd::getFieldCreate());
|
||||
pvd::StandardFieldPtr standard(pvd::getStandardField());
|
||||
|
||||
for(size_t i=0; i<1000; i++) {
|
||||
// unique name each time to (partially) defeat caching
|
||||
char buf[10];
|
||||
sprintf(buf, "field%zu", i);
|
||||
|
||||
record.start();
|
||||
|
||||
pvd::FieldConstPtr fld(create->createFieldBuilder()
|
||||
->setId(buf)
|
||||
->add("value", pvd::pvInt)
|
||||
->addNestedStructure(buf)
|
||||
->add("value", pvd::pvString)
|
||||
->endNested()
|
||||
->add("display", standard->display())
|
||||
->createStructure());
|
||||
record.end();
|
||||
}
|
||||
|
||||
record.report("us", 1e-6);
|
||||
}
|
||||
|
||||
void buildHit()
|
||||
{
|
||||
testDiag("%s", CURRENT_FUNCTION);
|
||||
TimeIt record;
|
||||
|
||||
pvd::FieldCreatePtr create(pvd::getFieldCreate());
|
||||
pvd::StandardFieldPtr standard(pvd::getStandardField());
|
||||
|
||||
pvd::FieldConstPtr fld(create->createFieldBuilder()
|
||||
->add("value", pvd::pvInt)
|
||||
->addNestedStructure("foo")
|
||||
->add("field", pvd::pvString)
|
||||
->endNested()
|
||||
->add("display", standard->display())
|
||||
->createStructure());
|
||||
|
||||
for(size_t i=0; i<1000; i++) {
|
||||
|
||||
record.start();
|
||||
|
||||
pvd::FieldConstPtr fld(create->createFieldBuilder()
|
||||
->add("value", pvd::pvInt)
|
||||
->addNestedStructure("foo")
|
||||
->add("field", pvd::pvString)
|
||||
->endNested()
|
||||
->add("display", standard->display())
|
||||
->createStructure());
|
||||
record.end();
|
||||
}
|
||||
|
||||
record.report("us", 1e-6);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MAIN(performStruct) {
|
||||
testPlan(0);
|
||||
buildMiss();
|
||||
buildHit();
|
||||
return testDone();
|
||||
}
|
||||
@@ -1,294 +0,0 @@
|
||||
/*
|
||||
* Network configuration -- QEMU NOT using DHCP
|
||||
*
|
||||
************************************************************
|
||||
* EDIT THIS FILE TO REFLECT YOUR NETWORK CONFIGURATION *
|
||||
* BEFORE RUNNING ANY RTEMS PROGRAMS WHICH USE THE NETWORK! *
|
||||
************************************************************
|
||||
*
|
||||
* The dynamic probing is based upon the EPICS network
|
||||
* configuration file written by:
|
||||
* W. Eric Norum
|
||||
* eric.norum@usask.ca
|
||||
* (306) 966-5394
|
||||
*/
|
||||
|
||||
#ifndef _RTEMS_NETWORKCONFIG_H_
|
||||
#define _RTEMS_NETWORKCONFIG_H_
|
||||
|
||||
/* #define USE_LIBBSDPORT */
|
||||
|
||||
#if defined(USE_LIBBSDPORT)
|
||||
#include <bsp/libbsdport_api.h>
|
||||
#define CONFIGURE_MAXIMUM_TIMERS 10
|
||||
#endif
|
||||
/*
|
||||
* For TFTP test application
|
||||
*/
|
||||
#if (defined (RTEMS_USE_BOOTP))
|
||||
#define RTEMS_TFTP_TEST_HOST_NAME "BOOTP_HOST"
|
||||
#define RTEMS_TFTP_TEST_FILE_NAME "BOOTP_FILE"
|
||||
#else
|
||||
#define RTEMS_TFTP_TEST_HOST_NAME "XXX.YYY.ZZZ.XYZ"
|
||||
#define RTEMS_TFTP_TEST_FILE_NAME "tftptest"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* For NFS test application
|
||||
*
|
||||
* NFS mount and a directory to ls once mounted
|
||||
*/
|
||||
#define RTEMS_NFS_SERVER "192.168.1.210"
|
||||
#define RTEMS_NFS_SERVER_PATH "/home"
|
||||
#define RTEMS_NFS_LS_PATH "/mnt/nfstest"
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* This file can be copied to an application source directory
|
||||
* and modified to override the values shown below.
|
||||
*
|
||||
* The following CPP symbols may be passed from the Makefile:
|
||||
*
|
||||
* symbol default description
|
||||
*
|
||||
* NETWORK_TASK_PRIORITY 150 can be read by app from public
|
||||
* var 'gesysNetworkTaskPriority'
|
||||
* FIXED_IP_ADDR <undefined> hardcoded IP address (e.g.,
|
||||
* "192.168.0.10"); disables BOOTP;
|
||||
* must also define FIXED_NETMASK
|
||||
* FIXED_NETMASK <undefined> IP netmask string
|
||||
* (e.g. "255.255.255.0")
|
||||
* MULTI_NETDRIVER <undefined> ugly hack; if defined try to probe
|
||||
* a variety of PCI and ISA drivers
|
||||
* (i386 ONLY) use is discouraged!
|
||||
* NIC_NAME <undefined> Ethernet driver name (e.g. "pcn1");
|
||||
* must also define NIC_ATTACH
|
||||
* NIC_ATTACH <undefined> Ethernet driver attach function
|
||||
* (e.g., rtems_fxp_attach).
|
||||
* If these are undefined then
|
||||
* a) MULTI_NETDRIVER is used
|
||||
* (if defined)
|
||||
* b) RTEMS_BSP_NETWORK_DRIVER_NAME/
|
||||
* RTEMS_BSP_NETWORK_DRIVER_ATTACH
|
||||
* are tried
|
||||
* MEMORY_CUSTOM <undefined> Allocate the defined amount of
|
||||
* memory for mbufs and mbuf clusters,
|
||||
* respectively. Define to a comma ','
|
||||
* separated pair of two numerical
|
||||
* values, e.g: 100*1024,200*1024
|
||||
* MEMORY_SCARCE <undefined> Allocate few memory for mbufs
|
||||
* (hint for how much memory the
|
||||
* board has)
|
||||
* MEMORY_HUGE <undefined> Allocate a lot of memory for mbufs
|
||||
* (hint for how much memory the
|
||||
* board has)
|
||||
* If none of MEMORY_CUSTOM/
|
||||
* MEMORY_SCARCE/MEMORY_HUGE are
|
||||
* defined then a medium amount of
|
||||
* memory is allocated for mbufs.
|
||||
*/
|
||||
|
||||
#include <rtems/bspIo.h>
|
||||
#include <bsp.h>
|
||||
#include <rtems/rtems_bsdnet.h>
|
||||
|
||||
#if 0
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#else
|
||||
#include "verscheck.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//#define MULTI_NETDRIVER
|
||||
//#define RTEMS_BSP_NETWORK_DRIVER_NAME 1
|
||||
|
||||
#define FIXED_IP_ADDR "192.168.1.249"
|
||||
#define FIXED_NETMASK "255.255.255.0"
|
||||
|
||||
#ifndef NETWORK_TASK_PRIORITY
|
||||
#define NETWORK_TASK_PRIORITY 150 /* within EPICS' range */
|
||||
#endif
|
||||
|
||||
/* make publicly available for startup scripts... */
|
||||
const int gesysNetworkTaskPriority = NETWORK_TASK_PRIORITY;
|
||||
|
||||
#ifdef FIXED_IP_ADDR
|
||||
#define RTEMS_DO_BOOTP 0
|
||||
#else
|
||||
#define RTEMS_DO_BOOTP rtems_bsdnet_do_bootp
|
||||
#define FIXED_IP_ADDR 0
|
||||
#undef FIXED_NETMASK
|
||||
#define FIXED_NETMASK 0
|
||||
#endif
|
||||
|
||||
#if !defined(NIC_NAME)
|
||||
|
||||
#ifdef MULTI_NETDRIVER
|
||||
|
||||
#if 0
|
||||
#if RTEMS_VERSION_ATLEAST(4,6,99)
|
||||
#define pcib_init pci_initialize
|
||||
#endif
|
||||
#endif
|
||||
|
||||
extern int rtems_3c509_driver_attach (struct rtems_bsdnet_ifconfig *, int);
|
||||
extern int rtems_fxp_attach (struct rtems_bsdnet_ifconfig *, int);
|
||||
extern int rtems_elnk_driver_attach (struct rtems_bsdnet_ifconfig *, int);
|
||||
extern int rtems_dec21140_driver_attach (struct rtems_bsdnet_ifconfig *, int);
|
||||
|
||||
/* these don't probe and will be used even if there's no device :-( */
|
||||
extern int rtems_ne_driver_attach (struct rtems_bsdnet_ifconfig *, int);
|
||||
extern int rtems_wd_driver_attach (struct rtems_bsdnet_ifconfig *, int);
|
||||
|
||||
static struct rtems_bsdnet_ifconfig isa_netdriver_config[] = {
|
||||
{
|
||||
"ep0", rtems_3c509_driver_attach, isa_netdriver_config + 1,
|
||||
},
|
||||
{
|
||||
"ne1", rtems_ne_driver_attach, 0, irno: 9 /* qemu cannot configure irq-no :-(; has it hardwired to 9 */
|
||||
},
|
||||
};
|
||||
|
||||
static struct rtems_bsdnet_ifconfig pci_netdriver_config[]={
|
||||
{
|
||||
"dc1", rtems_dec21140_driver_attach, pci_netdriver_config+1,
|
||||
},
|
||||
#if !defined(USE_LIBBSDPORT)
|
||||
{
|
||||
"fxp1", rtems_fxp_attach, pci_netdriver_config+2,
|
||||
},
|
||||
#else
|
||||
{
|
||||
"", libbsdport_netdriver_attach, pci_netdriver_config+2,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
"elnk1", rtems_elnk_driver_attach, isa_netdriver_config,
|
||||
},
|
||||
};
|
||||
|
||||
static int pci_check(struct rtems_bsdnet_ifconfig *ocfg, int attaching)
|
||||
{
|
||||
struct rtems_bsdnet_ifconfig *cfg;
|
||||
int if_index_pre;
|
||||
extern int if_index;
|
||||
if ( attaching ) {
|
||||
cfg = pci_initialize() ?
|
||||
isa_netdriver_config : pci_netdriver_config;
|
||||
}
|
||||
while ( cfg ) {
|
||||
printk("Probing '%s'", cfg->name);
|
||||
/* unfortunately, the return value is unreliable - some drivers report
|
||||
* success even if they fail.
|
||||
* Check if they chained an interface (ifnet) structure instead
|
||||
*/
|
||||
if_index_pre = if_index;
|
||||
cfg->attach(cfg, attaching);
|
||||
if ( if_index > if_index_pre ) {
|
||||
/* assume success */
|
||||
printk(" .. seemed to work\n");
|
||||
ocfg->name = cfg->name;
|
||||
ocfg->attach = cfg->attach;
|
||||
return 0;
|
||||
}
|
||||
printk(" .. failed\n");
|
||||
cfg = cfg->next;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
#define NIC_NAME "dummy"
|
||||
#define NIC_ATTACH pci_check
|
||||
|
||||
#else
|
||||
|
||||
#if defined(RTEMS_BSP_NETWORK_DRIVER_NAME) /* Use NIC provided by BSP */
|
||||
|
||||
/* force ne2k_isa on i386 for qemu */
|
||||
#if defined(__i386__)
|
||||
# define NIC_NAME BSP_NE2000_NETWORK_DRIVER_NAME
|
||||
# define NIC_ATTACH BSP_NE2000_NETWORK_DRIVER_ATTACH
|
||||
|
||||
#else
|
||||
|
||||
# define NIC_NAME RTEMS_BSP_NETWORK_DRIVER_NAME
|
||||
# define NIC_ATTACH RTEMS_BSP_NETWORK_DRIVER_ATTACH
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* ifdef MULTI_NETDRIVER */
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef NIC_NAME
|
||||
|
||||
extern int NIC_ATTACH();
|
||||
|
||||
#if RTEMS_BSP_NETWORK_DRIVER_ATTACH == BSP_NE2000_NETWORK_DRIVER_ATTACH
|
||||
static char ethernet_address[6] = { 0x00, 0xab, 0xcd, 0xef, 0x12, 0x34 };
|
||||
#endif
|
||||
|
||||
static struct rtems_bsdnet_ifconfig netdriver_config[1] = {{
|
||||
NIC_NAME, /* name */
|
||||
(int (*)(struct rtems_bsdnet_ifconfig*,int))NIC_ATTACH, /* attach function */
|
||||
0, /* link to next interface */
|
||||
FIXED_IP_ADDR,
|
||||
FIXED_NETMASK
|
||||
#if RTEMS_BSP_NETWORK_DRIVER_ATTACH == BSP_NE2000_NETWORK_DRIVER_ATTACH
|
||||
,
|
||||
ethernet_address,
|
||||
irno:9,
|
||||
port:0xc100
|
||||
#endif
|
||||
}};
|
||||
#else
|
||||
#warning "NO KNOWN NETWORK DRIVER FOR THIS BSP -- YOU MAY HAVE TO EDIT networkconfig.h"
|
||||
#endif
|
||||
|
||||
struct rtems_bsdnet_config rtems_bsdnet_config = {
|
||||
#ifdef NIC_NAME
|
||||
netdriver_config, /* link to next interface */
|
||||
RTEMS_DO_BOOTP, /* Use BOOTP to get network configuration */
|
||||
#else
|
||||
0,
|
||||
0,
|
||||
#endif
|
||||
NETWORK_TASK_PRIORITY, /* Network task priority */
|
||||
#if defined(MEMORY_CUSTOM)
|
||||
MEMORY_CUSTOM,
|
||||
#elif defined(MEMORY_SCARCE)
|
||||
100*1024, /* MBUF space */
|
||||
200*1024, /* MBUF cluster space */
|
||||
#elif defined(MEMORY_HUGE)
|
||||
2*1024*1024, /* MBUF space */
|
||||
5*1024*1024, /* MBUF cluster space */
|
||||
#else
|
||||
180*1024, /* MBUF space */
|
||||
350*1024, /* MBUF cluster space */
|
||||
#endif
|
||||
#if (!defined (RTEMS_USE_BOOTP)) && defined(ON_RTEMS_LAB_WINSYSTEMS)
|
||||
"rtems", /* Host name */
|
||||
"nodomain.com", /* Domain name */
|
||||
"192.168.1.14", /* Gateway */
|
||||
"192.168.1.1", /* Log host */
|
||||
{"89.212.75.6" }, /* Name server(s) */
|
||||
{"192.168.1.1" }, /* NTP server(s) */
|
||||
#else
|
||||
NULL, /* Host name */
|
||||
NULL, /* Domain name */
|
||||
NULL, /* Gateway */
|
||||
NULL, /* Log host */
|
||||
{ NULL }, /* Name server(s) */
|
||||
{ NULL }, /* NTP server(s) */
|
||||
#endif /* !RTEMS_USE_BOOTP */
|
||||
0, /* efficiency */
|
||||
0, /* udp TX buffer */
|
||||
0, /* udp RX buffer */
|
||||
0, /* tcp TX buffer */
|
||||
0, /* tcp RX buffer */
|
||||
};
|
||||
#endif /* _RTEMS_NETWORKCONFIG_H_ */
|
||||
@@ -324,8 +324,8 @@ void test_extendStructure()
|
||||
<<"amended: "<<amended
|
||||
<<"expected: "<<expected;
|
||||
|
||||
testNotEqual(static_cast<const void*>(amended.get()),
|
||||
static_cast<const void*>(expected.get()));
|
||||
testEqual(static_cast<const void*>(amended.get()),
|
||||
static_cast<const void*>(expected.get()));
|
||||
testEqual(*amended, *expected);
|
||||
|
||||
testThrows(std::runtime_error,
|
||||
|
||||
@@ -146,6 +146,54 @@ static void testStructure()
|
||||
testOk1(struct1->getFieldName(0)==names1[0]);
|
||||
testOk1(struct1->getFieldName(1)==names1[1]);
|
||||
|
||||
testOk1(struct1->getField("nonexistent").get()==NULL);
|
||||
try {
|
||||
FieldConstPtr field(struct1->getField(9999));
|
||||
testFail("struct1->getField(9999): missing expected exception");
|
||||
} catch (std::out_of_range& e) {
|
||||
testPass("struct1->getField(9999): caught expected exception: %s", e.what());
|
||||
}
|
||||
|
||||
testOk1(struct1->getFieldT("innerA")==fields1[0]);
|
||||
testOk1(struct1->getFieldT("innerB")==fields1[1]);
|
||||
testOk1(struct1->getFieldT(0)==fields1[0]);
|
||||
testOk1(struct1->getFieldT(1)==fields1[1]);
|
||||
|
||||
try {
|
||||
FieldConstPtr field(struct1->getFieldT("nonexistent"));
|
||||
testFail("struct1->getFieldT('nonexistent'): missing required exception");
|
||||
} catch (std::runtime_error& e) {
|
||||
testPass("struct1->getFieldT('nonexistent'): caught expected exception: %s", e.what());
|
||||
}
|
||||
|
||||
try {
|
||||
FieldConstPtr field(struct1->getFieldT(9999));
|
||||
testFail("struct1->getFieldT(9999): missing required exception");
|
||||
} catch (std::out_of_range& e) {
|
||||
testPass("struct1->getFieldT(9999): caught expected exception: %s", e.what());
|
||||
}
|
||||
|
||||
testOk1(struct1->getField<Scalar>("innerA").get()!=NULL);
|
||||
testOk1(struct1->getField<Structure>("innerA").get()==NULL);
|
||||
testOk1(struct1->getField<ScalarArray>(1).get()!=NULL);
|
||||
testOk1(struct1->getField<Structure>(1).get()==NULL);
|
||||
|
||||
testOk1(struct1->getFieldT<Scalar>("innerA").get()!=NULL);
|
||||
try {
|
||||
StructureConstPtr s(struct1->getFieldT<Structure>("innerA"));
|
||||
testFail("struct1->getFieldT<Structure>('innnerA'): missing required exception");
|
||||
} catch (std::runtime_error& e) {
|
||||
testPass("struct1->getFieldT<Structure>('innnerA'): caught expected exception: %s", e.what());
|
||||
}
|
||||
|
||||
testOk1(struct1->getFieldT<ScalarArray>(1).get()!=NULL);
|
||||
try {
|
||||
StructureConstPtr s(struct1->getFieldT<Structure>(1));
|
||||
testFail("struct1->getFieldT<Structure>(1): missing required exception");
|
||||
} catch (std::runtime_error& e) {
|
||||
testPass("struct1->getFieldT<Structure>(1): caught expected exception: %s", e.what());
|
||||
}
|
||||
|
||||
testOk1(struct1->getID() == Structure::DEFAULT_ID);
|
||||
|
||||
testOk1(fields1 == struct1->getFields()); // vector equality
|
||||
@@ -190,6 +238,55 @@ static void testUnion()
|
||||
testOk1(union1->getFieldName(0)==names1[0]);
|
||||
testOk1(union1->getFieldName(1)==names1[1]);
|
||||
|
||||
testOk1(union1->getField("nonexistent").get()==NULL);
|
||||
|
||||
try {
|
||||
FieldConstPtr field(union1->getField(9999).get());
|
||||
testFail("union1->getField(9999): missing expected exception");
|
||||
} catch (std::out_of_range& e) {
|
||||
testPass("union1->getField(9999): caught expected exception: %s", e.what());
|
||||
}
|
||||
|
||||
testOk1(union1->getFieldT("innerA")==fields1[0]);
|
||||
testOk1(union1->getFieldT("innerB")==fields1[1]);
|
||||
testOk1(union1->getFieldT(0)==fields1[0]);
|
||||
testOk1(union1->getFieldT(1)==fields1[1]);
|
||||
|
||||
try {
|
||||
FieldConstPtr field(union1->getFieldT("nonexistent"));
|
||||
testFail("union1->getFieldT('nonexistent'): missing required exception");
|
||||
} catch (std::runtime_error& e) {
|
||||
testPass("union1->getFieldT('nonexistent'): caught expected exception: %s", e.what());
|
||||
}
|
||||
|
||||
try {
|
||||
FieldConstPtr field(union1->getFieldT(9999));
|
||||
testFail("union1->getFieldT(9999): missing required exception");
|
||||
} catch (std::out_of_range& e) {
|
||||
testPass("union1->getFieldT(9999): caught expected exception: %s", e.what());
|
||||
}
|
||||
|
||||
testOk1(union1->getField<Scalar>("innerA").get()!=NULL);
|
||||
testOk1(union1->getField<Structure>("innerA").get()==NULL);
|
||||
testOk1(union1->getField<ScalarArray>(1).get()!=NULL);
|
||||
testOk1(union1->getField<Structure>(1).get()==NULL);
|
||||
|
||||
testOk1(union1->getFieldT<Scalar>("innerA").get()!=NULL);
|
||||
try {
|
||||
StructureConstPtr s(union1->getFieldT<Structure>("innerA"));
|
||||
testFail("union1->getFieldT<Structure>('innnerA'): missing required exception");
|
||||
} catch (std::runtime_error& e) {
|
||||
testPass("union1->getFieldT<Structure>('innnerA'): caught expected exception: %s", e.what());
|
||||
}
|
||||
|
||||
testOk1(union1->getFieldT<ScalarArray>(1).get()!=NULL);
|
||||
try {
|
||||
StructureConstPtr s(union1->getFieldT<Structure>(1));
|
||||
testFail("union1->getFieldT<Structure>(1): missing required exception");
|
||||
} catch (std::runtime_error& e) {
|
||||
testPass("union1->getFieldT<Structure>(1): caught expected exception: %s", e.what());
|
||||
}
|
||||
|
||||
testOk1(union1->getID() == Union::DEFAULT_ID);
|
||||
|
||||
testOk1(fields1 == union1->getFields()); // vector equality
|
||||
@@ -311,7 +408,7 @@ static void testMapping()
|
||||
|
||||
MAIN(testIntrospect)
|
||||
{
|
||||
testPlan(326);
|
||||
testPlan(358);
|
||||
fieldCreate = getFieldCreate();
|
||||
pvDataCreate = getPVDataCreate();
|
||||
standardField = getStandardField();
|
||||
|
||||
@@ -38,6 +38,30 @@ static string alarmTimeStamp("alarm,timeStamp");
|
||||
static string alarmTimeStampValueAlarm("alarm,timeStamp,valueAlarm");
|
||||
static string allProperties("alarm,timeStamp,display,control,valueAlarm");
|
||||
|
||||
static void testSizes()
|
||||
{
|
||||
#define SHOW(T) testDiag("sizeof(" #T ")==%zu", sizeof(T))
|
||||
SHOW(Field);
|
||||
SHOW(Structure);
|
||||
SHOW(StructureArray);
|
||||
SHOW(Union);
|
||||
SHOW(UnionArray);
|
||||
SHOW(Scalar);
|
||||
SHOW(ScalarArray);
|
||||
SHOW(PVField);
|
||||
SHOW(PVStructure);
|
||||
SHOW(PVStructureArray);
|
||||
SHOW(PVUnion);
|
||||
SHOW(PVUnionArray);
|
||||
SHOW(PVScalar);
|
||||
SHOW(PVScalarArray);
|
||||
SHOW(PVInt);
|
||||
SHOW(PVIntArray);
|
||||
SHOW(PVString);
|
||||
SHOW(PVStringArray);
|
||||
#undef SHOW
|
||||
}
|
||||
|
||||
static void testCreatePVStructure()
|
||||
{
|
||||
PVStructurePtr pv0 = standardPVField->scalar(
|
||||
@@ -384,7 +408,7 @@ static void testRequest()
|
||||
StructureConstPtr topStructure = fieldCreate->createStructure(
|
||||
topNames,topFields);
|
||||
cout << *topStructure << endl;
|
||||
PVStructurePtr pvTop = pvDataCreate->createPVStructure(topStructure);
|
||||
PVStructurePtr pvTop = topStructure->build();
|
||||
cout << *pvTop << endl;
|
||||
cout << *pvTop->getStructure() << endl;
|
||||
PVStructurePtr xxx = pvTop->getSubField<PVStructure>("record");
|
||||
@@ -522,7 +546,7 @@ static void testFieldAccess()
|
||||
endNested()->
|
||||
createStructure();
|
||||
|
||||
PVStructurePtr fld = pvDataCreate->createPVStructure(tdef);
|
||||
PVStructurePtr fld = tdef->build();
|
||||
|
||||
PVIntPtr a = fld->getSubField<PVInt>("test");
|
||||
testOk1(a.get() != NULL);
|
||||
@@ -628,11 +652,11 @@ static void testFieldAccess()
|
||||
|
||||
static void testAnyScalar()
|
||||
{
|
||||
PVStructurePtr value(getPVDataCreate()->createPVStructure(getFieldCreate()->createFieldBuilder()
|
||||
->add("a", pvInt)
|
||||
->add("b", pvDouble)
|
||||
->add("c", pvString)
|
||||
->createStructure()));
|
||||
PVStructurePtr value(FieldBuilder::begin()
|
||||
->add("a", pvInt)
|
||||
->add("b", pvDouble)
|
||||
->add("c", pvString)
|
||||
->createStructure()->build());
|
||||
|
||||
PVIntPtr a(value->getSubFieldT<PVInt>("a"));
|
||||
PVDoublePtr b(value->getSubFieldT<PVDouble>("b"));
|
||||
@@ -747,6 +771,7 @@ MAIN(testPVData)
|
||||
standardField = getStandardField();
|
||||
standardPVField = getStandardPVField();
|
||||
convert = getConvert();
|
||||
testSizes();
|
||||
testCreatePVStructure();
|
||||
testCreatePVStructureWithInvalidName();
|
||||
testPVScalar();
|
||||
|
||||
@@ -38,7 +38,7 @@ static void testBasic()
|
||||
StructureArrayConstPtr alarmtype(
|
||||
fieldCreate->createStructureArray(standardField->alarm()));
|
||||
|
||||
PVStructureArrayPtr alarmarr(pvDataCreate->createPVStructureArray(alarmtype));
|
||||
PVStructureArrayPtr alarmarr(alarmtype->build());
|
||||
|
||||
testOk1(alarmarr->getLength()==0);
|
||||
|
||||
@@ -69,7 +69,7 @@ static void testCompress()
|
||||
StructureArrayConstPtr alarmtype(
|
||||
fieldCreate->createStructureArray(standardField->alarm()));
|
||||
|
||||
PVStructureArrayPtr alarmarr(pvDataCreate->createPVStructureArray(alarmtype));
|
||||
PVStructureArrayPtr alarmarr(alarmtype->build());
|
||||
|
||||
alarmarr->setLength(5);
|
||||
|
||||
@@ -85,10 +85,10 @@ static void testCompress()
|
||||
|
||||
PVStructureArray::svector contents(10);
|
||||
|
||||
contents[2] = pvDataCreate->createPVStructure(standardField->alarm());
|
||||
contents[4] = pvDataCreate->createPVStructure(standardField->alarm());
|
||||
contents[5] = pvDataCreate->createPVStructure(standardField->alarm());
|
||||
contents[8] = pvDataCreate->createPVStructure(standardField->alarm());
|
||||
contents[2] = standardField->alarm()->build();
|
||||
contents[4] = standardField->alarm()->build();
|
||||
contents[5] = standardField->alarm()->build();
|
||||
contents[8] = standardField->alarm()->build();
|
||||
|
||||
PVStructureArray::const_svector scont(freeze(contents));
|
||||
|
||||
@@ -117,11 +117,11 @@ static void testRemove()
|
||||
PVStructureArray::svector contents(10);
|
||||
|
||||
for(size_t i=0; i<contents.size(); i++)
|
||||
contents[i] = pvDataCreate->createPVStructure(standardField->alarm());
|
||||
contents[i] = standardField->alarm()->build();
|
||||
|
||||
StructureArrayConstPtr alarmtype(
|
||||
fieldCreate->createStructureArray(standardField->alarm()));
|
||||
PVStructureArrayPtr alarmarr(pvDataCreate->createPVStructureArray(alarmtype));
|
||||
PVStructureArrayPtr alarmarr(alarmtype->build());
|
||||
|
||||
PVStructureArray::const_svector scont(freeze(contents));
|
||||
|
||||
@@ -147,10 +147,10 @@ static void testFromRaw()
|
||||
testDiag("Test structure array external allocation for shared_vector");
|
||||
|
||||
PVStructurePtr* raw = new PVStructurePtr[4];
|
||||
raw[0] = pvDataCreate->createPVStructure(standardField->alarm());
|
||||
raw[1] = pvDataCreate->createPVStructure(standardField->alarm());
|
||||
raw[2] = pvDataCreate->createPVStructure(standardField->alarm());
|
||||
raw[3] = pvDataCreate->createPVStructure(standardField->alarm());
|
||||
raw[0] = standardField->alarm()->build();
|
||||
raw[1] = standardField->alarm()->build();
|
||||
raw[2] = standardField->alarm()->build();
|
||||
raw[3] = standardField->alarm()->build();
|
||||
|
||||
PVStructureArray::svector cont(raw, 1, 2);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <epicsUnitTest.h>
|
||||
#include <testMain.h>
|
||||
|
||||
#include <pv/pvUnitTest.h>
|
||||
#include <pv/pvIntrospect.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/convert.h>
|
||||
@@ -29,9 +30,7 @@ static StandardFieldPtr standardField = getStandardField();
|
||||
|
||||
static void print(const string& name, FieldConstPtr const & f)
|
||||
{
|
||||
std::ostringstream strm;
|
||||
strm << std::endl << name << std::endl << f << std::endl;
|
||||
testDiag("%s", strm.str().c_str());
|
||||
testShow()<<name<<'\n'<<format::indent_level(1)<<f;
|
||||
}
|
||||
|
||||
MAIN(testStandardField)
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
/* copy */
|
||||
int testCreateRequest(void);
|
||||
int testPVCopy(void);
|
||||
|
||||
/* misc */
|
||||
int testBaseException(void);
|
||||
@@ -81,7 +80,6 @@ void pvDataAllTests(void)
|
||||
|
||||
/* copy */
|
||||
runTest(testCreateRequest);
|
||||
runTest(testPVCopy);
|
||||
|
||||
/* property */
|
||||
runTest(testCreateRequest);
|
||||
|
||||
Reference in New Issue
Block a user