Compare commits
63 Commits
7.1.0-pre1
...
7.1.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12d851dc6f | ||
|
|
643f289c23 | ||
|
|
68e74ed1d2 | ||
|
|
f0ef0965c4 | ||
|
|
61ce532fdf | ||
|
|
d746e1bfb3 | ||
|
|
deccc41b9a | ||
|
|
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
.travis.yml
17
.travis.yml
@@ -11,19 +11,20 @@ addons:
|
||||
- perl
|
||||
- clang
|
||||
- g++-mingw-w64-i686
|
||||
- qemu-system-x86
|
||||
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=7.0 CMPLR=gcc
|
||||
- BRBASE=7.0 CMPLR=clang
|
||||
- BRBASE=7.0 USR_CXXFLAGS=-std=c++11
|
||||
- BRBASE=7.0 CMPLR=clang USR_CXXFLAGS=-std=c++11
|
||||
- BRBASE=7.0 WINE=32 TEST=NO STATIC=YES
|
||||
- BRBASE=7.0 WINE=32 TEST=NO STATIC=NO
|
||||
- BRBASE=7.0 RTEMS=4.10 TEST=NO
|
||||
- BRBASE=7.0 RTEMS=4.9 TEST=NO
|
||||
- BRBASE=3.16
|
||||
- BRBASE=3.15
|
||||
- BRBASE=3.14
|
||||
|
||||
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,4 @@
|
||||
EPICS_PVD_MAJOR_VERSION = 7
|
||||
EPICS_PVD_MINOR_VERSION = 0
|
||||
EPICS_PVD_MAINTENANCE_VERSION = 1
|
||||
EPICS_PVD_DEVELOPMENT_FLAG = 1
|
||||
EPICS_PVD_MINOR_VERSION = 1
|
||||
EPICS_PVD_MAINTENANCE_VERSION = 0
|
||||
EPICS_PVD_DEVELOPMENT_FLAG = 0
|
||||
|
||||
@@ -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 = 7.1.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
|
||||
@@ -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
|
||||
|
||||
Binary file not shown.
@@ -23,33 +23,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,11 +2,21 @@
|
||||
|
||||
@page release_notes Release Notes
|
||||
|
||||
Release 7.1.0 (UNRELEASED)
|
||||
==========================
|
||||
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.
|
||||
@@ -14,14 +24,19 @@ Release 7.1.0 (UNRELEASED)
|
||||
- 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.
|
||||
- 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
|
||||
- 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.
|
||||
- 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)
|
||||
========================
|
||||
@@ -42,6 +57,9 @@ Release 7.0.0 (Dec 2017)
|
||||
- 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
|
||||
@@ -6,4 +6,5 @@ INC += pv/createRequest.h
|
||||
INC += pv/pvCopy.h
|
||||
|
||||
LIBSRCS += createRequest.cpp
|
||||
LIBSRCS += requestmapper.cpp
|
||||
LIBSRCS += pvCopy.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 */
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
|
||||
#include <compilerDependencies.h>
|
||||
#include <shareLib.h>
|
||||
|
||||
#include <pv/pvData.h>
|
||||
@@ -58,7 +59,7 @@ typedef std::tr1::shared_ptr<CopyStructureNode> CopyStructureNodePtr;
|
||||
* Class that manages one or more PVStructures that holds an arbitrary subset of the fields
|
||||
* in another PVStructure called master.
|
||||
*/
|
||||
class epicsShareClass PVCopy :
|
||||
class epicsShareClass EPICS_DEPRECATED PVCopy :
|
||||
public std::tr1::enable_shared_from_this<PVCopy>
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
#include <sstream>
|
||||
|
||||
#include <epicsThread.h>
|
||||
#include <compilerDependencies.h>
|
||||
#undef EPICS_DEPRECATED
|
||||
#define EPICS_DEPRECATED
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
|
||||
|
||||
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,21 +33,79 @@ 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);
|
||||
}
|
||||
|
||||
Field::~Field() {
|
||||
REFTRACE_DECREMENT(num_instances);
|
||||
FieldCreatePtr create(getFieldCreate());
|
||||
|
||||
Lock G(create->mutex);
|
||||
|
||||
std::pair<FieldCreate::cache_t::iterator, FieldCreate::cache_t::iterator> itp(create->cache.equal_range(m_hash));
|
||||
for(; itp.first!=itp.second; ++itp.first) {
|
||||
Field* cent(itp.first->second);
|
||||
if(cent==this) {
|
||||
create->cache.erase(itp.first);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<PVField> Field::build() const
|
||||
{
|
||||
FieldConstPtr self(shared_from_this());
|
||||
return getPVDataCreate()->createPVField(self);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& o, const Field& f)
|
||||
{
|
||||
@@ -119,6 +176,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
|
||||
@@ -285,6 +347,11 @@ 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() {}
|
||||
|
||||
@@ -336,9 +403,7 @@ StructureArray::StructureArray(StructureConstPtr const & structure)
|
||||
{
|
||||
}
|
||||
|
||||
StructureArray::~StructureArray() {
|
||||
if(debugLevel==highDebug) printf("~StructureArray\n");
|
||||
}
|
||||
StructureArray::~StructureArray() {}
|
||||
|
||||
string StructureArray::getID() const
|
||||
{
|
||||
@@ -365,14 +430,17 @@ 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() {}
|
||||
|
||||
string UnionArray::getID() const
|
||||
{
|
||||
@@ -407,6 +475,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()
|
||||
@@ -544,6 +617,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()
|
||||
@@ -742,6 +820,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 +926,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 +1175,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 +1199,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 +1212,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 +1271,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 +1282,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 +1310,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 +1323,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 +1454,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 +1482,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 +1504,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 +1517,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 +1540,7 @@ struct field_factory {
|
||||
FieldCreatePtr fieldCreate;
|
||||
field_factory() :fieldCreate(new FieldCreate()) {
|
||||
registerRefCounter("Field", &Field::num_instances);
|
||||
registerRefCounter("Thread", &Thread::num_instances);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1485,23 +1569,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()
|
||||
|
||||
@@ -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,521 @@
|
||||
* 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>
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
# include <pv/json.h>
|
||||
#endif
|
||||
|
||||
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) {
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
JSONPrintOptions opts;
|
||||
opts.multiLine = false;
|
||||
printJSON(strm, format.xtop, format.xshow ? *format.xshow : BitSet().set(0), opts);
|
||||
strm<<'\n';
|
||||
return strm;
|
||||
#else
|
||||
// fall through to Raw
|
||||
#endif
|
||||
|
||||
} else if(format.xfmt==PVStructure::Formatter::NT) {
|
||||
std::string id(format.xtop.getStructure()->getID()),
|
||||
idprefix(id.substr(0, id.find_first_of('.')));
|
||||
|
||||
// NTTable
|
||||
if(idprefix=="epics:nt/NTTable:1") {
|
||||
if(printTable(strm, format.xtop))
|
||||
return strm;
|
||||
} else {
|
||||
//NTScalar, NTScalarArray, NTEnum, or anything with '.value'
|
||||
|
||||
PVField::const_shared_pointer value(format.xtop.getSubField("value"));
|
||||
if(value) {
|
||||
switch(value->getField()->getType()) {
|
||||
case scalar:
|
||||
strm<<format::indent();
|
||||
printTimeT(strm, format.xtop);
|
||||
strm<<std::setprecision(6)<<*static_cast<const PVScalar*>(value.get())<<' ';
|
||||
printAlarmT(strm, format.xtop);
|
||||
strm<<'\n';
|
||||
return strm;
|
||||
|
||||
case scalarArray:
|
||||
strm<<format::indent();
|
||||
printTimeT(strm, format.xtop);
|
||||
printAlarmT(strm, format.xtop);
|
||||
strm<<std::setprecision(6)<<*static_cast<const PVScalarArray*>(value.get())<<'\n';
|
||||
return strm;
|
||||
|
||||
case structure:
|
||||
if(printEnumT(strm, format.xtop, true)) {
|
||||
strm<<'\n';
|
||||
return strm;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// fall through unhandled as Raw
|
||||
|
||||
PVStructure::Formatter format2(format);
|
||||
|
||||
if(format2.xmode==PVStructure::Formatter::Auto)
|
||||
format2.xmode = useEscapes(strm) ? PVStructure::Formatter::ANSI : PVStructure::Formatter::Plain;
|
||||
|
||||
printRaw(strm, format2, format.xtop);
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
static char hexdigit(char c) {
|
||||
c &= 0xf;
|
||||
if(c<9)
|
||||
return '0'+c;
|
||||
else
|
||||
return 'A'+c-10;
|
||||
}
|
||||
|
||||
escape::~escape() {}
|
||||
|
||||
std::string escape::str() const
|
||||
{
|
||||
std::ostringstream strm;
|
||||
strm<<(*this);
|
||||
return strm.str();
|
||||
}
|
||||
|
||||
epicsShareFunc
|
||||
std::ostream& operator<<(std::ostream& strm, const escape& Q)
|
||||
{
|
||||
for(size_t pos = 0, len = Q.orig.size(); pos < len; pos++) {
|
||||
const char C = Q.orig[pos];
|
||||
char quote = '\\', next;
|
||||
// compare me with epicsStrnEscapedFromRaw()
|
||||
switch(C) {
|
||||
case '\a': next = 'a'; break;
|
||||
case '\b': next = 'b'; break;
|
||||
case '\f': next = 'f'; break;
|
||||
case '\n': next = 'n'; break;
|
||||
case '\r': next = 'r'; break;
|
||||
case '\t': next = 't'; break;
|
||||
case '\v': next = 'v'; break;
|
||||
case '\\': next = '\\'; break;
|
||||
case '\'': next = '\''; break;
|
||||
case '\"': next = '\"'; if(Q.S==escape::CSV) quote = '"'; break;
|
||||
default:
|
||||
if(!isprint(C)) {
|
||||
// print three charator escape
|
||||
strm<<"\\x"<<hexdigit(C>>4)<<hexdigit(C);
|
||||
} else {
|
||||
// literal
|
||||
strm.put(C);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// print two charactor escape
|
||||
strm.put(quote);
|
||||
strm.put(next);
|
||||
}
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
}} //epics::pvData
|
||||
|
||||
@@ -302,13 +302,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 +322,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,6 +343,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
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <pv/pvdVersion.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/valueBuilder.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
|
||||
@@ -40,9 +41,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 +53,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 +72,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 +85,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 +108,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 +123,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 +132,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 +141,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,11 +212,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
|
||||
|
||||
@@ -51,15 +51,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 +99,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()
|
||||
*
|
||||
|
||||
@@ -33,6 +33,7 @@ LIBSRCS += timer.cpp
|
||||
LIBSRCS += status.cpp
|
||||
LIBSRCS += localStaticLock.cpp
|
||||
LIBSRCS += typeCast.cpp
|
||||
LIBSRCS += thread.cpp
|
||||
LIBSRCS += parseToPOD.cpp
|
||||
LIBSRCS += pvUnitTest.cpp
|
||||
LIBSRCS += debugPtr.cpp
|
||||
|
||||
@@ -254,7 +254,7 @@ epicsParseFloat(const char *str, float *to, char **units)
|
||||
#endif
|
||||
|
||||
// Sometimes we have to provide our own copy of strtoll()
|
||||
#if defined(_WIN32) && !defined(_MINGW)
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1800
|
||||
// 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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -33,10 +33,6 @@
|
||||
#ifndef EPICSEXCEPTION_H_
|
||||
#define EPICSEXCEPTION_H_
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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=epicsThreadStackSmall);
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -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
|
||||
|
||||
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(epicsThreadStackSmall);
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -68,9 +65,8 @@ bool Timer::cancel(TimerCallbackPtr const &timerCallback)
|
||||
{
|
||||
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 +91,7 @@ void Timer::run()
|
||||
|
||||
if(queue.empty()) {
|
||||
// no jobs, just go to sleep
|
||||
waiting = true;
|
||||
epicsGuardRelease<epicsMutex> U(G);
|
||||
|
||||
waitForWork.wait();
|
||||
@@ -122,12 +119,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 +168,7 @@ void Timer::schedulePeriodic(
|
||||
{
|
||||
epicsTime now(epicsTime::getCurrent());
|
||||
|
||||
bool wasempty;
|
||||
bool wakeup;
|
||||
{
|
||||
Lock xx(mutex);
|
||||
if(timerCallback->onList) {
|
||||
@@ -185,10 +184,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
|
||||
|
||||
262
src/pv/pvData.h
262
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>
|
||||
@@ -260,6 +256,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 +337,7 @@ public:
|
||||
|
||||
protected:
|
||||
explicit PVScalar(ScalarConstPtr const & scalar);
|
||||
EPICS_NOT_COPYABLE(PVScalar)
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
@@ -397,31 +395,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 +428,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 +460,7 @@ protected:
|
||||
|
||||
friend class PVDataCreate;
|
||||
storage_t storage;
|
||||
EPICS_NOT_COPYABLE(PVScalarValue)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -547,14 +522,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 +602,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 +697,7 @@ protected:
|
||||
explicit PVScalarArray(ScalarArrayConstPtr const & scalarArray);
|
||||
private:
|
||||
friend class PVDataCreate;
|
||||
EPICS_NOT_COPYABLE(PVScalarArray)
|
||||
};
|
||||
|
||||
|
||||
@@ -740,17 +720,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.
|
||||
@@ -841,14 +821,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 +836,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 +844,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 +857,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 +917,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.
|
||||
*
|
||||
@@ -1020,21 +1046,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 +1074,7 @@ private:
|
||||
int32 selector;
|
||||
PVFieldPtr value;
|
||||
bool variant;
|
||||
EPICS_NOT_COPYABLE(PVUnion)
|
||||
};
|
||||
|
||||
|
||||
@@ -1122,6 +1149,7 @@ namespace detail {
|
||||
return thaw(result);
|
||||
}
|
||||
|
||||
EPICS_NOT_COPYABLE(PVVectorStorage)
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
@@ -1152,67 +1180,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 +1242,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 +1271,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 +1311,7 @@ private:
|
||||
StructureArrayConstPtr structureArray;
|
||||
const_svector value;
|
||||
friend class PVDataCreate;
|
||||
EPICS_NOT_COPYABLE(PVValueArray)
|
||||
};
|
||||
|
||||
|
||||
@@ -1339,24 +1341,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 +1370,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 +1410,7 @@ private:
|
||||
UnionArrayConstPtr unionArray;
|
||||
const_svector value;
|
||||
friend class PVDataCreate;
|
||||
EPICS_NOT_COPYABLE(PVValueArray)
|
||||
};
|
||||
|
||||
|
||||
@@ -1623,6 +1626,7 @@ public:
|
||||
private:
|
||||
PVDataCreate();
|
||||
FieldCreatePtr fieldCreate;
|
||||
EPICS_NOT_COPYABLE(PVDataCreate)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1650,9 +1654,3 @@ namespace std{
|
||||
}
|
||||
|
||||
#endif /* PVDATA_H */
|
||||
|
||||
/** @page Overview Documentation
|
||||
*
|
||||
* <a href = "pvDataCPP.html">pvData.html</a>
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -12,7 +12,9 @@
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
#include <pv/lock.h>
|
||||
#include <pv/noDefaultMethods.h>
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/byteBuffer.h>
|
||||
@@ -21,6 +23,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 +127,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,6 +357,10 @@ 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;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructor
|
||||
@@ -347,6 +369,9 @@ protected:
|
||||
Field(Type type);
|
||||
private:
|
||||
const Type m_fieldType;
|
||||
unsigned int m_hash;
|
||||
struct Helper;
|
||||
friend struct Helper;
|
||||
|
||||
friend class StructureArray;
|
||||
friend class Structure;
|
||||
@@ -354,8 +379,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 +407,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 +422,7 @@ private:
|
||||
friend class BoundedScalarArray;
|
||||
friend class FixedScalarArray;
|
||||
friend class BoundedString;
|
||||
EPICS_NOT_COPYABLE(Scalar)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -418,6 +447,7 @@ protected:
|
||||
private:
|
||||
std::size_t maxLength;
|
||||
friend class FieldCreate;
|
||||
EPICS_NOT_COPYABLE(BoundedString)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -452,6 +482,7 @@ protected:
|
||||
*/
|
||||
Array(Type type);
|
||||
|
||||
EPICS_NOT_COPYABLE(Array)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -485,13 +516,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 +556,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 +588,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 +622,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 +667,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)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -731,7 +778,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:
|
||||
@@ -743,6 +794,7 @@ private:
|
||||
|
||||
friend class FieldCreate;
|
||||
friend class Union;
|
||||
EPICS_NOT_COPYABLE(Structure)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -865,6 +917,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();
|
||||
@@ -878,6 +934,7 @@ private:
|
||||
|
||||
friend class FieldCreate;
|
||||
friend class Structure;
|
||||
EPICS_NOT_COPYABLE(Union)
|
||||
};
|
||||
|
||||
class FieldCreate;
|
||||
@@ -897,6 +954,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 +981,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 +1006,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 +1015,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 +1155,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 +1168,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 +1274,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 +1339,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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
#include <epicsUnitTest.h>
|
||||
#include <testMain.h>
|
||||
|
||||
#include <compilerDependencies.h>
|
||||
#undef EPICS_DEPRECATED
|
||||
#define EPICS_DEPRECATED
|
||||
|
||||
#include <pv/standardField.h>
|
||||
#include <pv/standardPVField.h>
|
||||
#include <pv/convert.h>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,6 @@
|
||||
*/
|
||||
/* Author: Michael Davidsaver */
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
368
testApp/misc/testprinter.cpp
Normal file
368
testApp/misc/testprinter.cpp
Normal file
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
# define USE_JSON
|
||||
#endif
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -384,7 +384,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 +522,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 +628,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"));
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user