11 Commits
8.0.4 ... 6.0.1

Author SHA1 Message Date
Andrew Johnson
e0367a2dd7 Update release notes and release date. 2016-09-14 17:01:51 -05:00
Andrew Johnson
1e0f1119e2 testTypeCast: mangle values to be printable
print  (u)int8 as integer instead of character

(cherry picked from commit da8ba56dd1)
2016-09-14 16:43:53 -05:00
Andrew Johnson
6d6314a924 #include <algorithm> required for MSVS 2015
(cherry picked from commit 4555f69733)
2016-09-14 16:33:37 -05:00
Dave Hickin
e0b881d036 Correction to documentation for 6.0.1 2016-09-14 11:25:30 +01:00
Dave Hickin
d24ff6c3d2 Update documentation for 6.0.1 2016-09-13 22:44:11 +01:00
Dave Hickin
ef17aac046 Update release notes for 6.0.1 2016-09-13 22:33:28 +01:00
Dave Hickin
83b63a9138 Fix for MinGW
Fixes #42
2016-09-07 15:14:17 +01:00
Dave Hickin
0a5ab8625d Update documentation for 6.0.0 2016-08-01 17:25:12 +01:00
Dave Hickin
a115c9cd15 Add release notes for 6.0.0 2016-08-01 17:23:11 +01:00
Dave Hickin
6ba40803d8 Add 5.0.x release notes 2016-08-01 15:00:36 +01:00
Dave Hickin
8d6094c55a jenkins: Remove COPYRIGHT from tar 2016-07-22 16:08:27 +01:00
178 changed files with 15454 additions and 13793 deletions

View File

@@ -1,94 +0,0 @@
# .appveyor.yml for use with EPICS Base ci-scripts
# (see: https://github.com/epics-base/ci-scripts)
cache:
- C:\Users\appveyor\.tools
#---------------------------------#
# additional packages #
#---------------------------------#
install:
# for the sequencer
- cinst re2c
- cmd: git submodule update --init --recursive
#---------------------------------#
# repository cloning #
#---------------------------------#
init:
# Set autocrlf to make batch files work
- git config --global core.autocrlf true
clone_depth: 50
# Skipping commits affecting only specific files
skip_commits:
files:
- 'documentation/*'
- '**/*.md'
# Build Configurations: dll/static, regular/debug
configuration:
- dynamic
- static
- dynamic-debug
- static-debug
# Environment variables: compiler toolchain, base version, setup file, ...
environment:
# common / default variables for all jobs
SETUP_PATH: .ci-local
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
CMP: vs2019
BASE: 7.0
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
CMP: gcc
BASE: 7.0
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
CMP: vs2017
BASE: 7.0
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
CMP: vs2019
BASE: 3.15
# Platform: processor architecture
platform:
- x64
#---------------------------------#
# building & testing #
#---------------------------------#
build_script:
- cmd: python .ci/cue.py prepare
- cmd: python .ci/cue.py build
test_script:
- cmd: python .ci/cue.py test
on_finish:
- ps: Get-ChildItem *.tap -Recurse -Force | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
- cmd: python .ci/cue.py build test-results -s
#---------------------------------#
# debugging #
#---------------------------------#
## if you want to connect by remote desktop to a failed build, uncomment these lines
## note that you will need to connect within the usual build timeout limit (60 minutes)
## so you may want to adjust the build matrix above to just build the one of interest
#on_failure:
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
#---------------------------------#
# notifications #
#---------------------------------#
notifications:
- provider: GitHubPullRequest

1
.ci

Submodule .ci deleted from ad8dd4a136

View File

@@ -1,6 +0,0 @@
# EPICS Base
BASE_DIRNAME=base
BASE_REPONAME=epics-base
BASE_REPOOWNER=epics-base
BASE_VARNAME=EPICS_BASE
BASE_RECURSIVE=NO

View File

@@ -1,153 +0,0 @@
# .github/workflows/ci-scripts-build.yml for use with EPICS Base ci-scripts
# (see: https://github.com/epics-base/ci-scripts)
# This is YAML - indentation levels are crucial
# Set the 'name:' properties to values that work for you (pvxs)
name: pvData
# Trigger on pushes and PRs to any branch
on:
push:
paths-ignore:
- .appveyor.yml
pull_request:
env:
SETUP_PATH: .ci-local:.ci
EPICS_TEST_IMPRECISE_TIMING: YES
jobs:
build-base:
name: ${{ matrix.base }}/${{ matrix.os }}/${{ matrix.cmp }}/${{ matrix.configuration }}/${{ matrix.wine }}${{ matrix.rtems }}/${{ matrix.extra }}
runs-on: ${{ matrix.os }}
# Set environment variables from matrix parameters
env:
BASE: ${{ matrix.base }}
CMP: ${{ matrix.cmp }}
BCFG: ${{ matrix.configuration }}
WINE: ${{ matrix.wine }}
RTEMS: ${{ matrix.rtems }}
EXTRA: ${{ matrix.extra }}
TEST: ${{ matrix.test }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-20.04
cmp: gcc
configuration: default
base: "7.0"
wine: "64"
- os: ubuntu-20.04
cmp: gcc
configuration: static
base: "7.0"
wine: "64"
- os: ubuntu-20.04
cmp: gcc
configuration: default
base: "3.15"
wine: "64"
- os: ubuntu-20.04
cmp: gcc
configuration: static
base: "7.0"
extra: "CMD_CXXFLAGS=-std=c++11"
- os: ubuntu-16.04
cmp: clang
configuration: default
base: "7.0"
- os: ubuntu-20.04
cmp: clang
configuration: default
base: "7.0"
extra: "CMD_CXXFLAGS=-std=c++11"
- os: ubuntu-20.04
cmp: gcc
configuration: default
base: "7.0"
rtems: "4.10"
- os: ubuntu-20.04
cmp: gcc
configuration: default
base: "7.0"
rtems: "4.9"
- os: ubuntu-16.04
cmp: gcc-4.8
utoolchain: true
configuration: default
base: "7.0"
- os: ubuntu-16.04
cmp: gcc-4.9
utoolchain: true
configuration: default
base: "7.0"
- os: ubuntu-20.04
cmp: gcc-8
utoolchain: true
configuration: default
base: "7.0"
- os: ubuntu-20.04
cmp: clang
configuration: default
base: "7.0"
- os: macos-latest
cmp: clang
configuration: default
base: "7.0"
- os: windows-2019
cmp: vs2019
configuration: default
base: "7.0"
- os: windows-2019
cmp: vs2019
configuration: static
base: "7.0"
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Cache Dependencies
uses: actions/cache@v2
with:
path: ~/.cache
key: ${{ matrix.base }}/${{ matrix.os }}/${{ matrix.cmp }}/${{ matrix.configuration }}/${{ matrix.wine }}${{ matrix.rtems }}/${{ matrix.extra }}
- name: Automatic core dump analysis
uses: mdavidsaver/ci-core-dumper@master
- name: "apt-get install"
run: |
sudo apt-get update
sudo apt-get -y install qemu-system-x86 g++-mingw-w64-x86-64 gdb
if: runner.os == 'Linux'
- name: "apt-get install ${{ matrix.cmp }}"
run: |
sudo apt-get -y install software-properties-common
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get -y install ${{ matrix.cmp }}
if: matrix.utoolchain
- name: Prepare and compile dependencies
run: python .ci/cue.py prepare
- name: Build main module
run: python .ci/cue.py build
- name: Run main module tests
run: python .ci/cue.py test
- name: Collect and show test results
run: python .ci/cue.py test-results

30
.gitignore vendored
View File

@@ -1,17 +1,15 @@
/cfg/
/bin/
/lib/
/db/
/dbd/
/html/
/include/
/templates/
/configure/*.local
/configure/RELEASE.*
/configure/CONFIG_SITE.*
O.*/
/QtC-*
bin/
lib/
doc/
include/
db/
dbd/
documentation/html
documentation/*.tag
envPaths
*.orig
*.log
.*.swp
configure/*.local
configure/RELEASE.*
configure/CONFIG_SITE.*
!configure/ExampleRELEASE.local
**/O.*
QtC-*

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule ".ci"]
path = .ci
url = https://github.com/epics-base/ci-scripts

View File

@@ -1,17 +0,0 @@
# .readthedocs.yml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Build documentation in the documentation/ directory with Sphinx
sphinx:
configuration: documentation/conf.py
# Build documentation with MkDocs
#mkdocs:
# configuration: mkdocs.yml
# Optionally build your docs in additional formats such as PDF and ePub
formats: all

316
Doxyfile
View File

@@ -1,4 +1,4 @@
# Doxyfile 1.8.8
# Doxyfile 1.8.6
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@@ -32,33 +32,33 @@ DOXYFILE_ENCODING = UTF-8
# title of most generated pages and in a few other places.
# The default value is: My Project.
PROJECT_NAME = "PVData C++"
PROJECT_NAME = pvDataCPP
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 8.0.4
PROJECT_NUMBER =
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
# quick idea about the purpose of the project. Keep the description short.
PROJECT_BRIEF =
PROJECT_BRIEF =
# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
# the documentation. The maximum height of the logo should not exceed 55 pixels
# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
# to the output directory.
PROJECT_LOGO =
PROJECT_LOGO =
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
# into which the generated documentation will be written. If a relative path is
# entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used.
OUTPUT_DIRECTORY = .
OUTPUT_DIRECTORY =
# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
# directories (in 2 levels) under the output directory of each output format and
@@ -70,14 +70,6 @@ OUTPUT_DIRECTORY = .
CREATE_SUBDIRS = NO
# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
# characters to appear in the names of generated files. If set to NO, non-ASCII
# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
# U+3044.
# The default value is: NO.
ALLOW_UNICODE_NAMES = NO
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
@@ -118,17 +110,7 @@ REPEAT_BRIEF = YES
# the entity):The $name class, The $name widget, The $name file, is, provides,
# specifies, contains, represents, a, an and the.
ABBREVIATE_BRIEF = "The $name class" \
"The $name widget" \
"The $name file" \
is \
provides \
specifies \
contains \
represents \
a \
an \
the
ABBREVIATE_BRIEF =
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
# doxygen will generate a detailed section even if there is only a brief
@@ -143,7 +125,7 @@ ALWAYS_DETAILED_SEC = NO
# operators of the base classes will not be shown.
# The default value is: NO.
INLINE_INHERITED_MEMB = NO
INLINE_INHERITED_MEMB = YES
# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
# before files name in the file list and in the header files. If set to NO the
@@ -162,7 +144,7 @@ FULL_PATH_NAMES = YES
# will be relative from the directory where doxygen is started.
# This tag requires that the tag FULL_PATH_NAMES is set to YES.
STRIP_FROM_PATH = src
STRIP_FROM_PATH =
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
# path mentioned in the documentation of a class, which tells the reader which
@@ -171,7 +153,7 @@ STRIP_FROM_PATH = src
# specify the list of include paths that are normally passed to the compiler
# using the -I flag.
STRIP_FROM_INC_PATH = src
STRIP_FROM_INC_PATH =
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
# less readable) file names. This can be useful is your file systems doesn't
@@ -238,13 +220,13 @@ TAB_SIZE = 4
# "Side Effects:". You can put \n's in the value part of an alias to insert
# newlines.
ALIASES =
ALIASES =
# This tag can be used to specify a number of word-keyword mappings (TCL only).
# A mapping has the form "name=value". For example adding "class=itcl::class"
# will allow you to use the command class in the itcl::class meaning.
TCL_SUBST =
TCL_SUBST =
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For
@@ -279,19 +261,16 @@ OPTIMIZE_OUTPUT_VHDL = NO
# extension. Doxygen has a built-in mapping, but you can override or extend it
# using this tag. The format is ext=language, where ext is a file extension, and
# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
# Fortran. In the later case the parser tries to guess whether the code is fixed
# or free formatted code, this is the default for Fortran type files), VHDL. For
# instance to make doxygen treat .inc files as Fortran files (default is PHP),
# and .f files as C (default is Fortran), use: inc=Fortran f=C.
# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
# (default is Fortran), use: inc=Fortran f=C.
#
# Note For files without extension you can use no_extension as a placeholder.
#
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
# the files are not read by doxygen.
EXTENSION_MAPPING =
EXTENSION_MAPPING =
# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
# according to the Markdown format, which allows for more readable
@@ -319,7 +298,7 @@ AUTOLINK_SUPPORT = YES
# diagrams that involve STL classes more complete and accurate.
# The default value is: NO.
BUILTIN_STL_SUPPORT = NO
BUILTIN_STL_SUPPORT = YES
# If you use Microsoft's C++/CLI language, you should set this option to YES to
# enable parsing support.
@@ -419,7 +398,7 @@ LOOKUP_CACHE_SIZE = 0
# normally produced when WARNINGS is set to YES.
# The default value is: NO.
EXTRACT_ALL = NO
EXTRACT_ALL = YES
# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
# be included in the documentation.
@@ -508,7 +487,7 @@ INTERNAL_DOCS = NO
# and Mac users are advised to set this option to NO.
# The default value is: system dependent.
CASE_SENSE_NAMES = NO
CASE_SENSE_NAMES = YES
# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
# their full class and namespace scopes in the documentation. If set to YES the
@@ -627,7 +606,7 @@ GENERATE_DEPRECATEDLIST= YES
# sections, marked by \if <section_label> ... \endif and \cond <section_label>
# ... \endcond blocks.
ENABLED_SECTIONS =
ENABLED_SECTIONS =
# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
# initial value of a variable or macro / define can have for it to appear in the
@@ -669,7 +648,7 @@ SHOW_NAMESPACES = YES
# by doxygen. Whatever the program writes to standard output is used as the file
# version. For an example see the documentation.
FILE_VERSION_FILTER =
FILE_VERSION_FILTER =
# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
# by doxygen. The layout file controls the global structure of the generated
@@ -682,7 +661,7 @@ FILE_VERSION_FILTER =
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
# tag is left empty.
LAYOUT_FILE =
LAYOUT_FILE =
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
# the reference definitions. This must be a list of .bib files. The .bib
@@ -690,9 +669,10 @@ LAYOUT_FILE =
# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
# For LaTeX the style of the bibliography can be controlled using
# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
# search path. See also \cite for info how to create references.
# search path. Do not use file names with spaces, bibtex cannot handle them. See
# also \cite for info how to create references.
CITE_BIB_FILES =
CITE_BIB_FILES =
#---------------------------------------------------------------------------
# Configuration options related to warning and progress messages
@@ -719,7 +699,7 @@ WARNINGS = YES
# will automatically be disabled.
# The default value is: YES.
WARN_IF_UNDOCUMENTED = NO
WARN_IF_UNDOCUMENTED = YES
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
# potential errors in the documentation, such as not documenting some parameters
@@ -751,7 +731,7 @@ WARN_FORMAT = "$file:$line: $text"
# messages should be written. If left blank the output is written to standard
# error (stderr).
WARN_LOGFILE =
WARN_LOGFILE =
#---------------------------------------------------------------------------
# Configuration options related to the input files
@@ -763,12 +743,7 @@ WARN_LOGFILE =
# spaces.
# Note: If this tag is empty the current directory is searched.
INPUT = src/pv \
documentation/mainpage.dox \
src/copy/pv \
src/misc/pv \
src/json/pv \
documentation/release_notes.dox
INPUT = include
# 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
@@ -788,13 +763,13 @@ INPUT_ENCODING = UTF-8
# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
# *.qsf, *.as and *.js.
FILE_PATTERNS =
FILE_PATTERNS =
# The RECURSIVE tag can be used to specify whether or not subdirectories should
# be searched for input files as well.
# The default value is: NO.
RECURSIVE = NO
RECURSIVE = YES
# The EXCLUDE tag can be used to specify files and/or directories that should be
# excluded from the INPUT source files. This way you can easily exclude a
@@ -803,7 +778,7 @@ RECURSIVE = NO
# Note that relative paths are relative to the directory from which doxygen is
# run.
EXCLUDE =
EXCLUDE =
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
@@ -819,7 +794,7 @@ EXCLUDE_SYMLINKS = NO
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories for example use the pattern */test/*
EXCLUDE_PATTERNS =
EXCLUDE_PATTERNS =
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
# (namespaces, classes, functions, etc.) that should be excluded from the
@@ -830,20 +805,20 @@ EXCLUDE_PATTERNS =
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories use the pattern */test/*
EXCLUDE_SYMBOLS =
EXCLUDE_SYMBOLS =
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
# that contain example code fragments that are included (see the \include
# command).
EXAMPLE_PATH = examples
EXAMPLE_PATH =
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
# *.h) to filter out the source-files in the directories. If left blank all
# files are included.
EXAMPLE_PATTERNS = *.cpp
EXAMPLE_PATTERNS =
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# searched for input files to be used with the \include or \dontinclude commands
@@ -856,7 +831,7 @@ EXAMPLE_RECURSIVE = NO
# that contain images that are to be included in the documentation (see the
# \image command).
IMAGE_PATH =
IMAGE_PATH =
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
@@ -873,7 +848,7 @@ IMAGE_PATH =
# code is scanned, but not when the output code is generated. If lines are added
# or removed, the anchors will not be placed correctly.
INPUT_FILTER =
INPUT_FILTER =
# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
# basis. Doxygen will compare the file name with each pattern and apply the
@@ -882,7 +857,7 @@ INPUT_FILTER =
# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
# patterns match the file name, INPUT_FILTER is applied.
FILTER_PATTERNS =
FILTER_PATTERNS =
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
# INPUT_FILTER ) will also be used to filter the input files that are used for
@@ -897,14 +872,14 @@ FILTER_SOURCE_FILES = NO
# *.ext= (so without naming a filter).
# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
FILTER_SOURCE_PATTERNS =
FILTER_SOURCE_PATTERNS =
# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
# is part of the input, its contents will be placed on the main page
# (index.html). This can be useful if you have a project on for instance GitHub
# and want to reuse the introduction page also for the doxygen output.
USE_MDFILE_AS_MAINPAGE =
USE_MDFILE_AS_MAINPAGE =
#---------------------------------------------------------------------------
# Configuration options related to source browsing
@@ -936,13 +911,13 @@ STRIP_CODE_COMMENTS = YES
# function all documented functions referencing it will be listed.
# The default value is: NO.
REFERENCED_BY_RELATION = NO
REFERENCED_BY_RELATION = YES
# If the REFERENCES_RELATION tag is set to YES then for each documented function
# all documented entities called/used by that function will be listed.
# The default value is: NO.
REFERENCES_RELATION = NO
REFERENCES_RELATION = YES
# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
@@ -992,25 +967,6 @@ USE_HTAGS = NO
VERBATIM_HEADERS = YES
# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the
# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
# cost of reduced performance. This can be particularly helpful with template
# rich C++ code for which doxygen's built-in parser lacks the necessary type
# information.
# Note: The availability of this option depends on whether or not doxygen was
# compiled with the --with-libclang option.
# The default value is: NO.
CLANG_ASSISTED_PARSING = YES
# If clang assisted parsing is enabled you can provide the compiler with command
# line options that you would normally use when invoking the compiler. Note that
# the include paths will already be set by doxygen for the files and directories
# specified with INPUT and INCLUDE_PATH.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_OPTIONS =
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
@@ -1020,7 +976,7 @@ CLANG_OPTIONS =
# classes, structs, unions or interfaces.
# The default value is: YES.
ALPHABETICAL_INDEX = YES
ALPHABETICAL_INDEX = NO
# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
# which the alphabetical index list will be split.
@@ -1035,7 +991,7 @@ COLS_IN_ALPHA_INDEX = 5
# while generating the index headers.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
IGNORE_PREFIX =
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the HTML output
@@ -1052,7 +1008,7 @@ GENERATE_HTML = YES
# The default directory is: html.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_OUTPUT = html/doxygen
HTML_OUTPUT = documentation/html
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
# generated HTML page (for example: .htm, .php, .asp).
@@ -1079,7 +1035,7 @@ HTML_FILE_EXTENSION = .html
# of the possible markers and block names see the documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_HEADER =
HTML_HEADER =
# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
# generated HTML page. If the tag is left blank doxygen will generate a standard
@@ -1089,7 +1045,7 @@ HTML_HEADER =
# that doxygen normally uses.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FOOTER =
HTML_FOOTER =
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
# sheet that is used by each HTML page. It can be used to fine-tune the look of
@@ -1101,20 +1057,18 @@ HTML_FOOTER =
# obsolete.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_STYLESHEET =
HTML_STYLESHEET =
# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# cascading style sheets that are included after the standard style sheets
# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
# defined cascading style sheet that is included after the standard style sheets
# created by doxygen. Using this option one can overrule certain style aspects.
# This is preferred over using HTML_STYLESHEET since it does not replace the
# standard style sheet and is therefor more robust against future updates.
# Doxygen will copy the style sheet files to the output directory.
# Note: The order of the extra stylesheet files is of importance (e.g. the last
# stylesheet in the list overrules the setting of the previous ones in the
# list). For an example see the documentation.
# Doxygen will copy the style sheet file to the output directory. For an example
# see the documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET =
HTML_EXTRA_STYLESHEET =
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the HTML output directory. Note
@@ -1124,7 +1078,7 @@ HTML_EXTRA_STYLESHEET =
# files will be copied as-is; there are no commands or markers available.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_FILES =
HTML_EXTRA_FILES = documentation/pvDataCPP.html
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the stylesheet and background images according to
@@ -1252,7 +1206,7 @@ GENERATE_HTMLHELP = NO
# written to the html output directory.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_FILE =
CHM_FILE =
# The HHC_LOCATION tag can be used to specify the location (absolute path
# including file name) of the HTML help compiler ( hhc.exe). If non-empty
@@ -1260,7 +1214,7 @@ CHM_FILE =
# The file has to be specified with full path.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
HHC_LOCATION =
HHC_LOCATION =
# The GENERATE_CHI flag controls if a separate .chi index file is generated (
# YES) or that it should be included in the master .chm file ( NO).
@@ -1273,11 +1227,10 @@ GENERATE_CHI = NO
# and project file content.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_INDEX_ENCODING =
CHM_INDEX_ENCODING =
# The BINARY_TOC flag controls whether a binary table of contents is generated (
# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it
# enables the Previous and Next buttons.
# YES) or a normal table of contents ( NO) in the .chm file.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
@@ -1304,7 +1257,7 @@ GENERATE_QHP = NO
# the HTML output folder.
# This tag requires that the tag GENERATE_QHP is set to YES.
QCH_FILE =
QCH_FILE =
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace
@@ -1329,7 +1282,7 @@ QHP_VIRTUAL_FOLDER = doc
# filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME =
QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom
@@ -1337,21 +1290,21 @@ QHP_CUST_FILTER_NAME =
# filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS =
QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
# project's filter section matches. Qt Help Project / Filter Attributes (see:
# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS =
QHP_SECT_FILTER_ATTRS =
# The QHG_LOCATION tag can be used to specify the location of Qt's
# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
# generated .qhp file.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHG_LOCATION =
QHG_LOCATION =
# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
# generated, together with the HTML files, they form an Eclipse help plugin. To
@@ -1484,7 +1437,7 @@ MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_EXTENSIONS =
MATHJAX_EXTENSIONS =
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
# of code that will be used on startup of the MathJax code. See the MathJax site
@@ -1492,7 +1445,7 @@ MATHJAX_EXTENSIONS =
# example see the documentation.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_CODEFILE =
MATHJAX_CODEFILE =
# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
# the HTML output. The underlying search engine uses javascript and DHTML and
@@ -1513,15 +1466,15 @@ MATHJAX_CODEFILE =
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
SEARCHENGINE = YES
SEARCHENGINE = NO
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
# implemented using a web server instead of a web client using Javascript. There
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
# setting. When disabled, doxygen will generate a PHP script for searching and
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
# and searching needs to be provided by external tools. See the section
# "External Indexing and Searching" for details.
# are two flavours of web server based searching depending on the
# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
# searching and an index file used by the script. When EXTERNAL_SEARCH is
# enabled the indexing and searching needs to be provided by external tools. See
# the section "External Indexing and Searching" for details.
# The default value is: NO.
# This tag requires that the tag SEARCHENGINE is set to YES.
@@ -1552,7 +1505,7 @@ EXTERNAL_SEARCH = NO
# Searching" for details.
# This tag requires that the tag SEARCHENGINE is set to YES.
SEARCHENGINE_URL =
SEARCHENGINE_URL =
# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
# search data is written to a file for indexing by an external tool. With the
@@ -1568,7 +1521,7 @@ SEARCHDATA_FILE = searchdata.xml
# projects and redirect the results back to the right project.
# This tag requires that the tag SEARCHENGINE is set to YES.
EXTERNAL_SEARCH_ID =
EXTERNAL_SEARCH_ID =
# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
# projects other than the one defined by this configuration file, but that are
@@ -1578,7 +1531,7 @@ EXTERNAL_SEARCH_ID =
# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
# This tag requires that the tag SEARCHENGINE is set to YES.
EXTRA_SEARCH_MAPPINGS =
EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
# Configuration options related to the LaTeX output
@@ -1630,7 +1583,7 @@ COMPACT_LATEX = NO
# The default value is: a4.
# This tag requires that the tag GENERATE_LATEX is set to YES.
PAPER_TYPE = a4
PAPER_TYPE = a4wide
# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
# that should be included in the LaTeX output. To get the times font for
@@ -1639,7 +1592,7 @@ PAPER_TYPE = a4
# If left blank no extra packages will be included.
# This tag requires that the tag GENERATE_LATEX is set to YES.
EXTRA_PACKAGES =
EXTRA_PACKAGES =
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
# generated LaTeX document. The header should contain everything until the first
@@ -1649,24 +1602,22 @@ EXTRA_PACKAGES =
#
# Note: Only use a user-defined header if you know what you are doing! The
# following commands have a special meaning inside the header: $title,
# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
# $projectbrief, $projectlogo. Doxygen will replace $title with the empy string,
# for the replacement values of the other commands the user is refered to
# HTML_HEADER.
# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
# replace them by respectively the title of the page, the current date and time,
# only the current date, the version number of doxygen, the project name (see
# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HEADER =
LATEX_HEADER =
# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
# generated LaTeX document. The footer should contain everything after the last
# chapter. If it is left blank doxygen will generate a standard footer. See
# LATEX_HEADER for more information on how to generate a default footer and what
# special commands can be used inside the footer.
# chapter. If it is left blank doxygen will generate a standard footer.
#
# Note: Only use a user-defined footer if you know what you are doing!
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_FOOTER =
LATEX_FOOTER =
# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the LATEX_OUTPUT output
@@ -1674,7 +1625,7 @@ LATEX_FOOTER =
# markers available.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EXTRA_FILES =
LATEX_EXTRA_FILES =
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
@@ -1683,15 +1634,15 @@ LATEX_EXTRA_FILES =
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
PDF_HYPERLINKS = YES
PDF_HYPERLINKS = NO
# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
# the PDF file directly from the LaTeX files. Set this option to YES to get a
# higher quality PDF documentation.
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
USE_PDFLATEX = YES
USE_PDFLATEX = NO
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
# command to the generated LaTeX files. This will instruct LaTeX to keep running
@@ -1774,14 +1725,14 @@ RTF_HYPERLINKS = NO
# default style sheet that doxygen normally uses.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_STYLESHEET_FILE =
RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an RTF document. Syntax is
# similar to doxygen's config file. A template extensions file can be generated
# using doxygen -e rtf extensionFile.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_EXTENSIONS_FILE =
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# Configuration options related to the man page output
@@ -1811,13 +1762,6 @@ MAN_OUTPUT = man
MAN_EXTENSION = .3
# The MAN_SUBDIR tag determines the name of the directory created within
# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
# MAN_EXTENSION with the initial . removed.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_SUBDIR =
# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
# will generate one additional man file for each entity documented in the real
# man page(s). These additional files only source the real man page, but without
@@ -1845,6 +1789,18 @@ GENERATE_XML = NO
XML_OUTPUT = xml
# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a
# validating XML parser to check the syntax of the XML files.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_SCHEMA =
# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
# validating XML parser to check the syntax of the XML files.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_DTD =
# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
# listings (including syntax highlighting and cross-referencing information) to
# the XML output. Note that enabling this will significantly increase the size
@@ -1872,15 +1828,6 @@ GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook
# If the DOCBOOK_PROGRAMLISTING tag is set to YES doxygen will include the
# program listings (including syntax highlighting and cross-referencing
# information) to the DOCBOOK output. Note that enabling this will significantly
# increase the size of the DOCBOOK output.
# The default value is: NO.
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
DOCBOOK_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
@@ -1929,7 +1876,7 @@ PERLMOD_PRETTY = YES
# overwrite each other's variables.
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_MAKEVAR_PREFIX =
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
@@ -1948,7 +1895,7 @@ ENABLE_PREPROCESSING = YES
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
MACRO_EXPANSION = YES
MACRO_EXPANSION = NO
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
# the macro expansion is limited to the macros specified with the PREDEFINED and
@@ -1970,7 +1917,7 @@ SEARCH_INCLUDES = YES
# preprocessor.
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
INCLUDE_PATH = src/misc src ../../include/
INCLUDE_PATH =
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
@@ -1978,7 +1925,7 @@ INCLUDE_PATH = src/misc src ../../include/
# used.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
INCLUDE_FILE_PATTERNS =
INCLUDE_FILE_PATTERNS =
# The PREDEFINED tag can be used to specify one or more macro names that are
# defined before the preprocessor is started (similar to the -D option of e.g.
@@ -1988,7 +1935,7 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED =
PREDEFINED =
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
@@ -2000,14 +1947,14 @@ PREDEFINED =
EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
# remove all references to function-like macros that are alone on a line, have
# an all uppercase name, and do not end with a semicolon. Such function macros
# are typically used for boiler-plate code, and will confuse the parser if not
# remove all refrences to function-like macros that are alone on a line, have an
# all uppercase name, and do not end with a semicolon. Such function macros are
# typically used for boiler-plate code, and will confuse the parser if not
# removed.
# The default value is: YES.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
SKIP_FUNCTION_MACROS = NO
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration options related to external references
@@ -2022,17 +1969,17 @@ SKIP_FUNCTION_MACROS = NO
# where loc1 and loc2 can be relative or absolute paths or URLs. See the
# section "Linking to external documentation" for more information about the use
# of tag files.
# Note: Each tag file must have a unique name (where the name does NOT include
# Note: Each tag file must have an unique name (where the name does NOT include
# the path). If a tag file is not located in the directory in which doxygen is
# run, you must also specify the path to the tagfile here.
TAGFILES = "libstdc++.tag = http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen"
TAGFILES =
# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
# tag file that is based on the input files it reads. See section "Linking to
# external documentation" for more information about the usage of tag files.
GENERATE_TAGFILE = pvdata.tag
GENERATE_TAGFILE =
# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
# class index. If set to NO only the inherited external classes will be listed.
@@ -2045,14 +1992,14 @@ ALLEXTERNALS = NO
# listed.
# The default value is: YES.
EXTERNAL_GROUPS = NO
EXTERNAL_GROUPS = YES
# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
# the related pages index. If set to NO, only the current project's pages will
# be listed.
# The default value is: YES.
EXTERNAL_PAGES = NO
EXTERNAL_PAGES = YES
# The PERL_PATH should be the absolute path and name of the perl script
# interpreter (i.e. the result of 'which perl').
@@ -2080,14 +2027,14 @@ CLASS_DIAGRAMS = YES
# the mscgen tool resides. If left empty the tool is assumed to be found in the
# default search path.
MSCGEN_PATH =
MSCGEN_PATH =
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
# If left empty dia is assumed to be found in the default search path.
DIA_PATH =
DIA_PATH =
# If set to YES, the inheritance and collaboration graphs will hide inheritance
# and usage relations if the target is undocumented or is not a class.
@@ -2100,9 +2047,9 @@ HIDE_UNDOC_RELATIONS = YES
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# Bell Labs. The other options in this section have no effect if this option is
# set to NO
# The default value is: YES.
# The default value is: NO.
HAVE_DOT = YES
HAVE_DOT = NO
# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
# to run in parallel. When set to 0 doxygen will base this on the number of
@@ -2114,7 +2061,7 @@ HAVE_DOT = YES
DOT_NUM_THREADS = 0
# When you want a differently looking font in the dot files that doxygen
# When you want a differently looking font n the dot files that doxygen
# generates you can specify the font name using DOT_FONTNAME. You need to make
# sure dot is able to find the font, which can be done by putting it in a
# standard location or by setting the DOTFONTPATH environment variable or by
@@ -2136,7 +2083,7 @@ DOT_FONTSIZE = 10
# the path where dot can find it using this tag.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTPATH =
DOT_FONTPATH =
# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
# each documented class showing the direct and indirect inheritance relations.
@@ -2252,9 +2199,7 @@ DIRECTORY_GRAPH = YES
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
# to make the SVG files visible in IE 9+ (other browsers do not have this
# requirement).
# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
# gif:cairo:gd, gif:gd, gif:gd:gd and svg.
# Possible values are: png, jpg, gif and svg.
# The default value is: png.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2276,35 +2221,26 @@ INTERACTIVE_SVG = NO
# found. If left blank, it is assumed the dot tool can be found in the path.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_PATH =
DOT_PATH =
# The DOTFILE_DIRS tag can be used to specify one or more directories that
# contain dot files that are included in the documentation (see the \dotfile
# command).
# This tag requires that the tag HAVE_DOT is set to YES.
DOTFILE_DIRS =
DOTFILE_DIRS =
# The MSCFILE_DIRS tag can be used to specify one or more directories that
# contain msc files that are included in the documentation (see the \mscfile
# command).
MSCFILE_DIRS =
MSCFILE_DIRS =
# The DIAFILE_DIRS tag can be used to specify one or more directories that
# contain dia files that are included in the documentation (see the \diafile
# command).
DIAFILE_DIRS =
# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
# path where java can find the plantuml.jar file. If left blank, it is assumed
# PlantUML is not used or called during a preprocessing step. Doxygen will
# generate a warning when it encounters a \startuml command in this case and
# will not generate output for the diagram.
# This tag requires that the tag HAVE_DOT is set to YES.
PLANTUML_JAR_PATH =
DIAFILE_DIRS =
# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
# that will be shown in the graph. If the number of nodes in a graph becomes

View File

@@ -11,7 +11,4 @@ src_DEPEND_DIRS = configure
DIRS += testApp
testApp_DEPEND_DIRS = src
DIRS += examples
examples_DEPEND_DIRS = src
include $(TOP)/configure/RULES_TOP

View File

@@ -1,16 +1,57 @@
# pvaDataCPP
pvaDataCPP
==========
The EPICS **pvData** API provides a set of classes and utilities that form the core of the EPICS PVA implementation.
pvDataCPP is a set of data types and utilities that form part of the EPICS V4 project.
The pvDataCPP module is a part of the EPICS software toolkit that implements pvData structures as C++ class objects.
## Links
Further Info
------------
- General information about EPICS can be found at the
[EPICS Controls website](https://epics-controls.org).
- API documentation for this module can be found on its
[Github Pages website](https://epics-base.github.io/pvDataCPP/).
Consult the documents in the documentation directory, in particular
* pvDataCPP.html
* RELEASE_NOTES.md
Also see the [EPICS Version 4 website](http://epics-pvdata.sourceforge.net)
Prerequisites
-------------
The pvDataCPP requires recent versions of the following software:
1. EPICS Base (v3.14.12.3 or later)
2. EPICS4 pvCommonCPP (4.1.0 or later)
(pvCommonCPP may not be needed depending on host/compiler.)
Building
--------
Building uses the make utility and the EPICS base build system.
The build system needs the location of the prerequisites, e.g. by placing the
lines of the form
PVCOMMON = /home/install/epicsV4/pvCommonCPP
EPICS_BASE = /home/install/epics/base
pointing to the locations in a file called RELEASE.local
in the configure directory or the parent directory of pvDataCPP.
With this in place, to build type make
make
To perform a clean build type
make clean uninstall
To run the unit tests type
make runtests
For more information on the EPICS build system consult the
[Application Development guide](http://www.aps.anl.gov/epics/base/R3-14/12-docs/AppDevGuide.pdf).
## Building
This module is included as a submodule of a full EPICS 7 release and will be compiled during builds of that software.

View File

@@ -1,12 +0,0 @@
# Version number for the PV Data API and shared library
EPICS_PVD_MAJOR_VERSION = 8
EPICS_PVD_MINOR_VERSION = 0
EPICS_PVD_MAINTENANCE_VERSION = 4
# Development flag, set to zero for release versions
EPICS_PVD_DEVELOPMENT_FLAG = 0
# Immediately after a release the MAINTENANCE_VERSION
# will be incremented and the DEVELOPMENT_FLAG set to 1

View File

@@ -20,14 +20,9 @@ CHECK_RELEASE = YES
# INSTALL_LOCATION here.
#INSTALL_LOCATION=</path/name/to/install/top>
USR_CPPFLAGS += -DPVD_INTERNAL
-include $(TOP)/../CONFIG_SITE.local
-include $(TOP)/configure/CONFIG_SITE.local
# MSVC - skip defining min()/max() macros
USR_CPPFLAGS_WIN32 += -DNOMINMAX
ifdef WITH_COVERAGE
USR_CPPFLAGS += --coverage
USR_LDFLAGS += --coverage

View File

@@ -2,8 +2,6 @@ TOP=..
include $(TOP)/configure/CONFIG
CFG += CONFIG_PVDATA_VERSION
TARGETS = $(CONFIG_TARGETS)
CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS)))

View File

@@ -1,38 +1,25 @@
# RELEASE - Location of external support modules
# pvDataCPP RELEASE - Location of external support modules
#
# IF YOU CHANGE ANY PATHS in this file or make API changes to
# any modules it refers to, you should do a "make rebuild" in
# this application's top level directory.
# IF YOU CHANGE this file or any file it includes you must
# subsequently do a "gnumake rebuild" in the application's
# top level directory.
#
# The EPICS build process does not check dependencies against
# any files from outside the application, so it is safest to
# rebuild it completely if any modules it depends on change.
# The build process does not check dependencies against files
# that are outside this application, thus you should also do a
# "gnumake rebuild" in the top level directory after EPICS_BASE
# or any other external module pointed to below is rebuilt.
#
# Host- or target-specific settings can be given in files named
# RELEASE.$(EPICS_HOST_ARCH).Common
# RELEASE.Common.$(T_A)
# RELEASE.$(EPICS_HOST_ARCH).$(T_A)
# EPICS V4 Developers: Do not edit the locations in this file!
#
# This file is parsed by both GNUmake and an EPICS Perl script,
# so it may ONLY contain definititions of paths to other support
# modules, variable definitions that are used in module paths,
# and include statements that pull in other RELEASE files.
# Variables may be used before their values have been set.
# Build variables that are NOT used in paths should be set in
# the CONFIG_SITE file.
# Create a file RELEASE.local pointing to your PVCOMMON
# and EPICS_BASE build directories, e.g.
# PVCOMMON = /home/install/epicsV4/pvCommonCPP
# EPICS_BASE = /home/install/epics/base
# Variables and paths to dependent modules:
#MODULES = /path/to/modules
#MYMODULE = $(MODULES)/my-module
# If building the EPICS modules individually, set these:
#EPICS_BASE = /path/to/base
# Set RULES here if you want to use build rules from elsewhere:
#RULES = $(MODULES)/build-rules
# These allow developers to override the RELEASE variable settings
# without having to modify the configure/RELEASE file itself.
-include $(TOP)/../RELEASE.local
-include $(TOP)/../RELEASE.$(EPICS_HOST_ARCH).local
-include $(TOP)/configure/RELEASE.local

View File

@@ -1,3 +0,0 @@
*.tag
*.db
html/

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +0,0 @@
all: gen
clean:
rm -rf doxygen_sqlite3.db html
gen: libstdc++.tag
doxygen
commit: gen
touch html/.nojekyll
./commit-gh.sh documentation/html/ html/.nojekyll html/*.* html/search/*.*
libstdc++.tag:
wget -O $@ https://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/libstdc++.tag
.PHONY: all clean gen commit

View File

@@ -0,0 +1,345 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html;" />
<meta name="keywords" content="EPICS, EPICSv4" />
<title>EPICS V4 pvData C++ Release Notes</title>
<link rel="stylesheet" type="text/css" href="../../base.css" />
<link rel="stylesheet" type="text/css" href="../../epicsv4.css" />
</head>
<body>
<h1>Release 6.0.1</h1>
<p>The changes since release 6.0.0 are:</p>
<ul>
<li>Fix "Problem building pvDataCPP for win32-x86-mingw" (issue #42)</li>
<li>In src/misc/bitSet.cpp #include <algorithm> required for MSVS 2015</li>
<li>In testApp/misc/testTypeCast.cpp print (u)int8 values as integers</li>
<li>Minor documentation updates</li>
</ul>
<h1>Release 6.0.0</h1>
<p>The main changes since release 5.0.4 are:</p>
<ul>
<li>Linux shared library version added</li>
<li>Headers have been moved into pv directories</li>
<li>Bitset functions declared const where possible</li>
<li>Bitset::swap added</li>
<li>Requester::message has default implementation</li>
<li>Serialization/deserialization helpers added</li>
<li>Non-template getSubField char* overload added</li>
<li>MonitorPlugin deprecated</li>
<li>Field name validation performed</li>
<li>Now builds for Cygwin and MinGW targets</li>
<li>Fix for debug build issue.</li>
<li>New license file replaces LICENSE and COPYRIGHT</li>
</ul>
<h2>Shared library version added</h2>
<p>Linux shared library version numbers have been added by setting SHRLIB_VERSION
(to 6.0 in this case). So shared object will be libpvData.so.6.0 instead of
libpvData.so.</p>
<h2>Headers have been moved into pv directories</h2>
<p>E.g. src/property/alarm.h -> src/property/pv/alarm.h</p>
<p>This facilitates using some IDEs such as Qt Creator.</p>
<h2>Requester::message has default implementation</h2>
<p>Requester::message is no longer pure virtual. Default implementation sends
string to std::cerr.</p>
<h2>Serialization/deserialization helpers added</h2>
<p>A helper function, serializeToVector, has been added which serializes a
Serializable object into a standard vector of UInt8s.</p>
<p>Similarly a function deserializeFromVector deserializes a standard vector into
a Deserializable object.</p>
<p>A function deserializeFromBuffer deserializes a ByteBuffer into a
Deserializable object.</p>
<h2>Field name validation performed</h2>
<p>On creating a Structure or Union the field names are now validated.</p>
<p>Valid characters for a field name are upper or lowercase letters, numbers and
underscores and intial numbers are invalid, i.e. names must be of the form
[A-Za-z<em>][A-Za-z0-9</em>]*.</p>
<h2>Now builds for Cygwin and MinGW targets</h2>
<p>Includes cross-compiling MinGW on Linux.</p>
<h1>Release 5.0.4</h1>
<p>The changes since release 5.0.3 are:</p>
<ul>
<li>Fixed bitset serialization (issue #24)</li>
<li>Fixed truncation in BitSet::or_and (issue #27)</li>
</ul>
<h2>Fixed bitset serialization (issue #24)</h2>
<p>C++ bitset serialization was not consistent with the C++ deserialization and
Java code in some instances (depending on the endianness of the serializer and
deserializer) when the number of bits was 56-63 modulo 64. C++ serialization
has been fixed.</p>
<p>Fix exposed issue in deserialization on 32-bit platforms which
has also been corrected. </p>
<h2>Fixed truncation in BitSet::or_and (issue #27)</h2>
<p>If n, n1 and n2 words are used to store the values of the bitsets bitset,
bitset1 and bitset2 respectively then max(n, min(n1,n2)) words are needed
to store bitset.or_(bitset1, bitset2).</p>
<p>Previously min(n1,n2) words were used and the result would be truncated in
some instances. This has been fixed.</p>
<h1>Release 5.0.3</h1>
<p>The only change since release 5.0.2 is:</p>
<h2>Fixed buffer overflow in PVUnion::serialize() (issue #20)</h2>
<p>A PVUnion whose stored value was null was serialized without checking
whether the buffer had sufficient capacity. This has been fixed by calling
ensureBuffer().</p>
<h1>Release 5.0.2</h1>
<p>The main changes since release 4.0.3 are:</p>
<ul>
<li>Deprecated getXXXField() methods have been removed from PVStructure</li>
<li>Convert copy methods and equals operators (re)moved</li>
<li>Convert::copyUnion now always copies between subfields.</li>
<li>New method getSubFieldT, like getSubField except it throws an exception</li>
<li>findSubField method removed from PVStructure</li>
<li>New stream operators for Field and PVField are provided</li>
<li>New template versions of Structure::getField</li>
<li>Fixes for static initialisation order issues</li>
<li>CreateRequest prevents a possible SEGFAULT</li>
</ul>
<h2>Deprecated getXXXField methods have been removed from PVStructure</h2>
<p>The following methods have been removed from PVStructure</p>
<ul>
<li>getBooleanField</li>
<li>getByteField, getShortField, getIntField, getLongField</li>
<li>getUByteField, getUShortField, getUIntField, getULongField</li>
<li>getStringField</li>
<li>getStructureField, getUnionField</li>
<li>getScalarArrayField, getStructureArrayField, getUnionArrayField</li>
</ul>
<p>Use template getSubField instead, e.g. use</p>
<pre><code>getSubField&lt; PVInt &gt;(fieldName)
</code></pre>
<p>in place of</p>
<pre><code>getIntField(fieldName)
</code></pre>
<h2>Convert copy methods and equals operators</h2>
<p>Convert copy methods where moved and replaced with methods
on PVField classes, i.e.</p>
<pre><code>PVField::copy(const PVField&amp; from)
</code></pre>
<p>Methods</p>
<pre><code>PVField::copyUnchecked(const PVField&amp; from)
</code></pre>
<p>were added to allow unchecked copies, to gain performance
where checked are not needed (anymore).</p>
<p>In addition:
- isCompatibleXXX methods were removed in favour of Field::operator==.
- equals methods were remove in favour of PVField::operator==.
- operator== methods where moved to pvIntrospect.h and pvData.h</p>
<h2>Convert::copyUnion</h2>
<p>Before this method, depending on types for to and from,
sometimes did a shallow copy, i.e. just made to shared_ptr for to
share the same data as from.
Now it always copies between the subfield of to and from.</p>
<h2>New method getSubFieldT, like getSubField except it throws an exception</h2>
<p>PVStructure has a new template member</p>
<pre><code>getSubFieldT(std::string const &amp;fieldName)
</code></pre>
<p>that is like <b>getSubField</b> except that it throws a runtime_error
instead of returning null.</p>
<h2>findSubField method removed from PVStructure</h2>
<p>This was mainly used in the implementation of getSubField. With a change to
the latter, findSubField was removed.</p>
<h2>New stream operators</h2>
<p>New steam operators are available for Field and PVField.
Before to print a Field (or any extension) or a PVField (or any extension)
it was necessary to have code like:</p>
<pre><code> void print(StructureConstPtr struc, PVStructurePtr pv)
{
if(struc) {
cout &lt;&lt; *struc &lt;&lt; endl;
} else {
cout &lt;&lt; "nullptr\n"
}
if(pv) {
cout &lt;&lt; *.struc &lt;&lt; endl;
} else {
cout &lt;&lt; "nullptr\n"
}
}
</code></pre>
<p>Now it can be done as follows:</p>
<pre><code> void print(StructureConstPtr struc, PVStructurePtr pv)
{
cout &lt;&lt; struc &lt;&lt; endl;
cout &lt;&lt; pv &lt;&lt; endl;
}
</code></pre>
<h2>New template version of Structure::getField</h2>
<p>A new template getField method has been added to Structure</p>
<p>template&lt;typename FT-&gt;
std::tr1::shared_ptr&lt; const FT-&gt; getField(std::string const &amp;fieldName) const </p>
<p>Can be used, for example, as follows:</p>
<pre><code>StructurePtr tsStruc = struc-&gt;getField&lt;Structure&gt;("timeStamp");
</code></pre>
<h2>Fixes for static initialisation order issues</h2>
<p>Certain static builds (in particular Windows builds) of applications using
pvData had issues due to PVStructure::DEFAULT_ID being used before being initialised. This has been fixed.</p>
<h2>CreateRequest change</h2>
<p>createRequest could cause a SEGFAULT if passed a bad argument.
This has been changed so the it returns a null pvStructure
and provides an error.</p>
<h1>Release 4.0.3</h1>
<p>The main changes since release 3.0.2 are:</p>
<ul>
<li>array semantics now enforce Copy On Write.</li>
<li>String no longer defined.</li>
<li>timeStamp and valueAlarm name changes</li>
<li>toString replaced by stream I/O </li>
<li>union is new type.</li>
<li>copy is new.</li>
<li>monitorPlugin is new.</li>
</ul>
<h2>New Semantics for Arrays</h2>
<p>PVScalarArray, PVStructureArray, and PVUnionArray all enforce COW (Copy On Write) Semantics.
In order to limit memory usage the storage for raw data is managed via a new shared<em>vector facility.
This allows multiple instances of array data to use the shared raw data.
COW is implemented via shared</em>vectors of const data, i. e. data that can not be modified.</p>
<h2>String no longer defined</h2>
<p>This is replaced by std::string.</p>
<h2>timeStamp and valueAlarm name changes</h2>
<p>In timeStamp nanoSeconds is changed to nanoseconds.</p>
<p>In valueAlarm hysteresis is changed to hysteresis</p>
<h2>toString replaced by stream I/O</h2>
<p>pvData.h and pvIntrospect no longer defines toString
Instead they have stream support.
pvIntrospect uses method dump and pvData uses dumpValue.
For example:</p>
<pre><code> PVDoublePtr pvValue;
String buffer;
pvValue-&gt;toString(&amp;buffer);
cout &lt;&lt; buffer &lt;&lt; endl;
buffer.clear();
pvValue-&gt;getField()-&gt;toString(&amp;buffer);
cout &lt;&lt; buffer &lt;&lt; evdl;
</code></pre>
<p>is replaced by</p>
<pre><code> PVDoublePtr pvValue;
cout &lt;&lt; *pvValue &lt;&lt; endl
cout &lt;&lt; *pvValue-&gt;getField() &lt;&lt; endl;
</code></pre>
<h2>union is a new basic type.</h2>
<p>There are two new basic types: union_t and unionArray.</p>
<p>A union is like a structure that has a single subfield.
There are two flavors:</p>
<ul>
<li><b>variant union</b> The field can have any type.</li>
<li><b>union</b> The field can any of specified set of types.</li>
</ul>
<p>The field type can be dynamically changed.</p>
<h2>copy </h2>
<p>This consists of createRequest and pvCopy.
createRequest was moved from pvAccess to here.
pvCopy is moved from pvDatabaseCPP and now depends
only on pvData, i.e. it no longer has any knowledge of PVRecord.</p>
<h2>monitorPlugin</h2>
<p>This is for is for use by code that implements pvAccess monitors.
This is prototype and is subject to debate.</p>
<h1>Release 3.0.2</h1>
<p>This was the starting point for RELEASE_NOTES</p>
</body>
</html>

View File

@@ -1,133 +1,15 @@
/**
@page release_notes Release Notes
Release 8.1.0 (Feb 2021)
========================
- Incompatible changes
- Remove ByteBuffer::align()
- Compatible changes
- Deprecate SerializableControl::alignBuffer() and DeserializableControl::alignData()
- shared_vector_convert<>() fix convert of empty, untyped, array
Release 8.0.3 (July 2020)
=========================
- Incompatible changes
- Removed THROW_BASE_EXCEPTION_CAUSE() macro which has long ignored its cause.
Any external users should switch to the functionally identical THROW_BASE_EXCEPTION_CAUSE()
- Various printing of functions now conditionally escape strings
including quote '\"' and similar charactors.
Release 8.0.2 (May 2020)
========================
- Changes to documentation and unittests.
No functional changes to library.
Release 8.0.1 (Nov 2019)
==========================
- Incompatible changes
- Requires Base >= 3.15
- Bug fixes
Release 8.0.0 (July 2019)
=========================
- Deprecations
- ByteBuffer::getArray()
- Removals
- pv/localStaticLock.h
- pv/pvCopy.h (see epics::pvData::PVRequestMapper)
- Additions
- Add {Structure,Union}::getFieldT
Release 7.1.3 (Apr 2019)
========================
- Fix for array serialization error to/from big endian.
https://github.com/epics-base/pvDataCPP/issues/65
Release 7.1.2 (Mar 2019)
========================
- 7.1.1 tag pushed prematurely.
Release 7.1.1 (Mar 2019)
========================
- Fixes
- Init order issue with StandardField::getStandardField()
- Build fix for Visual Studio 2013+
Release 7.1.0 (Nov 2018)
========================
- Deprecations
- BoundedString, BoundedScalarArray, and FixedScalarArray will be removed unless they are fixed.
See https://github.com/epics-base/pvDataCPP/issues/52 for discussion.
- pv/localStaticLock.h
- pv/pvCopy.h (see epics::pvData::PVRequestMapper)
- Removals
- Remove previously deprecated executor.h, queue.h and timerFunction.h
- Remove *HashFunction functors to "hash" Field sub-classes which were never fully implemented.
- Fixes
- Make thread safe getFieldCreate() and getPVDataCreate()
- Workaround for MSVC pickyness that iterators be non-NULL, even when not de-referenced.
- Fix alignment fault during (de)serialization on RTEMS/vxWorks.
- Fix epics::pvData::shared_vector::swap() for void specialization.
- Changes in several epics::pvData::Field sub-classes to return const ref. instead of a copy.
- Additions
- epics::pvData::shared_vector add c++11 move and construct for initializer list.
- Add epics::pvData::AnyScalar::clear()
- Add ctor epics::pvData::AnyScalar(ScalarType, const void*) to allow construction from an untyped buffer.
- Add epics::pvData::Timer::close()
- Allow epics::pvData::castUnsafe() from const char* without first allocating a std::string.
- De-duplication of epics::pvData::Field instances is performed using a global hash table.
Identical definitions will share a single instance. Allows O(0) comparision.
- Add epics::pvData::PVRequestMapper to facilitate (partial) copying between PVStructure instances
modified by a pvRequest.
- Add shorthand notations epics::pvData::FieldBuilder::begin() and epics::pvData::Field::build()
Release 7.0.0 (Dec 2017)
========================
- Removals
- Remove requester.h, monitor.h, and destroyable.h.. Migrated to the pvAccessCPP module.
- Previously deprecated monitorPlugin.h is removed.
- Remove pv/messageQueue.h and epics::pvData::MessageQueue
- Deprecate the following utility classes, to be removed in 8.0.
- epics::pvData::Queue
- epics::pvData::Executor
- epics::pvData::TimeFunction
- Additions
- Add pv/pvdVersion.h which is included by pv/pvIntrospect.h
- Add epics::pvData::createRequest() function. Alternative to epics::pvData::CreateRequest class which throws on error.
- epics::pvData::FieldBuilder allow Structure defintion to be changed/appended
- Add epics::pvData::ValueBuilder like FieldBuilder also sets initial values.
- Can also be constructed using an existing PVStructure to allow "editing".
- Add debugPtr.h wrapper with reference tracking to assist in troubleshooting shared_ptr related ref. loops.
- Add @ref pvjson utilities
- Add reftrack @ref pvd_reftrack
- Add header typemap.h to facilitate boilerplate switch() over ScalarType
- Add epics::auto_ptr typedef in help writing code supporting both c++98 and c++11 w/o copious deprecation warnings.
Release 6.0.1
=============
The changes since release 6.0.0 are:
* Fix "Problem building pvDataCPP for win32-x86-mingw" (issue #42)
* In src/misc/bitSet.cpp #include "algorithm" required for MSVS 2015
* In src/misc/bitSet.cpp #include <algorithm> required for MSVS 2015
* In testApp/misc/testTypeCast.cpp print (u)int8 values as integers
* Minor documentation updates
Release 6.0.0 (Aug. 2016)
=======================
Release 6.0.0
=============
The main changes since release 5.0.4 are:
@@ -167,13 +49,13 @@ string to std::cerr.
Serialization/deserialization helpers added
-------------------------------------------
A helper function, serializeToVector, has been added which serializes a
A helper function, serializeToVector, has been added which serializes a
Serializable object into a standard vector of UInt8s.
Similarly a function deserializeFromVector deserializes a standard vector into
a Deserializable object.
A function deserializeFromBuffer deserializes a ByteBuffer into a
A function deserializeFromBuffer deserializes a ByteBuffer into a
Deserializable object.
Field name validation performed
@@ -208,7 +90,7 @@ deserializer) when the number of bits was 56-63 modulo 64. C++ serialization
has been fixed.
Fix exposed issue in deserialization on 32-bit platforms which
has also been corrected.
has also been corrected.
Fixed truncation in BitSet::or_and (issue #27)
----------------------------------------------
@@ -229,38 +111,38 @@ The only change since release 5.0.2 is:
Fixed buffer overflow in PVUnion::serialize() (issue #20)
---------------------------------------------------------
A PVUnion whose stored value was null was serialized without checking
A PVUnion whose stored value was null was serialized without checking
whether the buffer had sufficient capacity. This has been fixed by calling
ensureBuffer().
Release 5.0.2 (Sep. 2015)
=========================
Release 5.0.2
=============
The main changes since release 4.0 are:
The main changes since release 4.0.3 are:
- Deprecated getXXXField() methods have been removed from PVStructure
- Convert copy methods and equals operators (re)moved
- Convert::copyUnion now always copies between subfields.
- New method getSubFieldT, like getSubField except it throws an exception
- findSubField method removed from PVStructure
- New stream operators for Field and PVField are provided
- New template versions of Structure::getField
- Fixes for static initialisation order issues
- CreateRequest prevents a possible SEGFAULT
* Deprecated getXXXField() methods have been removed from PVStructure
* Convert copy methods and equals operators (re)moved
* Convert::copyUnion now always copies between subfields.
* New method getSubFieldT, like getSubField except it throws an exception
* findSubField method removed from PVStructure
* New stream operators for Field and PVField are provided
* New template versions of Structure::getField
* Fixes for static initialisation order issues
* CreateRequest prevents a possible SEGFAULT
Deprecated getXXXField methods have been removed from PVStructure
-------------------------------------------------------------------
-----------------------------------------------------------------
The following methods have been removed from PVStructure
- getBooleanField
- getByteField, getShortField, getIntField, getLongField
- getUByteField, getUShortField, getUIntField, getULongField
- getStringField
- getStructureField, getUnionField
- getScalarArrayField, getStructureArrayField, getUnionArrayField
* getBooleanField
* getByteField, getShortField, getIntField, getLongField
* getUByteField, getUShortField, getUIntField, getULongField
* getStringField
* getStructureField, getUnionField
* getScalarArrayField, getStructureArrayField, getUnionArrayField
Use template getSubField instead, e.g. use
@@ -308,7 +190,7 @@ PVStructure has a new template member
getSubFieldT(std::string const &fieldName)
that is like `getSubField()` except that it throws a runtime_error
that is like <b>getSubField</b> except that it throws a runtime_error
instead of returning null.
@@ -354,8 +236,8 @@ New template version of Structure::getField
A new template getField method has been added to Structure
template<typename FT >
std::tr1::shared_ptr< const FT > getField(std::string const &fieldName) const
template<typename FT >
std::tr1::shared_ptr< const FT > getField(std::string const &fieldName) const
Can be used, for example, as follows:
@@ -382,13 +264,13 @@ Release 4.0.3
The main changes since release 3.0.2 are:
- array semantics now enforce Copy On Write.
- String no longer defined.
- timeStamp and valueAlarm name changes
- toString replaced by stream I/O
- union is new type.
- copy is new.
- monitorPlugin is new.
* array semantics now enforce Copy On Write.
* String no longer defined.
* timeStamp and valueAlarm name changes
* toString replaced by stream I/O
* union is new type.
* copy is new.
* monitorPlugin is new.
New Semantics for Arrays
--------
@@ -444,8 +326,8 @@ There are two new basic types: union_t and unionArray.
A union is like a structure that has a single subfield.
There are two flavors:
- *variant union* The field can have any type.
- *union* The field can any of specified set of types.
* <b>variant union</b> The field can have any type.
* <b>union</b> The field can any of specified set of types.
The field type can be dynamically changed.
@@ -466,5 +348,3 @@ This is prototype and is subject to debate.
Release 3.0.2
==========
This was the starting point for RELEASE_NOTES
*/

View File

@@ -12,3 +12,9 @@ valueAlarm
normativeTypes.html describes valueAlarm only for a value field that has type
double.
The implementation also supports all the numeric scalar types.
monitorPlugin
-------------
A debate is on-going about what semantics should be.

View File

@@ -1,7 +0,0 @@
.wy-side-nav-search {
background-color: #18334B;
}
.wy-side-nav-search input[type="text"] {
border-color: #18334b;
}

View File

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

View File

@@ -1,78 +0,0 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# http://www.sphinx-doc.org/en/master/config
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = 'EPICS Documentation'
copyright = '2019, EPICS Controls.'
author = 'EPICS'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.intersphinx',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# Intersphinx links to subprojects
intersphinx_mapping = {
'how-tos': ('https://docs.epics-controls.org/projects/how-tos/en/latest', None),
}
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_css_files = [
'css/custom.css',
]
master_doc = 'index'
html_theme_options = {
'logo_only': True,
}
html_logo = "images/EPICS_white_logo_v02.png"
html_extra_path = ['../html']
# -- Run Doxygen ------------------------------------------------------------
import subprocess
subprocess.call('cd ..; mkdir -p html/doxygen; doxygen', shell=True)

View File

@@ -0,0 +1,679 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
<title>EPICS pvDataCPP: copy and monitor</title>
<link rel="stylesheet" type="text/css"
href="http://epics-pvdata.sourceforge.net/base.css" />
<link rel="stylesheet" type="text/css"
href="http://epics-pvdata.sourceforge.net/epicsv4.css" />
<style type="text/css">
/*<![CDATA[*/
.about { margin-left: 3em; margin-right: 3em; font-size: .83em}
table { margin-left: auto; margin-right: auto }
.diagram { text-align: center; margin: 2.5em 0 }
span.opt { color: grey }
span.nterm { font-style:italic }
span.term { font-family:courier }
span.user { font-family:courier }
span.user:before { content:"<" }
span.user:after { content:">" }
.nonnorm { font-style:italic }
p.ed { color: #AA0000 }
span.ed { color: #AA0000 }
p.ed.priv { display: inline; }
span.ed.priv { display: inline; }
/*]]>*/</style>
<!-- Script that generates the Table of Contents -->
<script type="text/javascript"
src="http://epics-pvdata.sourceforge.net/script/tocgen.js">
</script>
</head>
<body>
<div id="toc">
<h2 class="nocount" style="page-break-before: always">Table of Contents</h2>
</div>
<div id="contents" class="contents">
<h2>support for copy and monitor</h2>
<p><b>copy</b> and <b>monitor</b> are not used in this project.
They are intended for use by pvAccess and by pvAccess servers.
They are provided with this project because the code depends only on
pvData itself.
</p>
<p>This document describes C++ specific code.
<a href="http://epics-pvdata.sourceforge.net/informative/pvRequest.html">
pvRequest.html</a>
provides a language independent overview of <b>copy</b> and <b>monitor</b>.
</p>
<p>
<b>NOTE:pvRequest.html</b> must be updated since it is based on an earlier version of pvCopy that
had knowledge of PVRecord. The C++ version was implemented in pvDatabaseCPP
and the Java version on pvIOCJava.
At present only the C++ version of the new API for pvCopy is implemented.
</p>
<p>Copy provides:
<dl>
<dt>createRequest</dt>
<dd>
The Channel create methods in pvAccess all have an argument
<b>PVStructure pvRequest</b>.<br />
Given an ascii string createRequest creates a PVStructure that provides
a pvData representation of the information from the ascii string.
It is this structure that can be passed to the channel create methods.<br />
The information in a pvRequest selects an arbitrary subset of the
fields in a top level structure that resides in the server.
In addition options can be specified. Both global and field specific
options can be specified.
</dd>
<dt>pvCopy</dt>
<dd>This is a facility used by channel providers.
It provides client specific code that manages a copy of an arbitrary
subset of the fields in a top level structure that resides in the
provider. It also allows provider access to options specified
by the client.
</dd>
</dl>
Monitor provides:
<dl>
<dt>monitor</dt>
<dd>This is support code for channel providers that implement channel
monitor. It, together with the queue facility, provides support for
monitor queues.
</dd>
<dt>monitorPlugin</dt>
<dd>This is support for implementing monitor plugins.
A monitor plugin can be developed that has no knowledge
of pvAccess but only pvData.
</dd>
</dl>
</p>
<h2>support for copy</h2>
<p><b>copy</b> provides the ability to create a structure that has
a copy of an arbitrary subset of the fields in an existing top level
structure. In addition it allows global options and field specific options.
It has two main components: <b>createRequest</b> and <b>pvCopy</b>.
Given a string createRequest creates a pvRequest, which is a PVStructure
that has the format expected by <b>pvCopy</b>.
</p>
<h3>createRequest</h3>
<p>This is mainly used by pvAccess clients. Given a request string it creates
a pvRequest structure that can be passed to the pvAccess create methods.
In turn pvAccess passes the pvRequest to a local channel provider which
then passes it to pvCopy.
</p>
<p>The definition of the public members is:</p>
<pre>
class CreateRequest {
...
static CreateRequestPtr create();
virtual PVStructurePtr createRequest(std::string const &amp;request);
std::string getMessage();
};
</pre>
<p>An example of how it is used is:</p>
<pre>
CreateRequestPtr createRequest = CreateRequest::create();
PVStructurePtr pvRequest = createRequest-&gt;createRequest(request);
if(pvRequest==NULL) {
std::string error = createRequest-&gt;getMessage();
// take some action
} else {
//success do something
}
</pre>
<h3>pvCopy</h3>
<p>The definition of the public members is:</p>
<pre>
class epicsShareClass PVCopyTraverseMasterCallback
{
...
virtual void nextMasterPVField(PVFieldPtr const &amp;pvField);
};
class class epicsShareClass PVCopy
{
...
static PVCopyPtr create(
PVStructurePtr const &amp;pvMaster,
PVStructurePtr const &amp;pvRequest,
std::string const &amp; structureName);
PVStructurePtr getPVMaster();
void traverseMaster(PVCopyTraverseMasterCallbackPtr const &amp; callback);
StructureConstPtr getStructure();
PVStructurePtr createPVStructure();
size_t getCopyOffset(PVFieldPtr const &amp;masterPVField);
size_t getCopyOffset(
PVStructurePtr const &amp;masterPVStructure,
PVFieldPtr const &amp;masterPVField);
PVFieldPtr getMasterPVField(std::size_t structureOffset);
void initCopy(
PVStructurePtr const &amp;copyPVStructure,
BitSetPtr const &amp;bitSet);
void updateCopySetBitSet(
PVStructurePtr const &amp;copyPVStructure,
BitSetPtr const &amp;bitSet);
void updateCopyFromBitSet(
PVStructurePtr const &amp;copyPVStructure,
BitSetPtr const &amp;bitSet);
void updateMaster(
PVStructurePtr const &amp;copyPVStructure,
BitSetPtr const &amp;bitSet);
PVStructurePtr getOptions(std::size_t fieldOffset);
...
};
</pre>
where
<dl>
<dt>PVCopyTraverseMasterCallback::nextMasterPVField</dt>
<dd>
<b>PVCopyTraverseMasterCallback</b> is a callback which must
be implemented by the code that uses pvCopy, normally
the channel provider. It has the single method <b>nextMasterPVField</b>
<br />
<b>nextMasterPVField</b> is called for each field in the master
as a result of a call to <b>traverseMaster</b>.
</dd>
<dt>create</dt>
<dd>
This is the method for creating a PVCopy instance.<br/>
<dl>
<dt>pvMaster</dt>
<dd>the top level structure managed by the server.</dd>
<dt>pvRequest</dt>
<dd>selects the set of subfields desired
and options for each field.</dd>
<dt>structureName</dt>
<dd>the name for the top level of any PVStructure created.
</dd>
</dl>
</dd>
<dt>getPVMaster</dt>
<dd>
Gets the top level structure from pvMaster.
</dd>
<dt>traverseMaster</dt>
<dd>
Traverse all fields of the top level structure of pvMaster.
For each field the callback is called.
</dd>
<dt>getStructure</dt>
<dd>
Get the introspection interface for a PVStructure for e copy.
</dd>
<dt>createPVStructure</dt>
<dd>Create a copy instance.
Monitors keep a queue of monitor elements.
Since each element needs a PVStructure, multiple top level structures
will be created.
</dd>
<dt>getCopyOffset</dt>
<dd>Given a field in pvMaster.
return the offset in copy for the same field.
A value of std::string::npos means that the copy does not have this field.
Two overloaded methods are provided. The first is called if
the field of master is not a structure. The second is for
subfields of a structure.
</dd>
<dt>getMasterPVField</dt>
<dd>
Given a offset in the copy get the corresponding field in pvMaster.
</dd>
<dt>initCopy</dt>
<dd>
Initialize the fields in copyPVStructure
by giving each field the value from the corresponding field in pvMaster.
bitSet will be set to show that all fields are changed.
This means that bit set will have the value <b>{0}</b>.
</dd>
<dt>updateCopySetBitSet</dt>
<dd>
Set all fields in copyPVStructure to the value of the corresponding field
in pvMaster. Each field that is changed has it's corresponding
bit set in bitSet.
</dd>
<dt>updateCopyFromBitSet</dt>
<dd>
For each set bit in bitSet set the field in copyPVStructure to the value
of the corresponding field in pvMaster.
</dd>
<dt>updateMaster</dt>
<dd>
For each set bit in bitSet set the field in pvMaster to the value
of the corresponding field in copyPVStructure.
</dd>
<dt>getOptions</dt>
<dd>
Get the options for the field at the specified offset.
A NULL is returned if no options were specified for the field.
If options were specified,PVStructurePtr is
a structure with a set of PVString subfields that specify name,value
pairs. name is the subField name and value is the subField value.
</dd>
</dl>
<h2>support for monitor</h2>
<p>This consists of two components:
<dl>
<dt>monitor</dt>
<dd>Used by code that implements pvAccess monitors.</dd>
<dt>monitorPlugin</dt>
<dd>Code that provides special semantics for monitors.</dd>
</dl>
</p>
<h3>monitor</h3>
<pre>
class MonitorElement {
MonitorElement(PVStructurePtr const &amp; pvStructurePtr);
PVStructurePtr pvStructurePtr;
BitSetPtr changedBitSet;
BitSetPtr overrunBitSet;
};
class Monitor {
virtual Status start() = 0;
virtual Status stop() = 0;
virtual MonitorElementPtr poll() = 0;
virtual void release(MonitorElementPtr const &amp; monitorElement) = 0;
};
class MonitorRequester : public virtual Requester {
virtual void monitorConnect(Status const &amp; status,
MonitorPtr const &amp; monitor, StructureConstPtr const &amp; structure) = 0;
virtual void monitorEvent(MonitorPtr const &amp; monitor) = 0;
virtual void unlisten(MonitorPtr const &amp; monitor) = 0;
};
</pre>
<h4>monitorElement</h4>
<p><b>MonitorElement</b> holds the data for one element of a monitor queue.
It has the fields:
<dl>
<dt>pvStructurePtr</dt>
<dd>A top level structure with data values at the time the monitors occurs.</dd>
<dt>changedBitSet</dt>
<dd>Shows which fields have changed since the previous monitor.</dd>
<dt>overrunBitSet</dt>
<dd>Shows which fields have changed more han once since the previous monitor.</dd>
</dl>
</p>
<h4>monitorElement queue</h4>
<p>
A queue of monitor elements must be implemented by any channel provider that implements
<b>Channel::createMonitor</b>.
For an example implementation look at pvDatabaseCPP.
It has the following:
<pre>
typedef Queue&lt;MonitorElement&gt; MonitorElementQueue;
typedef std::tr1::shared_ptr&lt;MonitorElementQueue&gt; MonitorElementQueuePtr;
class MultipleElementQueue :
public ElementQueue
{
public:
POINTER_DEFINITIONS(MultipleElementQueue);
virtual ~MultipleElementQueue(){}
MultipleElementQueue(
MonitorLocalPtr const &amp;monitorLocal,
MonitorElementQueuePtr const &amp;queue,
size_t nfields);
virtual void destroy(){}
virtual Status start();
virtual Status stop();
virtual bool dataChanged();
virtual MonitorElementPtr poll();
virtual void release(MonitorElementPtr const &amp;monitorElement);
...
};
</pre>
<h4>Monitor</h4>
<p><b>Monitor</b> must be implemented by any channel provider that implements
<b>Channel::createMonitor</b>.
Remote PVAccess also implements Monitor on the client side.
Note that each client has it's own queue that is not shared with other client.
</p>
<p>Monitor has the following methods:</p>
<dl>
<dt>start</dt>
<dd>
Start monitoring.
This will result in a an initial monitor that has the current value
of all fields.
</dd>
<dt>stop</dt>
<dd>
Stop monitoring.
</dd>
<dt>poll</dt>
<dd>
Called to get a monitor element.
If no new elements are available then a null pointer is returned.
</dd>
<dt>release</dt>
<dd>
Release the monitor element.
The caller owns the monitor element between the calls to poll and release.
</dd>
<dl>
</dl>
<h4>MonitorRequester</h4>
<p>This must be implemented by a pvAccess client.
It has the methods:</p>
<dl>
<dt>monitorConnect</dt>
<dd>
A monitor has either connected of disconnected.
</dd>
<dt>monitorEvent</dt>
<dd>
A new monitor element is available.
</dd>
<dt>unlisten</dt>
<dd>
The channel is going away. The client cam no longer access the monitor.
</dd>
</dl>
<h3>monitorPlugin</h3>
<pre>
class MonitorPlugin
{
virtual std::string const &amp; getName() = 0;
virtual bool causeMonitor(
PVFieldPtr const &amp;pvField,
PVStructurePtr const &amp;pvTop,
MonitorElementPtr const &amp;monitorElement) = 0;
virtual void monitorDone(
MonitorElementPtr const &amp;monitorElement);
virtual void startMonitoring();
virtual void stopMonitoring();
virtual void beginGroupPut();
virtual void endGroupPut();
};
class MonitorPluginCreator
{
virtual MonitorPluginPtr create(
FieldConstPtr const &amp;field,
StructureConstPtr const &amp;top,
PVStructurePtr const &amp;pvFieldOptions) = 0;
virtual std::string const &amp; getName() = 0;
}
class MonitorPluginManager
{
static MonitorPluginManagerPtr get();
bool addPlugin(
std::string const &amp;pluginName,
MonitorPluginCreatorPtr const &amp;creator);
MonitorPluginCreatorPtr findPlugin(std::string const &amp;pluginName);
void showNames();
};
</pre>
<h4>MonitorPlugin</h4>
<p><b>MonitorPlugin</b> must be implemented by the plugin implementation.
It has methods:</p>
<dl>
<dt>getName</dt>
<dd>Get the name of the plugin.</dd>
<dt>causeMonitor</dt>
<dd>
Should the value of pvField cause a monitor to be raised.
pvField and pvTop are fields in the top level structure
being monitored. monitorElement has the top level structure
for the copy</b>.
The implementation should <b>not</b> modify the fields in the structure
being monitored.
Called with pvTop locked.
</dd>
<dt>monitorDone</dt>
<dd>
Called just before monitorElement will be given to client.
The plugin can change the data values and bitSets in monitorElement.
Called with pvTop unlocked.
</dd>
<dt>startMonitoring</dt>
<dd>
Monitoring is starting.
</dd>
<dt>stopMonitoring</dt>
<dd>
Monitoring is being stopped.
</dd>
<dt>beginGroupPut</dt>
<dd>
A set of puts is starting.
Called with pvTop locked.
</dd>
<dt>endGroupPut</dt>
<dd>
The set of puts is complete.
Called with pvTop locked.
</dd>
</dl>
<h4>MonitorPluginCreator</h4>
<p><b>MonitorPluginCreator</b> must also be implemented by the plugin implementation.
It is called for each field instance that has options of the from
<b>[plugin=name...]</b> where <b>name</b> is the name of the plugin.
Note that a plugin instance will belong to a single client.
It has methods:</p>
<dl>
<dt>getName</dt>
<dd>Get the name of the plugin.</dd>
<dt>create</dt>
<dd>
Create a new plugin instance.
If the arguments are not compatible with the plugin a NULL shared pointer is
returned.<br/>
pvFieldOptions is
a structure with a set of PVString subfields that specify <b>name,value</b>
pairs. name is the subField name and value is the subField value.<br/>
Note that a plugin will below to a single client.
</dd>
<dl>
<h4>MonitorPluginManager</h4>
<p><b>MonitorPluginManager</b> has the methods:</p>
<dl>
<dt>get</dt>
<dd>
MonitorPluginManager is a singleton.
The first call to get will create the single instance.
Further calls will return the single instance.
</dd>
<dt>addPlugin</dt>
<dd>
Add a new plugin.
</dd>
<dt>findPlugin</dt>
<dd>
Find a plugin. A NULL shared pointer is returned if it has not been added.
</dd>
<dt>showNames</dt>
<dd>
Show the names of all plugins that have been added.
</dd>
</dl>
<p><b>NOTE:</b>
Should the method <b>causeMonitor</b>
have arguments <b>pvField</b> and <b>pvTop</b>
be defined so that they can not be modified.
This would be possible if the following was defined:
<pre>
typedef std::tr1::shared_ptr&lt;const PVField&gt; PVFieldConstPtr;
typedef std::tr1::shared_ptr&lt;const PVStructure&gt; PVStructureConstPtr;
</pre>
then the definition for causeMonitor could be:
<pre>
virtual bool causeMonitor(
PVFieldConstPtr const &amp;pvField,
PVStructureConstPtr const &amp;pvTop,
MonitorElementPtr const &amp;monitorElement) = 0;
</pre>
But just adding these definitions is not sufficient.
In addition all methods defined in pvDataCPP must be checked.
In particular many of the methods in <b>Convert</b> must have
their arguments modified.
Big job.
</p>
<h2>monitorPlugin example</h2>
<h3>Example Plugin Overview</h3>
<p>This section describes an example plugin that:</p>
<ul>
<li>Only raises monitors when a field changes value.<br />
If no plugin is provided
the default is to raise a monitor when a put is issued to a field.</li>
<li>Optionally a change will not raise a monitor.<br />
The change will, however,
appear if a put to another field raise a monitor.</li>
</ul>
<p>As an example assume that a channel provided by pvAccess has a top level structure
that represents a power supply.</p>
<pre>
structure powerSupply
structure alarm
structure timeStamp
structure power
double value
structure alarm
structure display
structure voltage
double value
structure alarm
structure display
structure current
double value
structure alarm
structure display
</pre>
<p>A pvAccess client wants to create a monitor on the powerSupply as follows:
The client wants a top level structure that looks like:
<pre>
structure powerSupply
structure alarm
structure timeStamp
structure power
double value
structure voltage
double value
structure current
double value
</pre>
In addition the client wants monitors to occur only when one of the monitored
fields changes value but not just because a put occured.
Also if only the timeStamp changes value then that should not cause a monitor.
</p>
<p>The example monitor plugin implements the semantics the
client wants. It can be attached to any field via the following options:
<pre>
[plugin=onChange,raiseMonitor=value]
</pre>
This plugin will trigger a monitor for the field only if the field changes
value. In addition <b>value</b> equals <b>false</b> means do not raise a monitor
for changes to this field.
But if a change to another field does cause a monitor the change to this field
will be passed to the client.
</p>
<p>
Assume that the client has already connected to the channel.
The client can then issue the commands:</p>
<pre>
std::string request("field(alarm[plugin=onChange]");
request += ",timeStamp[plugin=onChange,raiseMonitor=false]";
request += ",power.value[plugin=onChange";
request += ",voltage.value[plugin=onChange";
request += ",current.value[plugin=onChange";
PVStructurePtr pvRequest = createRequest-&gt;createRequest(request);
MonitorPtr monitor = channel-&gt;createMonitor(monitorRequester,pvRequest);
</pre>
<h3>Example Plugin Code</h3>
<p>The header file to create the example has the definition:</p>
<pre>
class ExampleMonitorPlugin{
public:
static void create();
};
</pre>
<p>The implementation is:</p>
<pre>
class OnChangePlugin : public MonitorPlugin
{
public:
virtual ~OnChangePlugin(){}
OnChangePlugin() {}
bool init(
FieldConstPtr const &amp;field,
StructureConstPtr const &amp;top,
PVStructurePtr const &amp;pvFieldOptions)
{
pvField = getPVDataCreate()-&gt;createPVField(field);
raiseMonitor = true;
if(pvFieldOptions!=NULL) {
PVStringPtr pvString =
pvFieldOptions-&gt;getSubField&lt;PVString&gt;("raiseMonitor");
if(pvString!=NULL) {
std::string value = pvString-&gt;get();
if(value.compare("false")==0) raiseMonitor = false;
}
}
return true;
}
virtual std::string &amp;getName(){return pluginName;}
virtual bool causeMonitor(
PVFieldPtr const &amp;pvNew,
PVStructurePtr const &amp;pvTop,
MonitorElementPtr const &amp;monitorElement)
{
bool isSame = convert-&gt;equals(pvNew,pvField);
if(isSame) return false;
convert-&gt;copy(pvNew,pvField);
return raiseMonitor;
}
private:
PVFieldPtr pvField;
bool raiseMonitor;
};
class OnChangePluginCreator : public MonitorPluginCreator
{
public:
virtual std::string &amp;getName(){return pluginName;}
virtual MonitorPluginPtr create(
FieldConstPtr const &amp;field,
StructureConstPtr const &amp;top,
PVStructurePtr const &amp;pvFieldOptions)
{
OnChangePluginPtr plugin(new OnChangePlugin());
bool result = plugin-&gt;init(field,top,pvFieldOptions);
if(!result) return MonitorPluginPtr();
return plugin;
}
};
void ExampleMonitorPlugin::create()
{
static OnChangePluginCreatorPtr plugin;
static Mutex mutex;
Lock xx(mutex);
if(plugin==NULL) {
plugin = OnChangePluginCreatorPtr(new OnChangePluginCreator());
MonitorPluginManager::get()-&gt;addPlugin(pluginName,plugin);
}
}
</pre>
</div>
</body>
</html>

BIN
documentation/examples.zip Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -1,16 +0,0 @@
pvData (C++) Library
====================
.. toctree::
:hidden:
EPICS Website <https://epics-controls.org>
EPICS Documentation Home <https://docs.epics-controls.org>
.. toctree::
:maxdepth: 1
:caption: pvDataCPP
Reference Manual and API Documentation <https://docs.epics-controls.org/projects/pvdata-cpp/en/latest/doxygen>
Source Code Repository on GitHub <https://github.com/epics-base/pvDataCPP>

View File

@@ -1,58 +0,0 @@
#ifndef MAINPAGE_H
#define MAINPAGE_H
/**
@mainpage pvDataCPP documentation
- This module is included in [EPICS Base releases](https://epics-controls.org/resources-and-support/base/) beginning with 7.0.1
- It may also be [Downloaded](https://github.com/epics-base/pvDataCPP/releases) and built separately.
- @htmlonly <a href="modules.html">API components</a> @endhtmlonly
- @ref release_notes
The epics::pvData namespace.
See pv/pvData.h header.
@code
#include <pv/pvData.h>
#include <pv/createRequest.h>
@endcode
- Type description epics::pvData::Field and sub-classes
- Value container epics::pvData::PVField and sub-classes
- POD array handling epics::pvData::shared_vector
- pvRequest parsing epics::pvData::createRequest()
Define a structure type and create a container with default values.
@code
namespace pvd = epics::pvData;
pvd::StructureConstPtr stype(pvd::FieldBuilder::begin()
->add("fld1", pvd::pvInt)
->addNestedStructure("sub")
->add("fld2", pvd::pvString)
->endNested()
->createStructure());
pvd::PVStructuretPtr value(stype->build());
value->getSubFieldT<pvd::PVInt>("fld1")->put(4); // store integer 4. would throw if not pvInt
value->getSubFieldT<pvd::PVScalar>("sub.fld2")->putFrom(4.2); // convert and store string "4.2"
@endcode
is equivalent to the following pseudo-code.
@code
struct stype {
pvd::int32 fld1;
struct {
std::string fld2;
} sub;
};
stype value;
value.fld1 = 4;
value.fld2 = pvd::castUnsafe<std::string>(4.2);
@endcode
*/
#endif /* MAINPAGE_H */

5466
documentation/pvDataCPP.html Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,179 @@
pvDataCPP cookbook
------------------
Creating introspection data interfaces
// create a scalar
getFieldCreate()->createScalar(pvDouble);
// create a scalar array
getFieldCreate()->createScalarArray(pvDouble);
// create a structure
getFieldCreate()->createFieldBuilder()->
setId("enum_t")->
add("index", pvDouble)->
addArray("choices", pvString)->
createStructure();
// create a structure (cntd.)
StructureConstPtr enum_t =
getFieldCreate()->createFieldBuilder()->
setId("enum_t")->
add("index", pvInt)->
addArray("choices", pvString)->
createStructure();
// create a structure (cntd.)
StructureConstPtr ntEnum =
getFieldCreate()->createFieldBuilder()->
setId("epics:nt/NTEnum:1.0")->
add("value", enum_t)->
addNestedStructure("timeStamp")->
setId("time_t")->
add("secondsPastEpoch", pvLong)->
add("nanoseconds", pvInt)->
add("userTag", pvInt)->
endNested()->
createStructure();
// create an union == same as structure
---
Creating data containers
// create a scalar
PVDouble::shared_pointer doubleValue = getPVDataCreate()->createPVScalar<PVDouble>();
// create a scalar array
PVDoubleArray::shared_pointer doubleArrayValue = getPVDataCreate()->createPVScalarArray<PVDouble>();
// create a structure
PVStructure::shared_pointer struct = getPVDataCreate()->createPVStructure(ntEnum);
// create an union
PVUnion::shared_pointer pvUnion = getPVDataCreate()->createPVUnion(unionIF);
// create a structure array
PVStructureArray::shared_pointer structArray = getPVDataCreate()->createPVStructureArray(ntEnum);
// scalar usage
PVInt::shared_pointer index = struct->getSubField<PVInt>("value.index");
int32 ix = index->get();
index->put(3);
std::cout << *index << std::endl;
// using <<=, >>= operators to get/set
*doubleValue <<= 12.3;
double val;
*doubleValue >>= val;
// array usage
PVStringArray::shared_pointer choices = struct->getSubField<PVStringArray>("value.choices");
// use view() to access read-only data
PVStringArray::const_svector data(choices->view());
for (std::size_t i = 0; i < data.size(); i++)
std::cout << data[i] << std::endl;
// use replace() to put new data
PVStringArray::svector newdata;
newdata.push_back("zero");
newdata.push_back("one");
newdata.push_back("two");
choices->replace(freeze(newdata));
// (add more use-cases) here
// print entire array
std::cout << *choices << std::endl;
// print elmenet at index == 1
std::cout << format::array_at(1) << *choices << std::endl;
----
Union handling
Union::const_shared_pointer punion =
getFieldCreate()->createFieldBuilder()->
add("doubleValue", pvDouble)->
add("intValue", pvInt)->
createUnion();
PVUnion::shared_pointer u = getPVDataCreate()->createPVUnion(punion);
// select and put
// this create a new instance of PVDouble (everytime select() is called)
PVDouble::shared_pointer doubleValue = u->select<PVDouble>("doubleValue");
doubeValue->put(12);
// select using index (and direct put)
u->select<PVDouble>(0)->put(12);
// select using existing PVField (PVUnion stores by-reference)
u->set("doubleValue", doubleValue);
// get selected field name or index
std::string selectedFN = u->getSelectedFieldName();
int32 selectedIndex = u->getSelectedIndex();
// get currently selected (knowing it's PVDouble)
PVDouble value = u->get<PVDouble>();
Variant Union handling
PVUnion::shared_pointer any = getPVDataCreate()->createPVVariantUnion();
PVDouble::shared_pointer doubleValue = getPVDataCreate()->createPVScalar<PVDouble>();
doubleValue->put(12.8);
any->set(doubleValue);
PVDouble::shared_pointer doubleValue2 = any->get<PVDouble>();
// variant union work by-reference (pointers match also)
// doubleValue.get() == doubleValue2.get()
------
Convert
// convert to int
int32 i = doubleValue->getAs<int32>();
// from int
doubleValue->putFrom<int32>(i);
// from string
doubleValue->putFrom<std::string>("12.3");
// from scalar field
doubleValue->assign(pvScalar);
// convert to int array
PVIntArray::const_svector intData;
doubleArrayValue->getAs<int32>(intData);
// from string array
PVStringArray::svector labels;
labels.push_back("zero");
labels.push_back("one");
labels.push_back("two");
doubleArrayValue->putFrom<std::string>(labels);
// from scalar array
doubleArrayValue->assign(pvScalarArray);

View File

@@ -1,14 +0,0 @@
# Makefile for the examples
# make sure they compile
TOP = ..
include $(TOP)/configure/CONFIG
PROD_LIBS += pvData Com
TESTPROD_HOST += unittest
unittest_SRCS += unittest.cpp
include $(TOP)/configure/RULES

View File

@@ -1,30 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/* c++ unittest skeleton */
#include <stdexcept>
#include <testMain.h>
#include <pv/pvUnitTest.h>
#include <pv/epicsException.h>
namespace {
void testCase1() {
testEqual(1+1, 2);
}
} // namespace
MAIN(testUnitTest)
{
testPlan(1);
try {
testCase1();
}catch(std::exception& e){
PRINT_EXCEPTION(e); // print stack trace if thrown with THROW_EXCEPTION()
testAbort("Unhandled exception: %s", e.what());
}
return testDone();
}

View File

@@ -21,15 +21,23 @@ installE4 () {
local module=$1
local branch=$2
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE}/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
# If microbench version does not exist, try without
if [ "${MB}" = "WITH_MICROBENCH" ]; then
if ! wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE},MB=WITH_MICROBENCH/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz; then
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE},MB=NO_MICROBENCH/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
fi
else
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE},MB=NO_MICROBENCH/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
fi
tar -xzf ${module}.CB-dist.tar.gz
}
###########################################
# Defaults for EPICS Base
# Defaults for EPICS Base and MB
DEFAULT_BASE=3.15.4
BASE=${BASE:-${DEFAULT_BASE}}
MB=${MB:-"NO_MICROBENCH"}
###########################################
# Fetch and unpack dependencies
@@ -65,6 +73,6 @@ make distclean all
make runtests
###########################################
# Create cache
# Create distribution
tar czf pvData.CB-dist.tar.gz lib include cfg LICENSE
tar czf pvData.CB-dist.tar.gz lib include LICENSE

View File

@@ -21,7 +21,14 @@ installE4 () {
local module=$1
local branch=$2
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE}/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
# If microbench version does not exist, try without
if [ "${MB}" = "WITH_MICROBENCH" ]; then
if ! wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE},MB=WITH_MICROBENCH/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz; then
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE},MB=NO_MICROBENCH/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
fi
else
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE},MB=NO_MICROBENCH/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
fi
tar -xzf ${module}.CB-dist.tar.gz
}
@@ -31,6 +38,7 @@ installE4 () {
BASE=3.15.4
PUBLISH=${PUBLISH:-NO}
BRANCH=${BRANCH:-master}
MB=NO_MICROBENCH
###########################################
# Fetch and unpack dependencies

View File

@@ -11,26 +11,14 @@ include $(PVDATA_SRC)/factory/Makefile
include $(PVDATA_SRC)/property/Makefile
include $(PVDATA_SRC)/copy/Makefile
include $(PVDATA_SRC)/pvMisc/Makefile
include $(PVDATA_SRC)/json/Makefile
include $(PVDATA_SRC)/monitor/Makefile
LIBRARY = pvData
pvData_LIBS += Com
EXPANDVARS += EPICS_PVD_MAJOR_VERSION
EXPANDVARS += EPICS_PVD_MINOR_VERSION
EXPANDVARS += EPICS_PVD_MAINTENANCE_VERSION
EXPANDVARS += EPICS_PVD_DEVELOPMENT_FLAG
EXPANDFLAGS += $(foreach var,$(EXPANDVARS),-D$(var)="$(strip $($(var)))")
# shared library ABI version.
SHRLIB_VERSION = $(EPICS_PVD_MAJOR_VERSION).$(EPICS_PVD_MINOR_VERSION).$(EPICS_PVD_MAINTENANCE_VERSION)
SHRLIB_VERSION ?= 6.0
include $(TOP)/configure/RULES
# Can't use EXPAND as generated headers must appear
# in O.Common, but EXPAND emits rules for O.$(T_A)
../O.Common/pv/pvdVersionNum.h: ../pv/pvdVersionNum.h@
$(MKDIR) $(COMMON_DIR)/pv
$(EXPAND_TOOL) $(EXPANDFLAGS) $($@_EXPANDFLAGS) $< $@

View File

@@ -3,6 +3,7 @@
SRC_DIRS += $(PVDATA_SRC)/copy
INC += pv/createRequest.h
INC += pv/pvCopy.h
LIBSRCS += createRequest.cpp
LIBSRCS += requestmapper.cpp
LIBSRCS += pvCopy.cpp

View File

@@ -22,15 +22,15 @@ using std::endl;
using std::string;
using std::vector;
namespace {
using namespace epics::pvData;
namespace epics { namespace pvData {
static PVDataCreatePtr pvDataCreate = getPVDataCreate();
static FieldCreatePtr fieldCreate = getFieldCreate();
struct CreateRequestImpl {
class CreateRequestImpl : public CreateRequest {
private:
struct Node
struct Node
{
string name;
vector<Node> nodes;
@@ -54,8 +54,13 @@ struct CreateRequestImpl {
string fullFieldName;
CreateRequestImpl() {}
public:
CreateRequestImpl()
{
fullFieldName = "";
}
private:
void removeBlanks(string& str)
{
@@ -70,7 +75,8 @@ struct CreateRequestImpl {
size_t openBrace = request.find('{', index+1);
size_t closeBrace = request.find('}', index+1);
if(openBrace == string::npos && closeBrace == string::npos){
throw std::runtime_error(request + " mismatched {}");
message = request + " mismatched {}";
throw std::logic_error("message");
}
if (openBrace != string::npos && openBrace!=0) {
if(openBrace<closeBrace) return findMatchingBrace(request,openBrace,numOpen+1);
@@ -85,12 +91,14 @@ struct CreateRequestImpl {
for(size_t i=index+1; i< request.size(); ++i) {
if(request[i] == ']') {
if(i==index+1) {
throw std::runtime_error(request + " mismatched []");
message = request + " mismatched []";
throw std::logic_error("message");
}
return i;
}
}
throw std::runtime_error(request + " missing ]");
message = request + " missing ]";
throw std::logic_error("message");
}
size_t findEndField(string& request) {
@@ -129,7 +137,7 @@ struct CreateRequestImpl {
if(pos==string::npos) break;
numValues++;
index = pos +1;
}
}
vector<string> valueList(numValues,"");
index=0;
for(size_t i=0; i<numValues; i++) {
@@ -145,7 +153,7 @@ struct CreateRequestImpl {
string const & request)
{
if(request.length()<=1) {
throw std::runtime_error("logic error empty options");
throw std::logic_error("logic error empty options");
}
vector<Node> top;
vector<string> items = split(request);
@@ -155,7 +163,8 @@ struct CreateRequestImpl {
string item = items[j];
size_t equals = item.find('=');
if(equals==string::npos || equals==0) {
throw std::runtime_error(item + " illegal option " + request);
message = item + " illegal option " + request;
throw std::logic_error("message");
}
top.push_back(Node(item.substr(0,equals)));
string name = fullFieldName + "._options." + item.substr(0,equals);
@@ -204,7 +213,7 @@ struct CreateRequestImpl {
if(end==0) end = request.size();
string name = request.substr(0,end);
if(name.size()<1) {
throw std::runtime_error("null field name " + request);
throw std::logic_error("null field name " + request);
}
string saveFullName = fullFieldName;
fullFieldName += "." + name;
@@ -227,7 +236,7 @@ struct CreateRequestImpl {
if(chr=='.') {
request = request.substr(end+1);
if(request.size()==string::npos || request.size()<1) {
throw std::runtime_error("null field name " + request);
throw std::logic_error("null field name " + request);
}
Node subNode(name);
if(optionNode.name.size()>0) subNode.nodes.push_back(optionNode);
@@ -248,11 +257,11 @@ struct CreateRequestImpl {
if(chr=='{') {
size_t endBrace = findEndField(request);
if((end+1)>=(endBrace-1)) {
throw std::runtime_error("illegal syntax " + request);
throw std::logic_error("illegal syntax " + request);
}
string subRequest = request.substr(end+1,endBrace-1 -end -1);
if(subRequest.size()<1) {
throw std::runtime_error("empty {} " + request);
throw std::logic_error("empty {} " + request);
}
Node subNode(name);
if(optionNode.name.size()>0) subNode.nodes.push_back(optionNode);
@@ -268,7 +277,7 @@ struct CreateRequestImpl {
createSubNode(node,request);
return;
}
throw std::runtime_error("logic error");
throw std::logic_error("logic error");
}
FieldConstPtr createSubStructure(vector<Node> & nodes)
@@ -309,16 +318,17 @@ struct CreateRequestImpl {
return structure;
}
public:
PVStructurePtr createRequest(
virtual PVStructurePtr createRequest(
string const & crequest)
{
{
try {
string request = crequest;
if (!request.empty()) removeBlanks(request);
if (request.empty())
{
return fieldCreate->createStructure()->build();
return pvDataCreate->createPVStructure(fieldCreate->createStructure());
}
size_t offsetRecord = request.find("record[");
size_t offsetField = request.find("field(");
@@ -347,17 +357,20 @@ struct CreateRequestImpl {
if(numParan!=0) {
ostringstream oss;
oss << "mismatched () " << numParan;
throw std::runtime_error(oss.str());
message = oss.str();
return PVStructurePtr();
}
if(numBrace!=0) {
ostringstream oss;
oss << "mismatched {} " << numBrace;
throw std::runtime_error(oss.str());
message = oss.str();
return PVStructurePtr();
}
if(numBracket!=0) {
ostringstream oss;
oss << "mismatched [] " << numBracket;
throw std::runtime_error(oss.str());
message = oss.str();
return PVStructurePtr();
}
vector<Node> top;
try {
@@ -366,8 +379,9 @@ struct CreateRequestImpl {
size_t openBracket = request.find('[', offsetRecord);
size_t closeBracket = request.find(']', openBracket);
if(closeBracket==string::npos) {
throw std::runtime_error(request.substr(offsetRecord) +
"record[ does not have matching ]");
message = request.substr(offsetRecord) +
"record[ does not have matching ]";
return PVStructurePtr();
}
if(closeBracket-openBracket > 3) {
Node node("record");
@@ -383,8 +397,9 @@ struct CreateRequestImpl {
size_t openParan = request.find('(', offsetField);
size_t closeParan = request.find(')', openParan);
if(closeParan==string::npos) {
throw std::runtime_error(request.substr(offsetField)
+ " field( does not have matching )");
message = request.substr(offsetField)
+ " field( does not have matching )";
return PVStructurePtr();
}
if(closeParan>openParan+1) {
createSubNode(node,request.substr(openParan+1,closeParan-openParan-1));
@@ -397,8 +412,9 @@ struct CreateRequestImpl {
size_t openParan = request.find('(', offsetGetField);
size_t closeParan = request.find(')', openParan);
if(closeParan==string::npos) {
throw std::runtime_error(request.substr(offsetField)
+ " getField( does not have matching )");
message = request.substr(offsetField)
+ " getField( does not have matching )";
return PVStructurePtr();
}
if(closeParan>openParan+1) {
createSubNode(node,request.substr(openParan+1,closeParan-openParan-1));
@@ -411,8 +427,9 @@ struct CreateRequestImpl {
size_t openParan = request.find('(', offsetPutField);
size_t closeParan = request.find(')', openParan);
if(closeParan==string::npos) {
throw std::runtime_error(request.substr(offsetField)
+ " putField( does not have matching )");
message = request.substr(offsetField)
+ " putField( does not have matching )";
return PVStructurePtr();
}
if(closeParan>openParan+1) {
createSubNode(node,request.substr(openParan+1,closeParan-openParan-1));
@@ -420,7 +437,9 @@ struct CreateRequestImpl {
top.push_back(node);
}
} catch (std::exception &e) {
throw std::runtime_error(std::string("while creating Structure exception ")+e.what());
string xxx = e.what();
message = "while creating Structure exception " + xxx;
return PVStructurePtr();
}
size_t num = top.size();
StringArray names(num);
@@ -437,7 +456,7 @@ struct CreateRequestImpl {
}
StructureConstPtr structure = fieldCreate->createStructure(names, fields);
if(!structure) throw std::invalid_argument("bad request " + crequest);
PVStructurePtr pvStructure = structure->build();
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(structure);
for(size_t i=0; i<optionList.size(); ++i) {
OptionPair pair = optionList[i];
string name = pair.name;
@@ -448,38 +467,20 @@ struct CreateRequestImpl {
}
optionList.clear();
return pvStructure;
} catch (std::exception &e) {
message = e.what();
return PVStructurePtr();
}
}
};
} // namespace
namespace epics {namespace pvData {
CreateRequest::shared_pointer CreateRequest::create()
{
CreateRequest::shared_pointer createRequest(new CreateRequest());
CreateRequest::shared_pointer createRequest(new CreateRequestImpl());
return createRequest;
}
PVStructure::shared_pointer CreateRequest::createRequest(std::string const & request)
{
message.clear();
try {
return ::createRequest(request);
} catch(std::exception& e) {
message = e.what();
return PVStructure::shared_pointer();
}
}
}}
PVStructure::shared_pointer createRequest(std::string const & request)
{
CreateRequestImpl I;
return I.createRequest(request);
}
}} // namespace

View File

@@ -8,18 +8,14 @@
#define CREATEREQUEST_H
#include <string>
#include <sstream>
#include <vector>
#include <pv/pvData.h>
#include <pv/lock.h>
#include <pv/bitSet.h>
#include <shareLib.h>
namespace epics { namespace pvData {
class BitSet;
/**
* @brief Create pvRequest structure for Channel methods.
*
@@ -36,7 +32,7 @@ class epicsShareClass CreateRequest {
* @returns A shared pointer to the new instance.
*/
static CreateRequest::shared_pointer create();
~CreateRequest() {};
virtual ~CreateRequest() {};
/**
* Create a request structure for the create calls in Channel.
* See the package overview documentation for details.
@@ -45,7 +41,7 @@ class epicsShareClass CreateRequest {
* If a NULL PVStructure is returned then getMessage will return
* the reason.
*/
PVStructure::shared_pointer createRequest(std::string const & request);
virtual PVStructure::shared_pointer createRequest(std::string const & request) = 0;
/**
* Get the error message of createRequest returns NULL
* return the error message
@@ -56,176 +52,6 @@ protected:
std::string message;
};
/** Parse and build pvRequest structure.
*
@params request the Request string to be parsed. eg. "field(value)"
@returns The resulting strucuture. Never NULL
@throws std::exception for various parsing errors
*/
epicsShareExtern
PVStructure::shared_pointer createRequest(std::string const & request);
/** Helper for implementations of epics::pvAccess::ChannelProvider in interpreting the
* 'field' substructure of a pvRequest.
* Copies between an internal (base) Structure, and a client/user visible (requested) Structure.
*
* @note PVRequestMapper is not re-entrant. It is copyable and swap()able.
*/
class epicsShareClass PVRequestMapper {
public:
enum mode_t {
/** Masking mode.
*
* Requested Structure is identical to Base.
* The 'field' substructure of the provided pvRequest is used to construct a BitSet
* where the bits corresponding to the "selected" fields are set. This mask can be
* access via. requestedMask(). The copy* and mask* methods operate only
* on "selected" fields.
*/
Mask,
/** Slice mode
*
* The Requested Structure is a strict sub-set of the Base Structure containing
* those fields "selected" by the 'field' substructure of the provided pvRequest.
*/
Slice,
};
PVRequestMapper();
//! @see compute()
PVRequestMapper(const PVStructure& base,
const PVStructure& pvRequest,
mode_t mode = Mask);
//! return to state of default ctor
void reset();
//! @returns the Structure of the PVStructure previously passed to compute(). NULL if never computed()'d
inline const StructureConstPtr& base() const { return typeBase; }
//! @returns the Structure which is the selected sub-set of the base Structure. NULL if never computed()'d
inline const StructureConstPtr& requested() const { return typeRequested; }
/** A mask of all fields in the base structure which are also in the requested structure,
* and any parent/structure "compress" bits. eg. bit 0 is always set.
*
@code
PVRequestMapper mapper(...);
...
BitSet changed = ...; // a base changed mask
bool wouldcopy = changed.logical_and(mapper.requestedMask());
// wouldcopy==false means that copyBaseToRequested(..., changed, ...) would be a no-op
@endcode
*
* eg. allows early detection of empty monitor updates.
*/
inline const BitSet& requestedMask() const { return maskRequested; }
//! @returns A new instance of the requested() Structure
PVStructurePtr buildRequested() const;
//! @returns A new instance of the base() Structure
PVStructurePtr buildBase() const;
/** (re)compute the selected subset of provided base structure.
* @param base A full base structure.
* Must be "top level" (field offset zero).
* @param pvRequest The user/client provided request modifier
* @param mode Control how the mapping is constructed. @see mode_t for a description of mapping modes.
*
* @post Updates warnings()
* @throws std::runtime_error For errors involving invalid pvRequest
* @throws std::logic_error if the provided base is not a "top level" PVStructure.
*/
void compute(const PVStructure& base,
const PVStructure& pvRequest,
mode_t mode = Mask);
//! After compute(), check if !warnings().empty()
inline const std::string& warnings() const { return messages; }
/** Copy field values from Base structure into Requested structure
*
* @param base An instance of the base Structure. Field values are copied from it.
* Need not be the same instance passed to compute().
* @param baseMask A bit mask selecting those base fields to copy.
* @param request An instance of the requested() Structure. Field values are copied to it.
* @param requestMask A bit mask indicating which requested fields were copied.
* BitSet::clear() is not called.
*/
void copyBaseToRequested(
const PVStructure& base,
const BitSet& baseMask,
PVStructure& request,
BitSet& requestMask
) const;
/** Copy field values into Base structure from Requested structure
*
* @param base An instance of the base Structure. Field values are copied into it.
* Need not be the same instance passed to compute().
* @param baseMask A bit mask indicating which base fields were copied.
* BitSet::clear() is not called.
* @param request An instance of the requested() Structure. Field values are copied from it.
* @param requestMask A bit mask selecting those requested fields to copy.
*/
void copyBaseFromRequested(
PVStructure& base,
BitSet& baseMask,
const PVStructure& request,
const BitSet& requestMask
) const;
//! Translate Base bit mask into requested bit mask.
//! BitSet::clear() is not called.
inline void maskBaseToRequested(
const BitSet& baseMask,
BitSet& requestMask
) const
{ _mapMask(baseMask, requestMask, false); }
//! Translate requested bit mask into base bit mask.
//! BitSet::clear() is not called.
inline void maskBaseFromRequested(
BitSet& baseMask,
const BitSet& requestMask
) const
{ _mapMask(requestMask, baseMask, true); }
//! Exchange contents of two mappers. O(0) and never throws.
void swap(PVRequestMapper& other);
private:
bool _compute(const PVStructure& base, const PVStructure& pvReq,
FieldBuilderPtr& builder, bool keepids, unsigned depth);
void _map(const PVStructure& src,
const BitSet& maskSrc,
PVStructure& dest,
BitSet& maskDest,
bool dir_r2b) const;
void _mapMask(const BitSet& maskSrc,
BitSet& maskDest,
bool dir_r2b) const;
StructureConstPtr typeBase, typeRequested;
BitSet maskRequested;
// Map between field offsets of base and requested Structures.
// Include all fields, both leaf and sub-structure.
struct Mapping {
size_t to; // offset in destination Structure
BitSet tomask, // if !leaf these are the other bits in the destination mask to changed
frommask; // if !leaf these are the other bits in the source mask to be copied
bool valid; // only true in (sparse) base -> requested mapping
bool leaf; // not a (sub)Structure?
Mapping() :valid(false) {}
Mapping(size_t to, bool leaf) :to(to), valid(true), leaf(leaf) {}
};
typedef std::vector<Mapping> mapping_t;
mapping_t base2req, req2base;
std::string messages;
mutable BitSet scratch; // avoid temporary allocs. (we aren't re-entrant!)
};
}}

232
src/copy/pv/pvCopy.h Normal file
View File

@@ -0,0 +1,232 @@
/* pvCopy.h */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author Marty Kraimer
* @date 2013.04
*/
#ifndef PVCOPY_H
#define PVCOPY_H
#include <string>
#include <stdexcept>
#include <memory>
#include <shareLib.h>
#include <pv/pvData.h>
#include <pv/bitSet.h>
namespace epics { namespace pvData{
class PVCopyTraverseMasterCallback;
typedef std::tr1::shared_ptr<PVCopyTraverseMasterCallback> PVCopyTraverseMasterCallbackPtr;
/**
* @brief Callback for traversing master structure
*
* Must be implemented by code that creates pvCopy.
*/
class epicsShareClass PVCopyTraverseMasterCallback
{
public:
POINTER_DEFINITIONS(PVCopyTraverseMasterCallback);
virtual ~PVCopyTraverseMasterCallback() {}
/**
* Called once for each field in master.
* @param pvField The field in master.
*/
virtual void nextMasterPVField(epics::pvData::PVFieldPtr const &pvField) = 0;
};
class PVCopy;
typedef std::tr1::shared_ptr<PVCopy> PVCopyPtr;
struct CopyNode;
typedef std::tr1::shared_ptr<CopyNode> CopyNodePtr;
struct CopyMasterNode;
typedef std::tr1::shared_ptr<CopyMasterNode> CopyMasterNodePtr;
struct CopyStructureNode;
typedef std::tr1::shared_ptr<CopyStructureNode> CopyStructureNodePtr;
/**
* @brief Support for subset of fields in a pvStructure.
*
* Class that manages one or more PVStructures that holds an arbitrary subset of the fields
* in another PVStructure called master.
*/
class epicsShareClass PVCopy :
public std::tr1::enable_shared_from_this<PVCopy>
{
public:
POINTER_DEFINITIONS(PVCopy);
/**
* Create a new pvCopy
* @param pvMaster The top-level structure for which a copy of
* an arbitrary subset of the fields in master will be created and managed.
* @param pvRequest Selects the set of subfields desired and options for each field.
* @param structureName The name for the top level of any PVStructure created.
*/
static PVCopyPtr create(
PVStructurePtr const &pvMaster,
PVStructurePtr const &pvRequest,
std::string const & structureName);
virtual ~PVCopy(){}
virtual void destroy();
/**
* Get the top-level structure of master
* @returns The master top-level structure.
* This should not be modified.
*/
PVStructurePtr getPVMaster();
/**
* Traverse all the fields in master.
* @param callback This is called for each field on master.
*/
void traverseMaster(PVCopyTraverseMasterCallbackPtr const & callback)
{
traverseMaster(headNode,callback);
}
/**
* Get the introspection interface for a PVStructure for e copy.
*/
StructureConstPtr getStructure();
/**
* Create a copy instance. Monitors keep a queue of monitor elements.
* Since each element needs a PVStructure, multiple top-level structures will be created.
*/
PVStructurePtr createPVStructure();
/**
* Given a field in pvMaster. return the offset in copy for the same field.
* A value of std::string::npos means that the copy does not have this field.
* @param masterPVField The field in master.
*/
std::size_t getCopyOffset(PVFieldPtr const &masterPVField);
/**
* Given a field in pvMaster. return the offset in copy for the same field.
* A value of std::string::npos means that the copy does not have this field.
* @param masterPVStructure A structure in master that has masterPVField.
* @param masterPVField The field in master.
*/
std::size_t getCopyOffset(
PVStructurePtr const &masterPVStructure,
PVFieldPtr const &masterPVField);
/**
* Given an offset in the copy get the corresponding field in pvMaster.
* @param structureOffset The offset in the copy.
*/
PVFieldPtr getMasterPVField(std::size_t structureOffset);
/**
* Initialize the fields in copyPVStructure by giving each field
* the value from the corresponding field in pvMaster.
* bitSet will be set to show that all fields are changed.
* @param copyPVStructure A copy top-level structure.
* @param bitSet A bitSet for copyPVStructure.
*/
void initCopy(
PVStructurePtr const &copyPVStructure,
BitSetPtr const &bitSet);
/**
* Set all fields in copyPVStructure to the value of the corresponding field in pvMaster.
* Each field that is changed has it's corresponding bit set in bitSet.
* @param copyPVStructure A copy top-level structure.
* @param bitSet A bitSet for copyPVStructure.
*/
void updateCopySetBitSet(
PVStructurePtr const &copyPVStructure,
BitSetPtr const &bitSet);
/**
* For each set bit in bitSet
* set the field in copyPVStructure to the value of the corresponding field in pvMaster.
* @param copyPVStructure A copy top-level structure.
* @param bitSet A bitSet for copyPVStructure.
*/
void updateCopyFromBitSet(
PVStructurePtr const &copyPVStructure,
BitSetPtr const &bitSet);
/**
* For each set bit in bitSet
* set the field in pvMaster to the value of the corresponding field in copyPVStructure
* @param copyPVStructure A copy top-level structure.
* @param bitSet A bitSet for copyPVStructure.
*/
void updateMaster(
PVStructurePtr const &copyPVStructure,
BitSetPtr const &bitSet);
/**
* Get the options for the field at the specified offset.
* @param fieldOffset the offset in copy.
* @returns A NULL is returned if no options were specified for the field.
* If options were specified,PVStructurePtr is a structures
* with a set of PVString subfields that specify name,value pairs.s
* name is the subField name and value is the subField value.
*/
PVStructurePtr getOptions(std::size_t fieldOffset);
/**
* For debugging.
*/
std::string dump();
private:
void dump(
std::string *builder,
CopyNodePtr const &node,
int indentLevel);
PVCopyPtr getPtrSelf()
{
return shared_from_this();
}
void traverseMaster(CopyNodePtr const &node, PVCopyTraverseMasterCallbackPtr const & callback);
PVStructurePtr pvMaster;
StructureConstPtr structure;
CopyNodePtr headNode;
PVStructurePtr cacheInitStructure;
PVCopy(PVStructurePtr const &pvMaster);
friend class PVCopyMonitor;
bool init(PVStructurePtr const &pvRequest);
std::string dump(
std::string const &value,
CopyNodePtr const &node,
int indentLevel);
StructureConstPtr createStructure(
PVStructurePtr const &pvMaster,
PVStructurePtr const &pvFromRequest);
CopyNodePtr createStructureNodes(
PVStructurePtr const &pvMasterStructure,
PVStructurePtr const &pvFromRequest,
PVStructurePtr const &pvFromField);
void updateStructureNodeSetBitSet(
PVStructurePtr const &pvCopy,
CopyStructureNodePtr const &structureNode,
BitSetPtr const &bitSet);
void updateSubFieldSetBitSet(
PVFieldPtr const &pvCopy,
PVFieldPtr const &pvMaster,
BitSetPtr const &bitSet);
void updateStructureNodeFromBitSet(
PVStructurePtr const &pvCopy,
CopyStructureNodePtr const &structureNode,
BitSetPtr const &bitSet,
bool toCopy,
bool doAll);
void updateSubFieldFromBitSet(
PVFieldPtr const &pvCopy,
PVFieldPtr const &pvMasterField,
BitSetPtr const &bitSet,
bool toCopy,
bool doAll);
CopyMasterNodePtr getCopyOffset(
CopyStructureNodePtr const &structureNode,
PVFieldPtr const &masterPVField);
CopyMasterNodePtr getMasterNode(
CopyStructureNodePtr const &structureNode,
std::size_t structureOffset);
};
}}
#endif /* PVCOPY_H */

656
src/copy/pvCopy.cpp Normal file
View File

@@ -0,0 +1,656 @@
/* pvCopy.cpp */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author Marty Kraimer
* @date 2013.04
*/
#include <string>
#include <stdexcept>
#include <memory>
#include <sstream>
#include <epicsThread.h>
#define epicsExportSharedSymbols
#include <pv/thread.h>
#include <pv/pvCopy.h>
using std::tr1::static_pointer_cast;
using std::tr1::dynamic_pointer_cast;
using std::string;
using std::size_t;
using std::cout;
using std::endl;
namespace epics { namespace pvData {
/**
* Convenience method for implementing dump.
* It generates a newline and inserts blanks at the beginning of the newline.
* @param builder The std::string * being constructed.
* @param indentLevel Indent level, Each level is four spaces.
*/
static void newLine(string *buffer, int indentLevel)
{
*buffer += "\n";
*buffer += string(indentLevel*4, ' ');
}
static PVCopyPtr NULLPVCopy;
static FieldConstPtr NULLField;
static StructureConstPtr NULLStructure;
static PVStructurePtr NULLPVStructure;
static CopyNodePtr NULLCopyNode;
static CopyMasterNodePtr NULLCopyMasterNode;
struct CopyNode {
CopyNode()
: isStructure(false),
structureOffset(0),
nfields(0)
{}
bool isStructure;
size_t structureOffset; // In the copy
size_t nfields;
PVStructurePtr options;
};
struct CopyMasterNode : public CopyNode{
PVFieldPtr masterPVField;
};
typedef std::vector<CopyNodePtr> CopyNodePtrArray;
typedef std::tr1::shared_ptr<CopyNodePtrArray> CopyNodePtrArrayPtr;
struct CopyStructureNode : public CopyNode {
CopyNodePtrArrayPtr nodes;
};
PVCopyPtr PVCopy::create(
PVStructurePtr const &pvMaster,
PVStructurePtr const &pvRequest,
string const & structureName)
{
PVStructurePtr pvStructure(pvRequest);
if(structureName.size()>0) {
if(pvRequest->getStructure()->getNumberFields()>0) {
pvStructure = pvRequest->getSubField<PVStructure>(structureName);
if(!pvStructure) return NULLPVCopy;
}
} else if(pvStructure->getSubField<PVStructure>("field")) {
pvStructure = pvRequest->getSubField<PVStructure>("field");
}
PVCopyPtr pvCopy = PVCopyPtr(new PVCopy(pvMaster));
bool result = pvCopy->init(pvStructure);
if(!result) pvCopy.reset();
return pvCopy;
}
PVCopy::PVCopy(
PVStructurePtr const &pvMaster)
: pvMaster(pvMaster)
{
}
void PVCopy::destroy()
{
headNode.reset();
}
PVStructurePtr PVCopy::getPVMaster()
{
return pvMaster;
}
void PVCopy::traverseMaster(CopyNodePtr const &innode, PVCopyTraverseMasterCallbackPtr const & callback)
{
CopyNodePtr node = innode;
if(!node->isStructure) {
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(node);
callback->nextMasterPVField(masterNode->masterPVField);
return;
}
CopyStructureNodePtr structNode = static_pointer_cast<CopyStructureNode>(node);
CopyNodePtrArrayPtr nodes = structNode->nodes;
for(size_t i=0; i< nodes->size(); i++) {
node = (*nodes)[i];
traverseMaster(node,callback);
}
}
StructureConstPtr PVCopy::getStructure()
{
return structure;
}
PVStructurePtr PVCopy::createPVStructure()
{
if(cacheInitStructure) {
PVStructurePtr save = cacheInitStructure;
cacheInitStructure.reset();
return save;
}
PVStructurePtr pvStructure =
getPVDataCreate()->createPVStructure(structure);
return pvStructure;
}
PVStructurePtr PVCopy::getOptions(std::size_t fieldOffset)
{
if(fieldOffset==0) return headNode->options;
CopyNodePtr node = headNode;
while(true) {
if(!node->isStructure) {
if(node->structureOffset==fieldOffset) return node->options;
return NULLPVStructure;
}
CopyStructureNodePtr structNode = static_pointer_cast<CopyStructureNode>(node);
CopyNodePtrArrayPtr nodes = structNode->nodes;
boolean okToContinue = false;
for(size_t i=0; i< nodes->size(); i++) {
node = (*nodes)[i];
size_t soff = node->structureOffset;
if(fieldOffset>=soff && fieldOffset<soff+node->nfields) {
if(fieldOffset==soff) return node->options;
if(!node->isStructure) {
return NULLPVStructure;
}
okToContinue = true;
break;
}
}
if(okToContinue) continue;
throw std::invalid_argument("fieldOffset not valid");
}
}
size_t PVCopy::getCopyOffset(PVFieldPtr const &masterPVField)
{
if(masterPVField->getFieldOffset()==0) return 0;
if(!headNode->isStructure) {
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(headNode);
if((masterNode->masterPVField.get())==masterPVField.get()) {
return headNode->structureOffset;
}
PVStructure * parent = masterPVField->getParent();
size_t offsetParent = parent->getFieldOffset();
size_t off = masterPVField->getFieldOffset();
size_t offdiff = off -offsetParent;
if(offdiff<masterNode->nfields) return headNode->structureOffset + offdiff;
return string::npos;
}
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
CopyMasterNodePtr masterNode = getCopyOffset(node,masterPVField);
if(masterNode) return masterNode->structureOffset;
return string::npos;
}
size_t PVCopy::getCopyOffset(
PVStructurePtr const &masterPVStructure,
PVFieldPtr const &masterPVField)
{
CopyMasterNodePtr masterNode;
if(!headNode->isStructure) {
masterNode = static_pointer_cast<CopyMasterNode>(headNode);
if(masterNode->masterPVField.get()!=masterPVStructure.get()) return string::npos;
} else {
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
masterNode = getCopyOffset(node,masterPVField);
}
if(!masterNode) return string::npos;
size_t diff = masterPVField->getFieldOffset()
- masterPVStructure->getFieldOffset();
return masterNode->structureOffset + diff;
}
PVFieldPtr PVCopy::getMasterPVField(size_t structureOffset)
{
CopyMasterNodePtr masterNode;
if(!headNode->isStructure) {
masterNode = static_pointer_cast<CopyMasterNode>(headNode);
} else {
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
masterNode = getMasterNode(node,structureOffset);
}
if(!masterNode) {
throw std::invalid_argument(
"PVCopy::getMasterPVField: setstructureOffset not valid");
}
size_t diff = structureOffset - masterNode->structureOffset;
PVFieldPtr pvMasterField = masterNode->masterPVField;
if(diff==0) return pvMasterField;
PVStructurePtr pvStructure
= static_pointer_cast<PVStructure>(pvMasterField);
return pvStructure->getSubField(
pvMasterField->getFieldOffset() + diff);
}
void PVCopy::initCopy(
PVStructurePtr const &copyPVStructure,
BitSetPtr const &bitSet)
{
bitSet->clear();
bitSet->set(0);
updateCopyFromBitSet(copyPVStructure,bitSet);
}
void PVCopy::updateCopySetBitSet(
PVStructurePtr const &copyPVStructure,
BitSetPtr const &bitSet)
{
if(headNode->isStructure) {
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
updateStructureNodeSetBitSet(copyPVStructure,node,bitSet);
} else {
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(headNode);
PVFieldPtr pvMasterField= masterNode->masterPVField;
PVFieldPtr copyPVField = copyPVStructure;
PVFieldPtr pvField = pvMasterField;
if(pvField->getField()->getType()==epics::pvData::structure) {
updateSubFieldSetBitSet(copyPVField,pvMasterField,bitSet);
return;
}
bool isEqual = (*copyPVField == *pvField);
if(!isEqual) {
copyPVField->copyUnchecked(*pvField);
bitSet->set(copyPVField->getFieldOffset());
}
}
}
void PVCopy::updateCopyFromBitSet(
PVStructurePtr const &copyPVStructure,
BitSetPtr const &bitSet)
{
bool doAll = bitSet->get(0);
if(headNode->isStructure) {
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
updateStructureNodeFromBitSet(copyPVStructure,node,bitSet,true,doAll);
} else {
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(headNode);
updateSubFieldFromBitSet(copyPVStructure, masterNode->masterPVField,bitSet, true,doAll);
}
}
void PVCopy::updateMaster(
PVStructurePtr const &copyPVStructure,
BitSetPtr const &bitSet)
{
bool doAll = bitSet->get(0);
if(headNode->isStructure) {
CopyStructureNodePtr node =
static_pointer_cast<CopyStructureNode>(headNode);
updateStructureNodeFromBitSet(
copyPVStructure,node,bitSet,false,doAll);
} else {
CopyMasterNodePtr masterNode =
static_pointer_cast<CopyMasterNode>(headNode);
updateSubFieldFromBitSet( copyPVStructure,masterNode->masterPVField,bitSet,false,doAll);
}
}
string PVCopy::dump()
{
string builder;
dump(&builder,headNode,0);
return builder;
}
void PVCopy::dump(string *builder,CopyNodePtr const &node,int indentLevel)
{
newLine(builder,indentLevel);
std::stringstream ss;
ss << (node->isStructure ? "structureNode" : "masterNode");
ss << " structureOffset " << node->structureOffset;
ss << " nfields " << node->nfields;
*builder += ss.str();
PVStructurePtr options = node->options;
if(options) {
newLine(builder,indentLevel +1);
// TODO !!! ugly
std::ostringstream oss;
oss << *options;
*builder += oss.str();
newLine(builder,indentLevel);
}
if(!node->isStructure) {
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(node);
string name = masterNode->masterPVField->getFullName();
*builder += " masterField " + name;
return;
}
CopyStructureNodePtr structureNode =
static_pointer_cast<CopyStructureNode>(node);
CopyNodePtrArrayPtr nodes = structureNode->nodes;
for(size_t i=0; i<nodes->size(); ++i) {
if((*nodes)[i].get()==NULL) {
newLine(builder,indentLevel +1);
ss.str("");
ss << "node[" << i << "] is null";
*builder += ss.str();
continue;
}
dump(builder,(*nodes)[i],indentLevel+1);
}
}
bool PVCopy::init(epics::pvData::PVStructurePtr const &pvRequest)
{
PVStructurePtr pvMasterStructure = pvMaster;
size_t len = pvRequest->getPVFields().size();
bool entireMaster = false;
if(len==string::npos) entireMaster = true;
if(len==0) entireMaster = true;
PVStructurePtr pvOptions;
if(len==1) {
pvOptions = pvRequest->getSubField<PVStructure>("_options");
}
if(entireMaster) {
structure = pvMasterStructure->getStructure();
CopyMasterNodePtr masterNode(new CopyMasterNode());
headNode = masterNode;
masterNode->options = pvOptions;
masterNode->isStructure = false;
masterNode->structureOffset = 0;
masterNode->masterPVField = pvMasterStructure;
masterNode->nfields = pvMasterStructure->getNumberFields();
return true;
}
structure = createStructure(pvMasterStructure,pvRequest);
if(!structure) return false;
cacheInitStructure = createPVStructure();
headNode = createStructureNodes(
pvMaster,
pvRequest,
cacheInitStructure);
return true;
}
string PVCopy::dump(
string const &value,
CopyNodePtr const &node,
int indentLevel)
{
throw std::logic_error(string("Not Implemented"));
}
StructureConstPtr PVCopy::createStructure(
PVStructurePtr const &pvMaster,
PVStructurePtr const &pvFromRequest)
{
if(pvFromRequest->getStructure()->getNumberFields()==0) {
return pvMaster->getStructure();
}
PVFieldPtrArray const &pvFromRequestFields = pvFromRequest->getPVFields();
StringArray const &fromRequestFieldNames = pvFromRequest->getStructure()->getFieldNames();
size_t length = pvFromRequestFields.size();
if(length==0) return NULLStructure;
FieldConstPtrArray fields; fields.reserve(length);
StringArray fieldNames; fields.reserve(length);
for(size_t i=0; i<length; ++i) {
string const &fieldName = fromRequestFieldNames[i];
PVFieldPtr pvMasterField = pvMaster->getSubField(fieldName);
if(!pvMasterField) continue;
FieldConstPtr field = pvMasterField->getField();
if(field->getType()==epics::pvData::structure) {
PVStructurePtr pvRequestStructure = static_pointer_cast<PVStructure>(
pvFromRequestFields[i]);
if(pvRequestStructure->getNumberFields()>0) {
StringArray const &names = pvRequestStructure->getStructure()->
getFieldNames();
size_t num = names.size();
if(num>0 && names[0].compare("_options")==0) --num;
if(num>0) {
if(pvMasterField->getField()->getType()!=epics::pvData::structure) continue;
fieldNames.push_back(fieldName);
fields.push_back(createStructure(
static_pointer_cast<PVStructure>(pvMasterField),
pvRequestStructure));
continue;
}
}
}
fieldNames.push_back(fieldName);
fields.push_back(field);
}
size_t numsubfields = fields.size();
if(numsubfields==0) return NULLStructure;
return getFieldCreate()->createStructure(fieldNames, fields);
}
CopyNodePtr PVCopy::createStructureNodes(
PVStructurePtr const &pvMasterStructure,
PVStructurePtr const &pvFromRequest,
PVStructurePtr const &pvFromCopy)
{
PVFieldPtrArray const & copyPVFields = pvFromCopy->getPVFields();
PVStructurePtr pvOptions;
PVFieldPtr pvField = pvFromRequest->getSubField("_options");
if(pvField) pvOptions = static_pointer_cast<PVStructure>(pvField);
size_t number = copyPVFields.size();
CopyNodePtrArrayPtr nodes(new CopyNodePtrArray());
nodes->reserve(number);
for(size_t i=0; i<number; i++) {
PVFieldPtr copyPVField = copyPVFields[i];
string fieldName = copyPVField->getFieldName();
PVStructurePtr requestPVStructure = pvFromRequest->getSubField<PVStructure>(fieldName);
PVStructurePtr pvSubFieldOptions = requestPVStructure->getSubField<PVStructure>("_options");
PVFieldPtr pvMasterField;
PVFieldPtrArray const & pvMasterFields = pvMasterStructure->getPVFields();
for(size_t j=0; i<pvMasterFields.size(); j++ ) {
if(pvMasterFields[j]->getFieldName().compare(fieldName)==0) {
pvMasterField = pvMasterFields[j];
break;
}
}
size_t numberRequest = requestPVStructure->getPVFields().size();
if(pvSubFieldOptions) numberRequest--;
if(numberRequest>0) {
nodes->push_back(createStructureNodes(
static_pointer_cast<PVStructure>(pvMasterField),
requestPVStructure,
static_pointer_cast<PVStructure>(copyPVField)));
continue;
}
CopyMasterNodePtr masterNode(new CopyMasterNode());
masterNode->options = pvSubFieldOptions;
masterNode->isStructure = false;
masterNode->masterPVField = pvMasterField;
masterNode->nfields = copyPVField->getNumberFields();
masterNode->structureOffset = copyPVField->getFieldOffset();
nodes->push_back(masterNode);
}
CopyStructureNodePtr structureNode(new CopyStructureNode());
structureNode->isStructure = true;
structureNode->nodes = nodes;
structureNode->structureOffset = pvFromCopy->getFieldOffset();
structureNode->nfields = pvFromCopy->getNumberFields();
structureNode->options = pvOptions;
return structureNode;
}
void PVCopy::updateStructureNodeSetBitSet(
PVStructurePtr const &pvCopy,
CopyStructureNodePtr const &structureNode,
epics::pvData::BitSetPtr const &bitSet)
{
for(size_t i=0; i<structureNode->nodes->size(); i++) {
CopyNodePtr node = (*structureNode->nodes)[i];
PVFieldPtr pvField = pvCopy->getSubField(node->structureOffset);
if(node->isStructure) {
PVStructurePtr xxx = static_pointer_cast<PVStructure>(pvField);
CopyStructureNodePtr yyy =
static_pointer_cast<CopyStructureNode>(node);
updateStructureNodeSetBitSet(xxx,yyy,bitSet);
} else {
CopyMasterNodePtr masterNode =
static_pointer_cast<CopyMasterNode>(node);
updateSubFieldSetBitSet(pvField,masterNode->masterPVField,bitSet);
}
}
}
void PVCopy::updateSubFieldSetBitSet(
PVFieldPtr const &pvCopy,
PVFieldPtr const &pvMaster,
BitSetPtr const &bitSet)
{
FieldConstPtr field = pvCopy->getField();
Type type = field->getType();
if(type!=epics::pvData::structure) {
bool isEqual = (*pvCopy == *pvMaster);
if(isEqual) {
if(type==structureArray) {
// always act as though a change occurred.
// Note that array elements are shared.
bitSet->set(pvCopy->getFieldOffset());
}
}
if(isEqual) return;
pvCopy->copyUnchecked(*pvMaster);
bitSet->set(pvCopy->getFieldOffset());
return;
}
PVStructurePtr pvCopyStructure = static_pointer_cast<PVStructure>(pvCopy);
PVFieldPtrArray const & pvCopyFields = pvCopyStructure->getPVFields();
PVStructurePtr pvMasterStructure =
static_pointer_cast<PVStructure>(pvMaster);
PVFieldPtrArray const & pvMasterFields =
pvMasterStructure->getPVFields();
size_t length = pvCopyFields.size();
for(size_t i=0; i<length; i++) {
updateSubFieldSetBitSet(pvCopyFields[i],pvMasterFields[i],bitSet);
}
}
void PVCopy::updateStructureNodeFromBitSet(
PVStructurePtr const &pvCopy,
CopyStructureNodePtr const &structureNode,
BitSetPtr const &bitSet,
bool toCopy,
bool doAll)
{
size_t offset = structureNode->structureOffset;
if(!doAll) {
size_t nextSet = bitSet->nextSetBit(offset);
if(nextSet==string::npos) return;
}
if(offset>=pvCopy->getNextFieldOffset()) return;
if(!doAll) doAll = bitSet->get(offset);
CopyNodePtrArrayPtr nodes = structureNode->nodes;
for(size_t i=0; i<nodes->size(); i++) {
CopyNodePtr node = (*nodes)[i];
PVFieldPtr pvField = pvCopy->getSubFieldT(node->structureOffset);
if(node->isStructure) {
PVStructurePtr xxx = static_pointer_cast<PVStructure>(pvField);
CopyStructureNodePtr subStructureNode =
static_pointer_cast<CopyStructureNode>(node);
updateStructureNodeFromBitSet(
xxx,subStructureNode,bitSet,toCopy,doAll);
} else {
CopyMasterNodePtr masterNode =
static_pointer_cast<CopyMasterNode>(node);
updateSubFieldFromBitSet(
pvField,masterNode->masterPVField,bitSet,toCopy,doAll);
}
}
}
void PVCopy::updateSubFieldFromBitSet(
PVFieldPtr const &pvCopy,
PVFieldPtr const &pvMasterField,
BitSetPtr const &bitSet,
bool toCopy,
bool doAll)
{
if(!doAll) {
doAll = bitSet->get(pvCopy->getFieldOffset());
}
if(!doAll) {
size_t offset = pvCopy->getFieldOffset();
size_t nextSet = bitSet->nextSetBit(offset);
if(nextSet==string::npos) return;
if(nextSet>=pvCopy->getNextFieldOffset()) return;
}
if(pvCopy->getField()->getType()==epics::pvData::structure) {
PVStructurePtr pvCopyStructure =
static_pointer_cast<PVStructure>(pvCopy);
PVFieldPtrArray const & pvCopyFields = pvCopyStructure->getPVFields();
if(pvMasterField->getField()->getType() !=epics::pvData::structure)
{
throw std::logic_error(string("Logic error"));
}
PVStructurePtr pvMasterStructure =
static_pointer_cast<PVStructure>(pvMasterField);
PVFieldPtrArray const & pvMasterFields =
pvMasterStructure->getPVFields();
for(size_t i=0; i<pvCopyFields.size(); i++) {
updateSubFieldFromBitSet(
pvCopyFields[i],
pvMasterFields[i],
bitSet,toCopy,doAll);
}
} else {
if(toCopy) {
pvCopy->copyUnchecked(*pvMasterField);
} else {
pvMasterField->copyUnchecked(*pvCopy);
}
}
}
CopyMasterNodePtr PVCopy::getCopyOffset(
CopyStructureNodePtr const &structureNode,
PVFieldPtr const &masterPVField)
{
size_t offset = masterPVField->getFieldOffset();
CopyNodePtrArrayPtr nodes = structureNode->nodes;
for(size_t i=0; i< nodes->size(); i++) {
CopyNodePtr node = (*nodes)[i];
if(!node->isStructure) {
CopyMasterNodePtr masterNode =
static_pointer_cast<CopyMasterNode>(node);
size_t off = masterNode->masterPVField->getFieldOffset();
size_t nextOffset = masterNode->masterPVField->getNextFieldOffset();
if(offset>= off && offset<nextOffset) return masterNode;
} else {
CopyStructureNodePtr subNode =
static_pointer_cast<CopyStructureNode>(node);
CopyMasterNodePtr masterNode =
getCopyOffset(subNode,masterPVField);
if(masterNode) return masterNode;
}
}
return NULLCopyMasterNode;
}
CopyMasterNodePtr PVCopy::getMasterNode(
CopyStructureNodePtr const &structureNode,
std::size_t structureOffset)
{
CopyNodePtrArrayPtr nodes = structureNode->nodes;
for(size_t i=0; i< nodes->size(); i++) {
CopyNodePtr node = (*nodes)[i];
if(structureOffset>=(node->structureOffset + node->nfields)) continue;
if(!node->isStructure) {
CopyMasterNodePtr masterNode =
static_pointer_cast<CopyMasterNode>(node);
return masterNode;
}
CopyStructureNodePtr subNode =
static_pointer_cast<CopyStructureNode>(node);
return getMasterNode(subNode,structureOffset);
}
return NULLCopyMasterNode;
}
}}

View File

@@ -1,328 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <sstream>
#include <epicsAssert.h>
#include <epicsTypes.h>
#include <epicsVersion.h>
#include <epicsConvert.h>
#include <epicsAssert.h>
#include <epicsMutex.h>
#include <epicsGuard.h>
#define epicsExportSharedSymbols
#include <pv/createRequest.h>
#include <pv/epicsException.h>
#include <pv/bitSet.h>
// Our arbitrary limit on pvRequest structure depth to bound stack usage during recursion
static const unsigned maxDepth = 5;
namespace epics{namespace pvData {
PVRequestMapper::PVRequestMapper() {}
PVRequestMapper::PVRequestMapper(const PVStructure &base,
const PVStructure &pvRequest,
mode_t mode)
{
compute(base, pvRequest, mode);
}
PVStructurePtr PVRequestMapper::buildRequested() const
{
if(!typeRequested)
THROW_EXCEPTION2(std::logic_error, "No mapping compute()d");
return typeRequested->build();
}
PVStructurePtr PVRequestMapper::buildBase() const
{
if(!typeBase)
THROW_EXCEPTION2(std::logic_error, "No mapping compute()d");
return typeBase->build();
}
void PVRequestMapper::compute(const PVStructure &base,
const PVStructure &pvRequest,
mode_t mode)
{
if(base.getFieldOffset()!=0)
THROW_EXCEPTION2(std::logic_error, "Mapper must be used with top level PVStructure");
bool ok = true;
// we want to be transactional, which requires a second copy of everything.
PVRequestMapper temp;
// whether to preserve IDs of partial structures.
bool keepids = false;
PVScalar::const_shared_pointer pbp(pvRequest.getSubField<PVScalar>("record._options.keepIDs"));
try {
if(pbp) keepids = pbp->getAs<boolean>();
}catch(std::runtime_error& e){
std::ostringstream msg;
msg<<"Can't parse keepIDs : '"<<e.what()<<"' ";
temp.messages+=msg.str();
}
PVStructure::const_shared_pointer fields(pvRequest.getSubField<PVStructure>("field"));
if(!fields || fields->getPVFields().empty()) {
// not selection, or empty selection, treated as select all
temp.typeBase = temp.typeRequested = base.getStructure();
for(size_t i=1, N=base.getNextFieldOffset(); i<N; i++)
temp.maskRequested.set(i);
} else {
FieldBuilderPtr builder(getFieldCreate()->createFieldBuilder());
if(keepids)
builder = builder->setId(base.getStructure()->getID());
ok &= temp._compute(base, *fields, builder, keepids, 0); // fills in builder
temp.typeBase = base.getStructure();
temp.typeRequested = builder->createStructure();
// possible that typeBase==typeRequested if all fields explicitly selected
}
if(mode==Mask) {
// short circuit use of masked Structure, but keep maskRequested
temp.typeRequested = temp.typeBase;
}
{
PVStructurePtr proto(temp.typeRequested->build());
// base -> request may be sparce mapping
temp.base2req.resize(base.getNextFieldOffset());
// request -> base is dense mapping
temp.req2base.resize(proto->getNextFieldOffset());
// special handling for whole structure mapping. in part because getSubField(0) isn't allowed
temp.base2req[0] = Mapping(0, false);
temp.req2base[0] = Mapping(0, false);
// Iterate prototype of requested to map with base field offsets.
// which is handled as a special case below.
// We also don't try to prevent redundant copies if both leaf and compress bits are set.
for(size_t r=1, N=proto->getNextFieldOffset(); r<N; r++) {
PVField::const_shared_pointer fld_req(proto->getSubFieldT(r)),
fld_base(base.getSubFieldT(fld_req->getFullName()));
const size_t b = fld_base->getFieldOffset();
if(!temp.requestedMask().get(b))
continue;
bool leaf = fld_base->getField()->getType()!=structure;
// initialize mapping when our bit is set
temp.base2req[b] = Mapping(r, leaf);
temp.req2base[r] = Mapping(b, leaf);
// add ourself to all "compress" bit mappings of enclosing structures
for(const PVStructure *parent = fld_req->getParent(); parent; parent = parent->getParent()) {
temp.req2base[parent->getFieldOffset()].tomask .set(b);
temp.req2base[parent->getFieldOffset()].frommask.set(r);
}
for(const PVStructure *parent = fld_base->getParent(); parent; parent = parent->getParent()) {
temp.base2req[parent->getFieldOffset()].tomask .set(r);
temp.base2req[parent->getFieldOffset()].frommask.set(b);
}
}
}
temp.maskRequested.set(0);
if(temp.maskRequested.nextSetBit(1)==-1) {
ok = false;
temp.messages+="Empty field selection";
}
if(!ok)
throw std::runtime_error(temp.messages);
swap(temp);
}
bool PVRequestMapper::_compute(const PVStructure& base, const PVStructure& pvReq,
FieldBuilderPtr& builder, bool keepids, unsigned depth)
{
bool ok = true;
const StringArray& reqNames = pvReq.getStructure()->getFieldNames();
for(size_t i=0, N=reqNames.size(); i<N; i++) {
// iterate through requested fields
PVField::const_shared_pointer subtype(base.getSubField(reqNames[i]));
const FieldConstPtr& subReq = pvReq.getStructure()->getFields()[i];
if(subReq->getType()!=structure) {
// pvRequest .field was not properly composed
std::ostringstream msg;
// not a great warning message as it doesn't distinguish 'a.value' from 'b.value',
// but getFullName() whould prefix with 'field.', which would probably cause
// more frequent confusion...
msg<<"request invalid '"<<pvReq.getStructure()->getFieldNames()[i]<<"' ";
messages+=msg.str();
ok = false;
} else if(!subtype) {
// requested field does not actually exist in base
std::ostringstream msg;
msg<<"No field '"<<pvReq.getStructure()->getFieldNames()[i]<<"' ";
messages+=msg.str();
} else if(depth>=maxDepth // exceeds max recursion depth
|| subtype->getField()->getType()!=structure // requested field is a leaf
|| static_cast<const Structure&>(*subReq).getFieldNames().empty() // requests all sub-fields
)
{
// just add the whole thing
builder = builder->add(reqNames[i], subtype->getField());
for(size_t j=subtype->getFieldOffset(), N=subtype->getNextFieldOffset(); j<N; j++)
maskRequested.set(j);
if(subtype->getField()->getType()!=structure
&& !static_cast<const Structure&>(*subReq).getFieldNames().empty())
{
// attempt to select below a leaf field
std::ostringstream msg;
msg<<"Leaf field '"<<pvReq.getFullName()<<"' ";
messages+=msg.str();
} else if(depth>=maxDepth) {
std::ostringstream msg;
msg<<"selection truncated at '"<<pvReq.getFullName()<<"' ";
messages+=msg.str();
}
} else {
// recurse into sub-structure
const PVStructure& substruct = static_cast<const PVStructure&>(*subtype);
builder = builder->addNestedStructure(reqNames[i]);
maskRequested.set(substruct.getFieldOffset());
if(keepids)
builder = builder->setId(substruct.getStructure()->getID());
_compute(substruct,
static_cast<const PVStructure&>(*pvReq.getPVFields()[i]),
builder, keepids, depth+1u);
builder = builder->endNested();
}
}
return ok;
}
void PVRequestMapper::copyBaseToRequested(
const PVStructure& base,
const BitSet& baseMask,
PVStructure& request,
BitSet& requestMask
) const {
assert(base.getStructure()==typeBase);
assert(request.getStructure()==typeRequested);
_map(base, baseMask, request, requestMask, false);
}
void PVRequestMapper::copyBaseFromRequested(
PVStructure& base,
BitSet& baseMask,
const PVStructure& request,
const BitSet& requestMask
) const {
assert(base.getStructure()==typeBase);
assert(request.getStructure()==typeRequested);
_map(request, requestMask, base, baseMask, true);
}
void PVRequestMapper::_map(const PVStructure& src, const BitSet& maskSrc,
PVStructure& dest, BitSet& maskDest,
bool dir_r2b) const
{
{
scratch = maskSrc;
const mapping_t& map = dir_r2b ? req2base : base2req;
assert(map.size()==src.getNumberFields());
for(int32 i=scratch.nextSetBit(0), N=map.size(); i>=0 && i<N; i=scratch.nextSetBit(i+1)) {
const Mapping& M = map[i];
if(!M.valid) {
assert(!dir_r2b); // only base -> requested mapping can have holes
} else if(M.leaf) {
// just copy
dest.getSubFieldT(M.to)->copy(*src.getSubFieldT(i));
maskDest.set(M.to);
} else {
// set bits of all sub-fields (in requested structure)
// these indicies are always >i
scratch |= M.frommask;
// we will also set the individual bits, but if a compress bit is set in the input,
// then set the corresponding bit in the output.
maskDest.set(M.to);
}
}
}
}
void PVRequestMapper::_mapMask(const BitSet& maskSrc,
BitSet& maskDest,
bool dir_r2b) const
{
if(maskSrc.isEmpty()) {
// no-op
} else {
const mapping_t& map = dir_r2b ? req2base : base2req;
for(int32 i=maskSrc.nextSetBit(0), N=map.size(); i>=0 && i<N; i=maskSrc.nextSetBit(i+1)) {
const Mapping& M = map[i];
if(!M.valid) {
assert(!dir_r2b); // only base -> requested mapping can have holes
} else {
maskDest.set(M.to);
if(!M.leaf) {
maskDest |= M.tomask;
}
}
}
}
}
void PVRequestMapper::swap(PVRequestMapper& other)
{
typeBase.swap(other.typeBase);
typeRequested.swap(other.typeRequested);
maskRequested.swap(other.maskRequested);
base2req.swap(other.base2req);
req2base.swap(other.req2base);
messages.swap(other.messages);
scratch.swap(other.scratch); // paranoia
}
void PVRequestMapper::reset()
{
typeBase.reset();
typeRequested.reset();
maskRequested.clear();
base2req.clear();
req2base.clear();
messages.clear();
scratch.clear(); // paranoia
}
}} //namespace epics::pvData

View File

@@ -23,7 +23,7 @@ namespace epics { namespace pvData {
* 1) same instance
* 2) same type (field and scalar/element), same name, same subfields (if any)
*/
bool compare(const Field& a, const Field& b)
bool operator==(const Field& a, const Field& b)
{
if(&a==&b)
return true;
@@ -33,58 +33,58 @@ bool compare(const Field& a, const Field& b)
case scalar: {
const Scalar &A=static_cast<const Scalar&>(a);
const Scalar &B=static_cast<const Scalar&>(b);
return compare(A, B);
return A==B;
}
case scalarArray: {
const ScalarArray &A=static_cast<const ScalarArray&>(a);
const ScalarArray &B=static_cast<const ScalarArray&>(b);
return compare(A, B);
return A==B;
}
case structure: {
const Structure &A=static_cast<const Structure&>(a);
const Structure &B=static_cast<const Structure&>(b);
return compare(A, B);
return A==B;
}
case structureArray: {
const StructureArray &A=static_cast<const StructureArray&>(a);
const StructureArray &B=static_cast<const StructureArray&>(b);
return compare(A, B);
return A==B;
}
case union_: {
const Union &A=static_cast<const Union&>(a);
const Union &B=static_cast<const Union&>(b);
return compare(A, B);
return A==B;
}
case unionArray: {
const UnionArray &A=static_cast<const UnionArray&>(a);
const UnionArray &B=static_cast<const UnionArray&>(b);
return compare(A, B);
return A==B;
}
default:
throw std::logic_error("Invalid Field type in comparison");
}
}
bool compare(const Scalar& a, const Scalar& b)
bool operator==(const Scalar& a, const Scalar& b)
{
if(&a==&b)
return true;
return a.getScalarType()==b.getScalarType();
}
bool compare(const ScalarArray& a, const ScalarArray& b)
bool operator==(const ScalarArray& a, const ScalarArray& b)
{
if(&a==&b)
return true;
return a.getElementType()==b.getElementType();
}
bool compare(const Structure& a, const Structure& b)
bool operator==(const Structure& a, const Structure& b)
{
if(&a==&b)
return true;
if (a.getID()!=b.getID())
return false;
return false;
size_t nflds=a.getNumberFields();
if (b.getNumberFields()!=nflds)
return false;
@@ -101,17 +101,17 @@ bool compare(const Structure& a, const Structure& b)
return std::equal( an.begin(), an.end(), bn.begin() );
}
bool compare(const StructureArray& a, const StructureArray& b)
bool operator==(const StructureArray& a, const StructureArray& b)
{
return *(a.getStructure().get())==*(b.getStructure().get());
}
bool compare(const Union& a, const Union& b)
bool operator==(const Union& a, const Union& b)
{
if(&a==&b)
return true;
if (a.getID()!=b.getID())
return false;
return false;
size_t nflds=a.getNumberFields();
if (b.getNumberFields()!=nflds)
return false;
@@ -128,12 +128,12 @@ bool compare(const Union& a, const Union& b)
return std::equal( an.begin(), an.end(), bn.begin() );
}
bool compare(const UnionArray& a, const UnionArray& b)
bool operator==(const UnionArray& a, const UnionArray& b)
{
return *(a.getUnion().get())==*(b.getUnion().get());
}
bool compare(const BoundedString& a, const BoundedString& b)
bool operator==(const BoundedString& a, const BoundedString& b)
{
if(&a==&b)
return true;
@@ -268,10 +268,10 @@ bool compareField(const PVUnion* left, const PVUnion* right)
if(*ls!=*right->getUnion())
return false;
if (ls->isVariant())
{
const PVField::const_shared_pointer& lval = left->get();
PVFieldPtr lval = left->get();
if (lval.get() == 0)
return right->get().get() == 0;
else

View File

@@ -24,7 +24,7 @@ using std::tr1::static_pointer_cast;
using std::size_t;
using std::string;
namespace epics { namespace pvData {
namespace epics { namespace pvData {
static std::vector<string> split(string commaSeparatedList) {
@@ -60,7 +60,7 @@ void Convert::getString(string *buf,PVField const *pvField,int /*indentLevel*/)
size_t Convert::fromString(PVStructurePtr const &pvStructure, StringArray const & from, size_t fromStartIndex)
{
size_t processed = 0;
PVFieldPtrArray const & fieldsData = pvStructure->getPVFields();
if (fieldsData.size() != 0) {
size_t length = pvStructure->getStructure()->getNumberFields();
@@ -93,7 +93,7 @@ size_t Convert::fromString(PVStructurePtr const &pvStructure, StringArray const
}
}
}
return processed;
}

File diff suppressed because it is too large Load Diff

View File

@@ -28,7 +28,7 @@ PVArray::PVArray(FieldConstPtr const & field)
{
capacityMutable = false;
PVField::setImmutable();
}
}
bool PVArray::isCapacityMutable() const
{
@@ -49,7 +49,7 @@ PVArray::PVArray(FieldConstPtr const & field)
std::ostream& operator<<(format::array_at_internal const& manip, const PVArray& array)
{
return array.dumpValue(manip.stream, manip.index);
return array.dumpValue(manip.stream, manip.index);
}
}}

View File

@@ -7,13 +7,16 @@
* @author mrk
*/
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <cstddef>
#include <cstdlib>
#include <string>
#include <cstdio>
#include <epicsMutex.h>
#include <epicsThread.h>
#define epicsExportSharedSymbols
#include <pv/lock.h>
@@ -21,7 +24,6 @@
#include <pv/pvData.h>
#include <pv/factory.h>
#include <pv/serializeHelper.h>
#include <pv/reftrack.h>
using std::tr1::static_pointer_cast;
using std::size_t;
@@ -57,123 +59,148 @@ template<> const ScalarType PVFloatArray::typeCode = pvFloat;
template<> const ScalarType PVDoubleArray::typeCode = pvDouble;
template<> const ScalarType PVStringArray::typeCode = pvString;
/** Default storage for scalar values
*/
template<typename T>
class BasePVScalar : public PVScalarValue<T> {
public:
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
BasePVScalar(ScalarConstPtr const & scalar);
virtual ~BasePVScalar();
virtual T get() const ;
virtual void put(T val);
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const;
virtual void deserialize(ByteBuffer *pbuffer,
DeserializableControl *pflusher);
private:
T value;
};
template<typename T>
PVScalarValue<T>::~PVScalarValue() {}
BasePVScalar<T>::BasePVScalar(ScalarConstPtr const & scalar)
: PVScalarValue<T>(scalar),value(0)
{}
//Note: '0' is a suitable default for all POD types (not string)
template<typename T>
std::ostream& PVScalarValue<T>::dumpValue(std::ostream& o) const
BasePVScalar<T>::~BasePVScalar() {}
template<typename T>
T BasePVScalar<T>::get() const { return value;}
template<typename T>
void BasePVScalar<T>::put(T val)
{
return o << get();
value = val;
PVField::postPut();
}
template<typename T>
void PVScalarValue<T>::operator>>=(T& value) const
{
value = get();
}
template<typename T>
void PVScalarValue<T>::operator<<=(typename storage_t::arg_type value)
{
put(value);
}
template<typename T>
void PVScalarValue<T>::assign(const PVScalar& scalar)
{
if(isImmutable())
throw std::invalid_argument("destination is immutable");
copyUnchecked(scalar);
}
template<typename T>
void PVScalarValue<T>::copy(const PVScalar& from)
{
assign(from);
}
template<typename T>
void PVScalarValue<T>::copyUnchecked(const PVScalar& from)
{
if(this==&from)
return;
T result;
from.getAs((void*)&result, typeCode);
put(result);
}
template<typename T>
void PVScalarValue<T>::serialize(ByteBuffer *pbuffer,
void BasePVScalar<T>::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const {
pflusher->ensureBuffer(sizeof(T));
pbuffer->put(storage.value);
}
template<>
void PVScalarValue<std::string>::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const {
SerializeHelper::serializeString(storage.value, pbuffer, pflusher);
pbuffer->put(value);
}
template<typename T>
void PVScalarValue<T>::deserialize(ByteBuffer *pbuffer,
void BasePVScalar<T>::deserialize(ByteBuffer *pbuffer,
DeserializableControl *pflusher)
{
pflusher->ensureData(sizeof(T));
storage.value = pbuffer->GET(T);
value = pbuffer->GET(T);
}
template<>
void PVScalarValue<std::string>::deserialize(ByteBuffer *pbuffer,
DeserializableControl *pflusher)
{
storage.value = SerializeHelper::deserializeString(pbuffer, pflusher);
// TODO: check for violations of maxLength?
}
typedef BasePVScalar<boolean> BasePVBoolean;
typedef BasePVScalar<int8> BasePVByte;
typedef BasePVScalar<int16> BasePVShort;
typedef BasePVScalar<int32> BasePVInt;
typedef BasePVScalar<int64> BasePVLong;
typedef BasePVScalar<uint8> BasePVUByte;
typedef BasePVScalar<uint16> BasePVUShort;
typedef BasePVScalar<uint32> BasePVUInt;
typedef BasePVScalar<uint64> BasePVULong;
typedef BasePVScalar<float> BasePVFloat;
typedef BasePVScalar<double> BasePVDouble;
PVString::PVString(ScalarConstPtr const & scalar)
: PVScalarValue<std::string>(scalar)
// BasePVString is special case, since it implements SerializableArray
class BasePVString : public PVString {
public:
typedef string value_type;
typedef string* pointer;
typedef const string* const_pointer;
BasePVString(ScalarConstPtr const & scalar);
virtual ~BasePVString();
virtual string get() const ;
virtual void put(string val);
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const;
virtual void deserialize(ByteBuffer *pbuffer,
DeserializableControl *pflusher);
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const;
private:
string value;
std::size_t maxLength;
};
BasePVString::BasePVString(ScalarConstPtr const & scalar)
: PVString(scalar),value()
{
BoundedStringConstPtr boundedString = std::tr1::dynamic_pointer_cast<const BoundedString>(scalar);
if (boundedString.get())
storage.maxLength = boundedString->getMaximumLength();
maxLength = boundedString->getMaximumLength();
else
storage.maxLength = 0;
maxLength = 0;
}
std::ostream& PVString::dumpValue(std::ostream& o) const
BasePVString::~BasePVString() {}
string BasePVString::get() const { return value;}
void BasePVString::put(string val)
{
o<<maybeQuote(get());
return o;
if (maxLength > 0 && val.length() > maxLength)
throw std::overflow_error("string too long");
value = val;
postPut();
}
/* mixing overrides (virtual functions) and overloads (different argument lists) is fun...
* we override all overloads to avoid the "hides overloaded virtual function" warning from clang.
* In this case we don't need/want to, so just delegate to the base class.
*/
void PVString::serialize(ByteBuffer *pbuffer,
void BasePVString::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const
{PVScalarValue<std::string>::serialize(pbuffer, pflusher);}
{
SerializeHelper::serializeString(value, pbuffer, pflusher);
}
void PVString::serialize(ByteBuffer *pbuffer,
void BasePVString::deserialize(ByteBuffer *pbuffer,
DeserializableControl *pflusher)
{
value = SerializeHelper::deserializeString(pbuffer, pflusher);
}
void BasePVString::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const
{
// check bounds
const size_t length = storage.value.length();
/*if (offset < 0) offset = 0;
else*/ if (offset > length) offset = length;
//if (count < 0) count = length;
// check bounds
const size_t length = /*(value == null) ? 0 :*/ value.length();
/*if (offset < 0) offset = 0;
else*/ if (offset > length) offset = length;
//if (count < 0) count = length;
const size_t maxCount = length - offset;
if (count > maxCount)
count = maxCount;
// write
SerializeHelper::serializeSubstring(storage.value, offset, count, pbuffer, pflusher);
const size_t maxCount = length - offset;
if (count > maxCount)
count = maxCount;
// write
SerializeHelper::serializeSubstring(value, offset, count, pbuffer, pflusher);
}
void PVArray::checkLength(size_t len) const
void PVArray::checkLength(size_t len)
{
Array::ArraySizeType type = getArray()->getArraySizeType();
if (type != Array::variable)
@@ -186,79 +213,61 @@ void PVArray::checkLength(size_t len) const
}
}
/** Default storage for arrays
*/
template<typename T>
PVValueArray<T>::PVValueArray(ScalarArrayConstPtr const & scalarArray)
:base_t(scalarArray)
,value()
class DefaultPVArray : public PVValueArray<T> {
public:
typedef T* pointer;
typedef const T* const_pointer;
typedef std::vector<T> vector;
typedef const std::vector<T> const_vector;
typedef std::tr1::shared_ptr<vector> shared_vector;
{}
typedef ::epics::pvData::shared_vector<T> svector;
typedef ::epics::pvData::shared_vector<const T> const_svector;
PVValueArray<PVStructurePtr>::PVValueArray(StructureArrayConstPtr const & structureArray)
:base_t(structureArray)
,structureArray(structureArray)
DefaultPVArray(ScalarArrayConstPtr const & scalarArray);
virtual ~DefaultPVArray();
{}
virtual size_t getLength() const {return value.size();}
virtual size_t getCapacity() const {return value.capacity();}
PVValueArray<PVUnionPtr>::PVValueArray(UnionArrayConstPtr const & unionArray)
:base_t(unionArray)
,unionArray(unionArray)
{}
virtual void setCapacity(size_t capacity);
virtual void setLength(size_t length);
virtual const_svector view() const {return value;}
virtual void swap(const_svector &other);
virtual void replace(const const_svector& next);
// from Serializable
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) const;
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher);
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const;
private:
const_svector value;
};
template<typename T>
PVValueArray<T>::~PVValueArray() {}
template<typename T>
ArrayConstPtr PVValueArray<T>::getArray() const
DefaultPVArray<T>::DefaultPVArray(ScalarArrayConstPtr const & scalarArray)
: PVValueArray<T>(scalarArray),
value()
{
return std::tr1::static_pointer_cast<const Array>(this->getField());
}
template<typename T>
std::ostream& PVValueArray<T>::dumpValue(std::ostream& o) const
{
const_svector v(this->view());
typename const_svector::const_iterator it(v.begin()),
end(v.end());
o << '[';
if(it!=end) {
o << print_cast(*it++);
for(; it!=end; ++it)
o << ',' << print_cast(*it);
ArrayConstPtr array = this->getArray();
if (array->getArraySizeType() == Array::fixed)
{
// this->setLength(array->getMaximumCapacity());
this->setCapacityMutable(false);
}
return o << ']';
}
template<>
std::ostream& PVValueArray<std::string>::dumpValue(std::ostream& o, size_t index) const
{
return o << maybeQuote(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 << maybeQuote(*it++);
for(; it!=end; ++it)
o << ", " << maybeQuote(*it);
}
return o << ']';
}
template<typename T>
std::ostream& PVValueArray<T>::dumpValue(std::ostream& o, size_t index) const
{
return o << print_cast(this->view().at(index));
}
DefaultPVArray<T>::~DefaultPVArray()
{ }
template<typename T>
void PVValueArray<T>::setCapacity(size_t capacity)
void DefaultPVArray<T>::setCapacity(size_t capacity)
{
if(this->isCapacityMutable()) {
this->checkLength(capacity);
@@ -269,7 +278,7 @@ void PVValueArray<T>::setCapacity(size_t capacity)
}
template<typename T>
void PVValueArray<T>::setLength(size_t length)
void DefaultPVArray<T>::setLength(size_t length)
{
if(this->isImmutable())
THROW_EXCEPTION2(std::logic_error, "immutable");
@@ -286,7 +295,7 @@ void PVValueArray<T>::setLength(size_t length)
}
template<typename T>
void PVValueArray<T>::replace(const const_svector& next)
void DefaultPVArray<T>::replace(const const_svector& next)
{
this->checkLength(next.size());
@@ -295,7 +304,7 @@ void PVValueArray<T>::replace(const const_svector& next)
}
template<typename T>
void PVValueArray<T>::swap(const_svector &other)
void DefaultPVArray<T>::swap(const_svector &other)
{
if (this->isImmutable())
THROW_EXCEPTION2(std::logic_error, "immutable");
@@ -307,13 +316,13 @@ void PVValueArray<T>::swap(const_svector &other)
template<typename T>
void PVValueArray<T>::serialize(ByteBuffer *pbuffer,
void DefaultPVArray<T>::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const {
serialize(pbuffer, pflusher, 0, this->getLength());
}
template<typename T>
void PVValueArray<T>::deserialize(ByteBuffer *pbuffer,
void DefaultPVArray<T>::deserialize(ByteBuffer *pbuffer,
DeserializableControl *pcontrol) {
size_t size = this->getArray()->getArraySizeType() == Array::fixed ?
@@ -362,7 +371,7 @@ void PVValueArray<T>::deserialize(ByteBuffer *pbuffer,
}
template<typename T>
void PVValueArray<T>::serialize(ByteBuffer *pbuffer,
void DefaultPVArray<T>::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const
{
//TODO: avoid incrementing the ref counter...
@@ -406,7 +415,7 @@ void PVValueArray<T>::serialize(ByteBuffer *pbuffer,
// specializations for string
template<>
void PVValueArray<string>::deserialize(ByteBuffer *pbuffer,
void DefaultPVArray<string>::deserialize(ByteBuffer *pbuffer,
DeserializableControl *pcontrol) {
size_t size = this->getArray()->getArraySizeType() == Array::fixed ?
@@ -433,7 +442,7 @@ void PVValueArray<string>::deserialize(ByteBuffer *pbuffer,
}
template<>
void PVValueArray<string>::serialize(ByteBuffer *pbuffer,
void DefaultPVArray<string>::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const {
const_svector temp(value);
@@ -449,17 +458,18 @@ void PVValueArray<string>::serialize(ByteBuffer *pbuffer,
}
}
template<typename T>
void PVValueArray<T>::_getAsVoid(epics::pvData::shared_vector<const void>& out) const
{
out = static_shared_vector_cast<const void>(this->view());
}
template<typename T>
void PVValueArray<T>::_putFromVoid(const epics::pvData::shared_vector<const void>& in)
{
this->replace(shared_vector_convert<const T>(in));
}
typedef DefaultPVArray<boolean> DefaultPVBooleanArray;
typedef DefaultPVArray<int8> BasePVByteArray;
typedef DefaultPVArray<int16> BasePVShortArray;
typedef DefaultPVArray<int32> BasePVIntArray;
typedef DefaultPVArray<int64> BasePVLongArray;
typedef DefaultPVArray<uint8> BasePVUByteArray;
typedef DefaultPVArray<uint16> BasePVUShortArray;
typedef DefaultPVArray<uint32> BasePVUIntArray;
typedef DefaultPVArray<uint64> BasePVULongArray;
typedef DefaultPVArray<float> BasePVFloatArray;
typedef DefaultPVArray<double> BasePVDoubleArray;
typedef DefaultPVArray<string> BasePVStringArray;
// Factory
@@ -554,29 +564,29 @@ PVScalarPtr PVDataCreate::createPVScalar(ScalarConstPtr const & scalar)
ScalarType scalarType = scalar->getScalarType();
switch(scalarType) {
case pvBoolean:
return PVScalarPtr(new PVBoolean(scalar));
return PVScalarPtr(new BasePVBoolean(scalar));
case pvByte:
return PVScalarPtr(new PVByte(scalar));
return PVScalarPtr(new BasePVByte(scalar));
case pvShort:
return PVScalarPtr(new PVShort(scalar));
return PVScalarPtr(new BasePVShort(scalar));
case pvInt:
return PVScalarPtr(new PVInt(scalar));
return PVScalarPtr(new BasePVInt(scalar));
case pvLong:
return PVScalarPtr(new PVLong(scalar));
return PVScalarPtr(new BasePVLong(scalar));
case pvUByte:
return PVScalarPtr(new PVUByte(scalar));
return PVScalarPtr(new BasePVUByte(scalar));
case pvUShort:
return PVScalarPtr(new PVUShort(scalar));
return PVScalarPtr(new BasePVUShort(scalar));
case pvUInt:
return PVScalarPtr(new PVUInt(scalar));
return PVScalarPtr(new BasePVUInt(scalar));
case pvULong:
return PVScalarPtr(new PVULong(scalar));
return PVScalarPtr(new BasePVULong(scalar));
case pvFloat:
return PVScalarPtr(new PVFloat(scalar));
return PVScalarPtr(new BasePVFloat(scalar));
case pvDouble:
return PVScalarPtr(new PVDouble(scalar));
return PVScalarPtr(new BasePVDouble(scalar));
case pvString:
return PVScalarPtr(new PVString(scalar));
return PVScalarPtr(new BasePVString(scalar));
}
throw std::logic_error("PVDataCreate::createPVScalar should never get here");
}
@@ -601,32 +611,32 @@ PVScalarArrayPtr PVDataCreate::createPVScalarArray(
{
switch(scalarArray->getElementType()) {
case pvBoolean:
return PVScalarArrayPtr(new PVBooleanArray(scalarArray));
return PVScalarArrayPtr(new DefaultPVBooleanArray(scalarArray));
case pvByte:
return PVScalarArrayPtr(new PVByteArray(scalarArray));
return PVScalarArrayPtr(new BasePVByteArray(scalarArray));
case pvShort:
return PVScalarArrayPtr(new PVShortArray(scalarArray));
return PVScalarArrayPtr(new BasePVShortArray(scalarArray));
case pvInt:
return PVScalarArrayPtr(new PVIntArray(scalarArray));
return PVScalarArrayPtr(new BasePVIntArray(scalarArray));
case pvLong:
return PVScalarArrayPtr(new PVLongArray(scalarArray));
return PVScalarArrayPtr(new BasePVLongArray(scalarArray));
case pvUByte:
return PVScalarArrayPtr(new PVUByteArray(scalarArray));
return PVScalarArrayPtr(new BasePVUByteArray(scalarArray));
case pvUShort:
return PVScalarArrayPtr(new PVUShortArray(scalarArray));
return PVScalarArrayPtr(new BasePVUShortArray(scalarArray));
case pvUInt:
return PVScalarArrayPtr(new PVUIntArray(scalarArray));
return PVScalarArrayPtr(new BasePVUIntArray(scalarArray));
case pvULong:
return PVScalarArrayPtr(new PVULongArray(scalarArray));
return PVScalarArrayPtr(new BasePVULongArray(scalarArray));
case pvFloat:
return PVScalarArrayPtr(new PVFloatArray(scalarArray));
return PVScalarArrayPtr(new BasePVFloatArray(scalarArray));
case pvDouble:
return PVScalarArrayPtr(new PVDoubleArray(scalarArray));
return PVScalarArrayPtr(new BasePVDoubleArray(scalarArray));
case pvString:
return PVScalarArrayPtr(new PVStringArray(scalarArray));
return PVScalarArrayPtr(new BasePVStringArray(scalarArray));
}
throw std::logic_error("PVDataCreate::createPVScalarArray should never get here");
}
PVScalarArrayPtr PVDataCreate::createPVScalarArray(
@@ -714,63 +724,23 @@ PVUnionPtr PVDataCreate::createPVUnion(PVUnionPtr const & unionToClone)
return punion;
}
namespace detail {
struct pvfield_factory {
PVDataCreatePtr pvDataCreate;
pvfield_factory() :pvDataCreate(new PVDataCreate()) {
registerRefCounter("PVField", &PVField::num_instances);
}
};
}
static detail::pvfield_factory* pvfield_factory_s;
static epicsThreadOnceId pvfield_factory_once = EPICS_THREAD_ONCE_INIT;
static void pvfield_factory_init(void*)
// TODO not thread-safe (local static initializers)
// TODO replace with non-locking singleton pattern
PVDataCreatePtr PVDataCreate::getPVDataCreate()
{
try {
pvfield_factory_s = new detail::pvfield_factory;
}catch(std::exception& e){
std::cerr<<"Error initializing getFieldCreate() : "<<e.what()<<"\n";
}
static PVDataCreatePtr pvDataCreate;
static Mutex mutex;
Lock xx(mutex);
if(pvDataCreate.get()==0) pvDataCreate = PVDataCreatePtr(new PVDataCreate());
return pvDataCreate;
}
const PVDataCreatePtr& PVDataCreate::getPVDataCreate()
{
epicsThreadOnce(&pvfield_factory_once, &pvfield_factory_init, 0);
if(!pvfield_factory_s->pvDataCreate)
throw std::logic_error("getPVDataCreate() not initialized");
return pvfield_factory_s->pvDataCreate;
PVDataCreatePtr getPVDataCreate() {
return PVDataCreate::getPVDataCreate();
}
// explicitly instanciate to ensure that windows
// builds emit exported symbols for inline'd methods
template class PVScalarValue<boolean>;
template class PVScalarValue<int8>;
template class PVScalarValue<uint8>;
template class PVScalarValue<int16>;
template class PVScalarValue<uint16>;
template class PVScalarValue<int32>;
template class PVScalarValue<uint32>;
template class PVScalarValue<int64>;
template class PVScalarValue<uint64>;
template class PVScalarValue<float>;
template class PVScalarValue<double>;
template class PVScalarValue<std::string>;
template class PVValueArray<boolean>;
template class PVValueArray<int8>;
template class PVValueArray<uint8>;
template class PVValueArray<int16>;
template class PVValueArray<uint16>;
template class PVValueArray<int32>;
template class PVValueArray<uint32>;
template class PVValueArray<int64>;
template class PVValueArray<uint64>;
template class PVValueArray<float>;
template class PVValueArray<double>;
template class PVValueArray<std::string>;
}} // namespace epics::pvData
}}
namespace std{
std::ostream& operator<<(std::ostream& o, const epics::pvData::PVField *ptr)
@@ -779,3 +749,4 @@ namespace std{
return o << "nullptr";
}
}

View File

@@ -17,7 +17,6 @@
#include <pv/lock.h>
#include <pv/pvData.h>
#include <pv/factory.h>
#include <pv/reftrack.h>
using std::tr1::const_pointer_cast;
using std::size_t;
@@ -25,20 +24,15 @@ using std::string;
namespace epics { namespace pvData {
size_t PVField::num_instances;
PVField::PVField(FieldConstPtr field)
: parent(NULL),field(field),
fieldOffset(0), nextFieldOffset(0),
immutable(false)
{
REFTRACE_INCREMENT(num_instances);
}
PVField::~PVField()
{
REFTRACE_DECREMENT(num_instances);
}
{ }
size_t PVField::getFieldOffset() const
@@ -60,16 +54,22 @@ size_t PVField::getNumberFields() const
}
bool PVField::isImmutable() const {return immutable;}
void PVField::setImmutable() {immutable = true;}
void PVField::postPut()
const FieldConstPtr & PVField::getField() const {return field;}
PVStructure *PVField::getParent() const {return parent;}
void PVField::postPut()
{
if(postHandler) postHandler->postPut();
if(postHandler.get()!=NULL) postHandler->postPut();
}
void PVField::setPostHandler(PostHandlerPtr const &handler)
{
if(postHandler) {
if(postHandler.get()!=NULL) {
if(postHandler.get()==handler.get()) return;
throw std::logic_error(
"PVField::setPostHandler a postHandler is already registered");
@@ -91,13 +91,13 @@ bool PVField::equals(PVField &pv)
std::ostream& operator<<(std::ostream& o, const PVField& f)
{
return f.dumpValue(o);
return f.dumpValue(o);
};
string PVField::getFullName() const
{
string ret(fieldName);
for(const PVField *fld=getParent(); fld; fld=fld->getParent())
for(PVField *fld=getParent(); fld; fld=fld->getParent())
{
if(fld->getFieldName().size()==0) break;
ret = fld->getFieldName() + '.' + ret;
@@ -184,16 +184,62 @@ void PVField::copy(const PVField& from)
if(isImmutable())
throw std::invalid_argument("destination is immutable");
if (getField() != from.getField())
if (getField()->getType() != from.getField()->getType())
throw std::invalid_argument("field types do not match");
copyUnchecked(from);
switch(getField()->getType())
{
case scalar:
{
const PVScalar* fromS = static_cast<const PVScalar*>(&from);
PVScalar* toS = static_cast<PVScalar*>(this);
toS->copy(*fromS);
break;
}
case scalarArray:
{
const PVScalarArray* fromS = static_cast<const PVScalarArray*>(&from);
PVScalarArray* toS = static_cast<PVScalarArray*>(this);
toS->copy(*fromS);
break;
}
case structure:
{
const PVStructure* fromS = static_cast<const PVStructure*>(&from);
PVStructure* toS = static_cast<PVStructure*>(this);
toS->copy(*fromS);
break;
}
case structureArray:
{
const PVStructureArray* fromS = static_cast<const PVStructureArray*>(&from);
PVStructureArray* toS = static_cast<PVStructureArray*>(this);
toS->copy(*fromS);
break;
}
case union_:
{
const PVUnion* fromS = static_cast<const PVUnion*>(&from);
PVUnion* toS = static_cast<PVUnion*>(this);
toS->copy(*fromS);
break;
}
case unionArray:
{
const PVUnionArray* fromS = static_cast<const PVUnionArray*>(&from);
PVUnionArray* toS = static_cast<PVUnionArray*>(this);
toS->copy(*fromS);
break;
}
default:
{
throw std::logic_error("PVField::copy unknown type");
}
}
}
void PVField::copyUnchecked(const PVField& from)
{
assert(getField()==from.getField());
switch(getField()->getType())
{
case scalar:

View File

@@ -24,6 +24,25 @@ using std::string;
namespace epics { namespace pvData {
PVFieldPtr PVStructure::nullPVField;
PVBooleanPtr PVStructure::nullPVBoolean;
PVBytePtr PVStructure::nullPVByte;
PVShortPtr PVStructure::nullPVShort;
PVIntPtr PVStructure::nullPVInt;
PVLongPtr PVStructure::nullPVLong;
PVUBytePtr PVStructure::nullPVUByte;
PVUShortPtr PVStructure::nullPVUShort;
PVUIntPtr PVStructure::nullPVUInt;
PVULongPtr PVStructure::nullPVULong;
PVFloatPtr PVStructure::nullPVFloat;
PVDoublePtr PVStructure::nullPVDouble;
PVStringPtr PVStructure::nullPVString;
PVStructurePtr PVStructure::nullPVStructure;
PVStructureArrayPtr PVStructure::nullPVStructureArray;
PVUnionPtr PVStructure::nullPVUnion;
PVUnionArrayPtr PVStructure::nullPVUnionArray;
PVScalarArrayPtr PVStructure::nullPVScalarArray;
PVStructure::PVStructure(StructureConstPtr const & structurePtr)
: PVField(structurePtr),
structurePtr(structurePtr),
@@ -38,7 +57,7 @@ PVStructure::PVStructure(StructureConstPtr const & structurePtr)
pvFields.push_back(pvDataCreate->createPVField(fields[i]));
}
for(size_t i=0; i<numberFields; i++) {
pvFields[i]->setParentAndName(this,fieldNames[i]);
pvFields[i]->setParentAndName(this,fieldNames[i]);
}
}
@@ -60,7 +79,9 @@ PVStructure::PVStructure(StructureConstPtr const & structurePtr,
}
}
PVStructure::~PVStructure() {}
PVStructure::~PVStructure()
{
}
void PVStructure::setImmutable()
{
@@ -72,42 +93,60 @@ void PVStructure::setImmutable()
PVField::setImmutable();
}
PVFieldPtr PVStructure::getSubFieldImpl(size_t fieldOffset, bool throws) const
StructureConstPtr PVStructure::getStructure() const
{
const PVStructure *current = this;
return structurePtr;
}
recurse:
// we don't permit self lookup
if(fieldOffset<=current->getFieldOffset() || fieldOffset>=current->getNextFieldOffset()) {
if(throws) {
std::stringstream ss;
ss << "Failed to get field with offset "
<< fieldOffset << " (Invalid offset)" ;
throw std::runtime_error(ss.str());
} else {
return PVFieldPtr();
const PVFieldPtrArray & PVStructure::getPVFields() const
{
return pvFields;
}
PVFieldPtr PVStructure::getSubField(const char * fieldName) const
{
PVField * field = getSubFieldImpl(fieldName, false);
if (field)
return field->shared_from_this();
else
return PVFieldPtr();
}
PVFieldPtr PVStructure::getSubField(size_t fieldOffset) const
{
if(fieldOffset<=getFieldOffset()) {
return nullPVField;
}
if(fieldOffset>getNextFieldOffset()) return nullPVField;
size_t numFields = pvFields.size();
for(size_t i=0; i<numFields; i++) {
PVFieldPtr pvField = pvFields[i];
if(pvField->getFieldOffset()==fieldOffset) return pvFields[i];
if(pvField->getNextFieldOffset()<=fieldOffset) continue;
if(pvField->getField()->getType()==structure) {
PVStructure *pvStructure = static_cast<PVStructure *>(pvField.get());
return pvStructure->getSubField(fieldOffset);
}
}
for(size_t i=0, numFields = current->pvFields.size(); i<numFields; i++) {
const PVFieldPtr& pvField = current->pvFields[i];
if(pvField->getFieldOffset()==fieldOffset) {
return pvField;
} else if(pvField->getNextFieldOffset()<=fieldOffset) {
continue;
} else if(pvField->getField()->getType()==structure) {
current = static_cast<PVStructure *>(pvField.get());
goto recurse;
}
}
// the first test against current->getNextFieldOffset() would avoid this
throw std::logic_error("PVStructure.getSubField: Logic error");
}
PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
PVFieldPtr PVStructure::getSubFieldT(std::size_t fieldOffset) const
{
PVFieldPtr pvField = getSubField(fieldOffset);
if (pvField.get())
return pvField;
else
{
std::stringstream ss;
ss << "Failed to get field with offset "
<< fieldOffset << "(Invalid offset)" ;
throw std::runtime_error(ss.str());
}
}
PVField* PVStructure::getSubFieldImpl(const char *name, bool throws) const
{
const PVStructure *parent = this;
if(!name)
@@ -115,7 +154,7 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
if (throws)
throw std::invalid_argument("Failed to get field: (Field name is NULL string)");
else
return PVFieldPtr();
return NULL;
}
const char *fullName = name;
while(true) {
@@ -131,7 +170,7 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
throw std::runtime_error(ss.str());
}
else
return PVFieldPtr();
return NULL;
}
size_t N = sep-name;
if(N==0)
@@ -144,7 +183,7 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
throw std::runtime_error(ss.str());
}
else
return PVFieldPtr();
return NULL;
}
const PVFieldPtrArray& pvFields = parent->getPVFields();
@@ -168,10 +207,10 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
std::stringstream ss;
ss << "Failed to get field: " << fullName << " ("
<< std::string(fullName, sep) << " not found)";
throw std::runtime_error(ss.str());
throw std::runtime_error(ss.str());
}
else
return PVFieldPtr();
return NULL;
}
if(*sep) {
@@ -184,36 +223,22 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
std::stringstream ss;
ss << "Failed to get field: " << fullName
<< " (" << std::string(fullName, sep)
<< " is not a structure)";
<< " is not a structure)";
throw std::runtime_error(ss.str());
}
else
return PVFieldPtr();
return NULL;
}
child = NULL;
name = sep+1; // skip past '.'
// loop around to new parent
} else {
return child->shared_from_this();
return child;
}
}
}
void PVStructure::throwBadFieldType(const char *name)
{
std::ostringstream ss;
ss << "Failed to get field: " << name << " (Field has wrong type)";
throw std::runtime_error(ss.str());
}
void PVStructure::throwBadFieldType(std::size_t fieldOffset)
{
std::stringstream ss;
ss << "Failed to get field with offset "
<< fieldOffset << " (Field has wrong type)";
throw std::runtime_error(ss.str());
}
void PVStructure::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const {
@@ -247,7 +272,7 @@ void PVStructure::serialize(ByteBuffer *pbuffer,
size_t fieldsSize = pvFields.size();
for(size_t i = 0; i<fieldsSize; i++) {
PVField* pvField = pvFields[i].get();
PVFieldPtr pvField = pvFields[i];
offset = pvField->getFieldOffset();
int32 inumberFields = static_cast<int32>(pvField->getNumberFields());
next = pbitSet->nextSetBit(static_cast<uint32>(offset));
@@ -261,7 +286,8 @@ void PVStructure::serialize(ByteBuffer *pbuffer,
if(inumberFields==1) {
pvField->serialize(pbuffer, pflusher);
} else {
static_cast<PVStructure*>(pvField)->serialize(pbuffer, pflusher, pbitSet);
PVStructurePtr pvStructure = std::tr1::static_pointer_cast<PVStructure>(pvField);
pvStructure->serialize(pbuffer, pflusher, pbitSet);
}
}
}
@@ -307,22 +333,22 @@ std::ostream& PVStructure::dumpValue(std::ostream& o) const
o << format::indent() << getStructure()->getID() << ' ' << getFieldName();
o << std::endl;
{
format::indent_scope s(o);
format::indent_scope s(o);
PVFieldPtrArray const & fieldsData = getPVFields();
if (fieldsData.size() != 0) {
size_t length = getStructure()->getNumberFields();
for(size_t i=0; i<length; i++) {
PVFieldPtr fieldField = fieldsData[i];
Type type = fieldField->getField()->getType();
if (type == scalar || type == scalarArray)
o << format::indent() << fieldField->getField()->getID() << ' ' << fieldField->getFieldName() << ' ' << *(fieldField.get()) << std::endl;
else
o << *(fieldField.get());
}
}
PVFieldPtrArray const & fieldsData = getPVFields();
if (fieldsData.size() != 0) {
size_t length = getStructure()->getNumberFields();
for(size_t i=0; i<length; i++) {
PVFieldPtr fieldField = fieldsData[i];
Type type = fieldField->getField()->getType();
if (type == scalar || type == scalarArray)
o << format::indent() << fieldField->getField()->getID() << ' ' << fieldField->getFieldName() << ' ' << *(fieldField.get()) << std::endl;
else
o << *(fieldField.get());
}
}
}
return o;
return o;
}

View File

@@ -76,14 +76,14 @@ void PVStructureArray::compress() {
size_t newLength = 0;
for(size_t i=0; i<length; i++) {
if(vec[i]) {
if(vec[i].get()!=NULL) {
newLength++;
continue;
}
// find first non 0
size_t notNull = 0;
for(size_t j=i+1;j<length;j++) {
if(vec[j]) {
if(vec[j].get()!=NULL) {
notNull = j;
break;
}
@@ -218,10 +218,10 @@ std::ostream& PVStructureArray::dumpValue(std::ostream& o) const
size_t length = getLength();
if (length > 0)
{
format::indent_scope s(o);
format::indent_scope s(o);
for (size_t i = 0; i < length; i++)
dumpValue(o, i);
dumpValue(o, i);
}
return o;

View File

@@ -23,9 +23,9 @@ using std::size_t;
using std::string;
namespace epics { namespace pvData {
#define PVUNION_UNDEFINED_INDEX -1
const int32 PVUnion::UNDEFINED_INDEX = PVUNION_UNDEFINED_INDEX;
int32 PVUnion::UNDEFINED_INDEX = PVUNION_UNDEFINED_INDEX;
PVDataCreatePtr PVUnion::pvDataCreate(getPVDataCreate());
@@ -40,7 +40,24 @@ PVUnion::PVUnion(UnionConstPtr const & unionPtr)
#undef PVUNION_UNDEFINED_INDEX
PVUnion::~PVUnion() {}
PVUnion::~PVUnion()
{
}
UnionConstPtr PVUnion::getUnion() const
{
return unionPtr;
}
PVFieldPtr PVUnion::get() const
{
return value;
}
int32 PVUnion::getSelectedIndex() const
{
return selector;
}
string PVUnion::getSelectedFieldName() const
{
@@ -53,11 +70,8 @@ string PVUnion::getSelectedFieldName() const
PVFieldPtr PVUnion::select(int32 index)
{
if (variant && index != UNDEFINED_INDEX)
throw std::invalid_argument("index out of bounds");
// no change
if (selector == index && !variant)
if (selector == index)
return value;
if (index == UNDEFINED_INDEX)
@@ -66,7 +80,9 @@ PVFieldPtr PVUnion::select(int32 index)
value.reset();
return value;
}
else if (index < 0 || size_t(index) >= unionPtr->getFields().size())
else if (variant)
throw std::invalid_argument("index out of bounds");
else if (index < 0 || index > static_cast<int32>(unionPtr->getFields().size()))
throw std::invalid_argument("index out of bounds");
FieldConstPtr field = unionPtr->getField(index);
@@ -75,13 +91,18 @@ PVFieldPtr PVUnion::select(int32 index)
return value;
}
PVFieldPtr PVUnion::select(string const & fieldName)
{
int32 index = variant ? -1 : static_cast<int32>(unionPtr->getFieldIndex(fieldName));
if (index == -1)
if (index == -1)
throw std::invalid_argument("no such fieldName");
return select(index);
return select(index);
}
void PVUnion::set(PVFieldPtr const & value)
{
set(selector, value);
}
void PVUnion::set(int32 index, PVFieldPtr const & value)
@@ -93,15 +114,15 @@ void PVUnion::set(int32 index, PVFieldPtr const & value)
if (index == UNDEFINED_INDEX)
{
// for undefined index we accept only null values
if (value)
throw std::invalid_argument("non-null value for index == UNDEFINED_INDEX");
if (value.get())
throw std::invalid_argument("non-null value for index == UNDEFINED_INDEX");
}
else if (index < 0 || size_t(index) >= unionPtr->getFields().size())
else if (index < 0 || index > static_cast<int32>(unionPtr->getFields().size()))
throw std::invalid_argument("index out of bounds");
else if (!value)
throw std::invalid_argument("Can't set defined index w/ NULL");
else if (value->getField() != unionPtr->getField(index))
throw std::invalid_argument("selected field and its introspection data do not match");
// value type must match
if (value->getField() != unionPtr->getField(index))
throw std::invalid_argument("selected field and its introspection data do not match");
}
selector = index;
@@ -115,7 +136,7 @@ void PVUnion::set(string const & fieldName, PVFieldPtr const & value)
if (index == -1)
throw std::invalid_argument("no such fieldName");
set(index, value);
set(index, value);
}
void PVUnion::serialize(ByteBuffer *pbuffer, SerializableControl *pflusher) const
@@ -136,7 +157,7 @@ void PVUnion::serialize(ByteBuffer *pbuffer, SerializableControl *pflusher) cons
// write selector value
SerializeHelper::writeSize(selector, pbuffer, pflusher);
// write value, no value for UNDEFINED_INDEX
if (selector != UNDEFINED_INDEX)
if (selector != UNDEFINED_INDEX)
value->serialize(pbuffer, pflusher);
}
}
@@ -180,20 +201,20 @@ std::ostream& PVUnion::dumpValue(std::ostream& o) const
{
o << format::indent() << getUnion()->getID() << ' ' << getFieldName() << std::endl;
{
format::indent_scope s(o);
const PVField::const_shared_pointer& fieldField = get();
if (fieldField.get() == NULL)
o << format::indent() << "(none)" << std::endl;
else
{
Type type = fieldField->getField()->getType();
if (type == scalar || type == scalarArray)
o << format::indent() << fieldField->getField()->getID() << ' ' << fieldField->getFieldName() << ' ' << *(fieldField.get()) << std::endl;
else
o << *(fieldField.get());
}
format::indent_scope s(o);
PVFieldPtr fieldField = get();
if (fieldField.get() == NULL)
o << format::indent() << "(none)" << std::endl;
else
{
Type type = fieldField->getField()->getType();
if (type == scalar || type == scalarArray)
o << format::indent() << fieldField->getField()->getID() << ' ' << fieldField->getFieldName() << ' ' << *(fieldField.get()) << std::endl;
else
o << *(fieldField.get());
}
}
return o;
return o;
}
void PVUnion::copy(const PVUnion& from)
@@ -210,7 +231,7 @@ void PVUnion::copy(const PVUnion& from)
void PVUnion::copyUnchecked(const PVUnion& from)
{
const PVField::const_shared_pointer& fromValue = from.get();
PVFieldPtr fromValue = from.get();
if (from.getUnion()->isVariant())
{
if (fromValue.get() == 0)

View File

@@ -76,14 +76,14 @@ void PVUnionArray::compress() {
size_t newLength = 0;
for(size_t i=0; i<length; i++) {
if(vec[i]) {
if(vec[i].get()!=NULL) {
newLength++;
continue;
}
// find first non 0
size_t notNull = 0;
for(size_t j=i+1;j<length;j++) {
if(vec[j]) {
if(vec[j].get()!=NULL) {
notNull = j;
break;
}
@@ -217,10 +217,10 @@ std::ostream& PVUnionArray::dumpValue(std::ostream& o) const
size_t length = getLength();
if (length > 0)
{
format::indent_scope s(o);
format::indent_scope s(o);
for (size_t i = 0; i < length; i++)
dumpValue(o, i);
dumpValue(o, i);
}
return o;

View File

@@ -11,7 +11,6 @@
#include <stdexcept>
#include <epicsMutex.h>
#include <epicsThread.h>
#define epicsExportSharedSymbols
#include <pv/lock.h>
@@ -21,88 +20,35 @@
using std::tr1::static_pointer_cast;
using std::string;
namespace epics { namespace pvData {
namespace epics { namespace pvData {
static
StructureConstPtr buildValueAlarm(ScalarType vtype)
{
return FieldBuilder::begin()
->setId("valueAlarm_t")
->add("active", pvBoolean)
->add("lowAlarmLimit", vtype)
->add("lowWarningLimit", vtype)
->add("highWarningLimit", vtype)
->add("highAlarmLimit", vtype)
->add("lowAlarmSeverity", pvInt)
->add("lowWarningSeverity", pvInt)
->add("highWarningSeverity", pvInt)
->add("highAlarmSeverity", pvInt)
->add("hysteresis", pvByte)
->createStructure();
}
StandardField::StandardField()
:fieldCreate(getFieldCreate())
,notImplemented("not implemented")
,valueFieldName("value")
,alarmField(FieldBuilder::begin()
->setId("alarm_t")
->add("severity", pvInt)
->add("status", pvInt)
->add("message", pvString)
->createStructure())
,timeStampField(FieldBuilder::begin()
->setId("time_t")
->add("secondsPastEpoch", pvLong)
->add("nanoseconds", pvInt)
->add("userTag", pvInt)
->createStructure())
,displayField(FieldBuilder::begin()
->setId("display_t")
->add("limitLow", pvDouble)
->add("limitHigh", pvDouble)
->add("description", pvString)
->add("format", pvString)
->add("units", pvString)
->createStructure())
,controlField(FieldBuilder::begin()
->setId("control_t")
->add("limitLow", pvDouble)
->add("limitHigh", pvDouble)
->add("minStep", pvDouble)
->createStructure())
,booleanAlarmField(FieldBuilder::begin()
->setId("valueAlarm_t")
->add("active", pvBoolean)
->add("falseSeverity", pvInt)
->add("trueSeverity", pvInt)
->add("changeStateSeverity", pvInt)
->createStructure())
,byteAlarmField(buildValueAlarm(pvByte))
,shortAlarmField(buildValueAlarm(pvShort))
,intAlarmField(buildValueAlarm(pvInt))
,longAlarmField(buildValueAlarm(pvLong))
,ubyteAlarmField(buildValueAlarm(pvUByte))
,ushortAlarmField(buildValueAlarm(pvUShort))
,uintAlarmField(buildValueAlarm(pvUInt))
,ulongAlarmField(buildValueAlarm(pvULong))
,floatAlarmField(buildValueAlarm(pvFloat))
,doubleAlarmField(buildValueAlarm(pvDouble))
,enumeratedAlarmField(FieldBuilder::begin()
->setId("valueAlarm_t")
->add("active", pvBoolean)
->add("stateSeverity", pvInt)
->add("changeStateSeverity", pvInt)
->createStructure())
: fieldCreate(getFieldCreate()),
notImplemented("not implemented"),
valueFieldName("value")
{}
void StandardField::init()
{
createAlarm();
createTimeStamp();
createDisplay();
createControl();
createBooleanAlarm();
createByteAlarm();
createShortAlarm();
createIntAlarm();
createLongAlarm();
createUByteAlarm();
createUShortAlarm();
createUIntAlarm();
createULongAlarm();
createFloatAlarm();
createDoubleAlarm();
createEnumeratedAlarm();
}
StandardField::~StandardField(){}
StructureConstPtr StandardField::createProperties(string id,FieldConstPtr field,string properties)
@@ -123,8 +69,8 @@ StructureConstPtr StandardField::createProperties(string id,FieldConstPtr field,
while(gotValueAlarm) {
if(type==epics::pvData::scalar || type==epics::pvData::scalarArray) {
ScalarType scalarType = (type==epics::pvData::scalar) ?
static_pointer_cast<const Scalar>(field)->getScalarType() :
static_pointer_cast<const ScalarArray>(field)->getElementType();
static_pointer_cast<const Scalar>(field)->getScalarType() :
static_pointer_cast<const ScalarArray>(field)->getElementType();
switch(scalarType) {
case pvBoolean: valueAlarm = booleanAlarmField; break;
case pvByte: valueAlarm = byteAlarmField; break;
@@ -157,7 +103,7 @@ StructureConstPtr StandardField::createProperties(string id,FieldConstPtr field,
if(first->getType()==epics::pvData::scalar
&& second->getType()==epics::pvData::scalarArray) {
ScalarConstPtr scalarFirst = static_pointer_cast<const Scalar>(first);
ScalarArrayConstPtr scalarArraySecond =
ScalarArrayConstPtr scalarArraySecond =
static_pointer_cast<const ScalarArray>(second);
if(scalarFirst->getScalarType()==pvInt
&& scalarArraySecond->getElementType()==pvString) {
@@ -199,6 +145,361 @@ StructureConstPtr StandardField::createProperties(string id,FieldConstPtr field,
return fieldCreate->createStructure(id,names,fields);
}
void StandardField::createAlarm() {
size_t num = 3;
FieldConstPtrArray fields(num);
StringArray names(num);
names[0] = "severity";
names[1] = "status";
names[2] = "message";
fields[0] = fieldCreate->createScalar(pvInt);
fields[1] = fieldCreate->createScalar(pvInt);
fields[2] = fieldCreate->createScalar(pvString);
alarmField = fieldCreate->createStructure("alarm_t",names,fields);
}
void StandardField::createTimeStamp() {
size_t num = 3;
FieldConstPtrArray fields(num);
StringArray names(num);
names[0] = "secondsPastEpoch";
names[1] = "nanoseconds";
names[2] = "userTag";
fields[0] = fieldCreate->createScalar(pvLong);
fields[1] = fieldCreate->createScalar(pvInt);
fields[2] = fieldCreate->createScalar(pvInt);
timeStampField = fieldCreate->createStructure("time_t",names,fields);
}
void StandardField::createDisplay() {
size_t num = 5;
FieldConstPtrArray fields(num);
StringArray names(num);
names[0] = "limitLow";
names[1] = "limitHigh";
names[2] = "description";
names[3] = "format";
names[4] = "units";
fields[0] = fieldCreate->createScalar(pvDouble);
fields[1] = fieldCreate->createScalar(pvDouble);
fields[2] = fieldCreate->createScalar(pvString);
fields[3] = fieldCreate->createScalar(pvString);
fields[4] = fieldCreate->createScalar(pvString);
displayField = fieldCreate->createStructure("display_t",names,fields);
}
void StandardField::createControl() {
size_t num = 3;
FieldConstPtrArray fields(num);
StringArray names(num);
names[0] = "limitLow";
names[1] = "limitHigh";
names[2] = "minStep";
fields[0] = fieldCreate->createScalar(pvDouble);
fields[1] = fieldCreate->createScalar(pvDouble);
fields[2] = fieldCreate->createScalar(pvDouble);
controlField = fieldCreate->createStructure("control_t",names,fields);
}
void StandardField::createBooleanAlarm() {
size_t numFields = 4;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "falseSeverity";
names[2] = "trueSeverity";
names[3] = "changeStateSeverity";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalar(pvInt);
fields[2] = fieldCreate->createScalar(pvInt);
fields[3] = fieldCreate->createScalar(pvInt);
booleanAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
void StandardField::createByteAlarm() {
size_t numFields = 10;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "lowAlarmLimit";
names[2] = "lowWarningLimit";
names[3] = "highWarningLimit";
names[4] = "highAlarmLimit";
names[5] = "lowAlarmSeverity";
names[6] = "lowWarningSeverity";
names[7] = "highWarningSeverity";
names[8] = "highAlarmSeverity";
names[9] = "hysteresis";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalar(pvByte);
fields[2] = fieldCreate->createScalar(pvByte);
fields[3] = fieldCreate->createScalar(pvByte);
fields[4] = fieldCreate->createScalar(pvByte);
fields[5] = fieldCreate->createScalar(pvInt);
fields[6] = fieldCreate->createScalar(pvInt);
fields[7] = fieldCreate->createScalar(pvInt);
fields[8] = fieldCreate->createScalar(pvInt);
fields[9] = fieldCreate->createScalar(pvByte);
byteAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
void StandardField::createShortAlarm() {
size_t numFields = 10;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "lowAlarmLimit";
names[2] = "lowWarningLimit";
names[3] = "highWarningLimit";
names[4] = "highAlarmLimit";
names[5] = "lowAlarmSeverity";
names[6] = "lowWarningSeverity";
names[7] = "highWarningSeverity";
names[8] = "highAlarmSeverity";
names[9] = "hysteresis";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalar(pvShort);
fields[2] = fieldCreate->createScalar(pvShort);
fields[3] = fieldCreate->createScalar(pvShort);
fields[4] = fieldCreate->createScalar(pvShort);
fields[5] = fieldCreate->createScalar(pvInt);
fields[6] = fieldCreate->createScalar(pvInt);
fields[7] = fieldCreate->createScalar(pvInt);
fields[8] = fieldCreate->createScalar(pvInt);
fields[9] = fieldCreate->createScalar(pvShort);
shortAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
void StandardField::createIntAlarm() {
size_t numFields = 10;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "lowAlarmLimit";
names[2] = "lowWarningLimit";
names[3] = "highWarningLimit";
names[4] = "highAlarmLimit";
names[5] = "lowAlarmSeverity";
names[6] = "lowWarningSeverity";
names[7] = "highWarningSeverity";
names[8] = "highAlarmSeverity";
names[9] = "hysteresis";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalar(pvInt);
fields[2] = fieldCreate->createScalar(pvInt);
fields[3] = fieldCreate->createScalar(pvInt);
fields[4] = fieldCreate->createScalar(pvInt);
fields[5] = fieldCreate->createScalar(pvInt);
fields[6] = fieldCreate->createScalar(pvInt);
fields[7] = fieldCreate->createScalar(pvInt);
fields[8] = fieldCreate->createScalar(pvInt);
fields[9] = fieldCreate->createScalar(pvInt);
intAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
void StandardField::createLongAlarm() {
size_t numFields = 10;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "lowAlarmLimit";
names[2] = "lowWarningLimit";
names[3] = "highWarningLimit";
names[4] = "highAlarmLimit";
names[5] = "lowAlarmSeverity";
names[6] = "lowWarningSeverity";
names[7] = "highWarningSeverity";
names[8] = "highAlarmSeverity";
names[9] = "hysteresis";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalar(pvLong);
fields[2] = fieldCreate->createScalar(pvLong);
fields[3] = fieldCreate->createScalar(pvLong);
fields[4] = fieldCreate->createScalar(pvLong);
fields[5] = fieldCreate->createScalar(pvInt);
fields[6] = fieldCreate->createScalar(pvInt);
fields[7] = fieldCreate->createScalar(pvInt);
fields[8] = fieldCreate->createScalar(pvInt);
fields[9] = fieldCreate->createScalar(pvLong);
longAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
void StandardField::createUByteAlarm() {
size_t numFields = 10;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "lowAlarmLimit";
names[2] = "lowWarningLimit";
names[3] = "highWarningLimit";
names[4] = "highAlarmLimit";
names[5] = "lowAlarmSeverity";
names[6] = "lowWarningSeverity";
names[7] = "highWarningSeverity";
names[8] = "highAlarmSeverity";
names[9] = "hysteresis";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalar(pvUByte);
fields[2] = fieldCreate->createScalar(pvUByte);
fields[3] = fieldCreate->createScalar(pvUByte);
fields[4] = fieldCreate->createScalar(pvUByte);
fields[5] = fieldCreate->createScalar(pvInt);
fields[6] = fieldCreate->createScalar(pvInt);
fields[7] = fieldCreate->createScalar(pvInt);
fields[8] = fieldCreate->createScalar(pvInt);
fields[9] = fieldCreate->createScalar(pvUByte);
ubyteAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
void StandardField::createUShortAlarm() {
size_t numFields = 10;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "lowAlarmLimit";
names[2] = "lowWarningLimit";
names[3] = "highWarningLimit";
names[4] = "highAlarmLimit";
names[5] = "lowAlarmSeverity";
names[6] = "lowWarningSeverity";
names[7] = "highWarningSeverity";
names[8] = "highAlarmSeverity";
names[9] = "hysteresis";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalar(pvUShort);
fields[2] = fieldCreate->createScalar(pvUShort);
fields[3] = fieldCreate->createScalar(pvUShort);
fields[4] = fieldCreate->createScalar(pvUShort);
fields[5] = fieldCreate->createScalar(pvInt);
fields[6] = fieldCreate->createScalar(pvInt);
fields[7] = fieldCreate->createScalar(pvInt);
fields[8] = fieldCreate->createScalar(pvInt);
fields[9] = fieldCreate->createScalar(pvUShort);
ushortAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
void StandardField::createUIntAlarm() {
size_t numFields = 10;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "lowAlarmLimit";
names[2] = "lowWarningLimit";
names[3] = "highWarningLimit";
names[4] = "highAlarmLimit";
names[5] = "lowAlarmSeverity";
names[6] = "lowWarningSeverity";
names[7] = "highWarningSeverity";
names[8] = "highAlarmSeverity";
names[9] = "hysteresis";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalar(pvUInt);
fields[2] = fieldCreate->createScalar(pvUInt);
fields[3] = fieldCreate->createScalar(pvUInt);
fields[4] = fieldCreate->createScalar(pvUInt);
fields[5] = fieldCreate->createScalar(pvInt);
fields[6] = fieldCreate->createScalar(pvInt);
fields[7] = fieldCreate->createScalar(pvInt);
fields[8] = fieldCreate->createScalar(pvInt);
fields[9] = fieldCreate->createScalar(pvUInt);
uintAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
void StandardField::createULongAlarm() {
size_t numFields = 10;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "lowAlarmLimit";
names[2] = "lowWarningLimit";
names[3] = "highWarningLimit";
names[4] = "highAlarmLimit";
names[5] = "lowAlarmSeverity";
names[6] = "lowWarningSeverity";
names[7] = "highWarningSeverity";
names[8] = "highAlarmSeverity";
names[9] = "hysteresis";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalar(pvULong);
fields[2] = fieldCreate->createScalar(pvULong);
fields[3] = fieldCreate->createScalar(pvULong);
fields[4] = fieldCreate->createScalar(pvULong);
fields[5] = fieldCreate->createScalar(pvInt);
fields[6] = fieldCreate->createScalar(pvInt);
fields[7] = fieldCreate->createScalar(pvInt);
fields[8] = fieldCreate->createScalar(pvInt);
fields[9] = fieldCreate->createScalar(pvULong);
ulongAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
void StandardField::createFloatAlarm() {
size_t numFields = 10;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "lowAlarmLimit";
names[2] = "lowWarningLimit";
names[3] = "highWarningLimit";
names[4] = "highAlarmLimit";
names[5] = "lowAlarmSeverity";
names[6] = "lowWarningSeverity";
names[7] = "highWarningSeverity";
names[8] = "highAlarmSeverity";
names[9] = "hysteresis";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalar(pvFloat);
fields[2] = fieldCreate->createScalar(pvFloat);
fields[3] = fieldCreate->createScalar(pvFloat);
fields[4] = fieldCreate->createScalar(pvFloat);
fields[5] = fieldCreate->createScalar(pvInt);
fields[6] = fieldCreate->createScalar(pvInt);
fields[7] = fieldCreate->createScalar(pvInt);
fields[8] = fieldCreate->createScalar(pvInt);
fields[9] = fieldCreate->createScalar(pvFloat);
floatAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
void StandardField::createDoubleAlarm() {
size_t numFields = 10;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "lowAlarmLimit";
names[2] = "lowWarningLimit";
names[3] = "highWarningLimit";
names[4] = "highAlarmLimit";
names[5] = "lowAlarmSeverity";
names[6] = "lowWarningSeverity";
names[7] = "highWarningSeverity";
names[8] = "highAlarmSeverity";
names[9] = "hysteresis";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalar(pvDouble);
fields[2] = fieldCreate->createScalar(pvDouble);
fields[3] = fieldCreate->createScalar(pvDouble);
fields[4] = fieldCreate->createScalar(pvDouble);
fields[5] = fieldCreate->createScalar(pvInt);
fields[6] = fieldCreate->createScalar(pvInt);
fields[7] = fieldCreate->createScalar(pvInt);
fields[8] = fieldCreate->createScalar(pvInt);
fields[9] = fieldCreate->createScalar(pvDouble);
doubleAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
void StandardField::createEnumeratedAlarm() {
size_t numFields = 3;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "stateSeverity";
names[2] = "changeStateSeverity";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalarArray(pvInt);
fields[2] = fieldCreate->createScalar(pvInt);
enumeratedAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
StructureConstPtr StandardField::scalar(
ScalarType type,string const &properties)
{
@@ -263,21 +564,104 @@ StructureConstPtr StandardField::enumerated(string const &properties)
return createProperties("epics:nt/NTEnum:1.0",field,properties);
}
static StandardFieldPtr *stdFieldGbl;
static epicsThreadOnceId stdFieldGblOnce = EPICS_THREAD_ONCE_INIT;
void StandardField::once(void*)
StructureConstPtr StandardField::alarm()
{
stdFieldGbl = new StandardFieldPtr;
stdFieldGbl->reset(new StandardField);
return alarmField;
}
const StandardFieldPtr &StandardField::getStandardField()
StructureConstPtr StandardField::timeStamp()
{
epicsThreadOnce(&stdFieldGblOnce, &StandardField::once, 0);
return timeStampField;
}
return *stdFieldGbl;
StructureConstPtr StandardField::display()
{
return displayField;
}
StructureConstPtr StandardField::control()
{
return controlField;
}
StructureConstPtr StandardField::booleanAlarm()
{
return booleanAlarmField;
}
StructureConstPtr StandardField::byteAlarm()
{
return byteAlarmField;
}
StructureConstPtr StandardField::ubyteAlarm()
{
return ubyteAlarmField;
}
StructureConstPtr StandardField::shortAlarm()
{
return shortAlarmField;
}
StructureConstPtr StandardField::ushortAlarm()
{
return ushortAlarmField;
}
StructureConstPtr StandardField::intAlarm()
{
return intAlarmField;
}
StructureConstPtr StandardField::uintAlarm()
{
return uintAlarmField;
}
StructureConstPtr StandardField::longAlarm()
{
return longAlarmField;
}
StructureConstPtr StandardField::ulongAlarm()
{
return ulongAlarmField;
}
StructureConstPtr StandardField::floatAlarm()
{
return floatAlarmField;
}
StructureConstPtr StandardField::doubleAlarm()
{
return doubleAlarmField;
}
StructureConstPtr StandardField::enumeratedAlarm()
{
return enumeratedAlarmField;
}
StandardFieldPtr StandardField::getStandardField()
{
static StandardFieldPtr standardFieldCreate;
static Mutex mutex;
Lock xx(mutex);
if(standardFieldCreate.get()==0)
{
standardFieldCreate = StandardFieldPtr(new StandardField());
standardFieldCreate->init();
}
return standardFieldCreate;
}
StandardFieldPtr getStandardField() {
return StandardField::getStandardField();
}
}}

View File

@@ -20,7 +20,7 @@
using std::string;
namespace epics { namespace pvData {
namespace epics { namespace pvData {
StandardPVField::StandardPVField()
: standardField(getStandardField()),

View File

@@ -59,7 +59,7 @@ namespace ScalarTypeFunc {
if(type>=pvBoolean && type<=pvDouble) return true;
return false;
}
static const char* names[] = {
"boolean",
"byte", "short", "int", "long",

View File

@@ -3,549 +3,42 @@
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <stdio.h>
#include <ctype.h>
#if defined(__linux__) || defined(__APPLE__)
#include <unistd.h>
#define HAVE_ISATTY
#endif
#include <deque>
#include <epicsTime.h>
#include <epicsString.h>
#define epicsExportSharedSymbols
#include <pv/bitSet.h>
#include <pv/pvData.h>
#include <pv/json.h>
#include <pv/pvIntrospect.h>
using std::string;
namespace epics { namespace pvData {
namespace format
{
static int indent_index = std::ios_base::xalloc();
long& indent_value(std::ios_base& ios)
{
return ios.iword(indent_index);
}
std::ostream& operator<<(std::ostream& os, indent_level const& indent)
{
indent_value(os) = indent.level;
return os;
}
std::ostream& operator<<(std::ostream& os, indent const&)
{
long il = indent_value(os);
for(long i=0, spaces = il * 4; i<spaces; i++)
os.put(' ');
return os;
}
array_at_internal operator<<(std::ostream& str, array_at const& manip)
{
return array_at_internal(manip.index, str);
}
}
}} //epics::pvData
namespace {
using namespace epics::pvData;
bool useEscapes(std::ostream& strm) {
FILE *fp = 0;
// TODO: a better way to do this...
if(&std::cout==&strm)
fp = stdout;
if(&std::cerr==&strm)
fp = stderr;
if(!fp) return false;
#ifdef HAVE_ISATTY
// check $TERM as well?
return isatty(fileno(fp))==1;
#else
// TODO: in theory windows 10 can be made to understand escapes as well
return false;
#endif
}
void printAlarmTx(std::ostream& strm, const PVStructure& sub)
{
PVScalar::const_shared_pointer pvSeverity(sub.getSubField<PVInt>("severity"));
PVScalar::const_shared_pointer pvStatus(sub.getSubField<PVInt>("status"));
PVString::const_shared_pointer pvMessage(sub.getSubField<PVString>("message"));
switch(pvSeverity ? pvSeverity->getAs<int32>() : 0) {
case 0: return; // nothing more to do here...
case 1: strm<<"MINOR "; break;
case 2: strm<<"MAJOR "; break;
case 3: strm<<"INVALID "; break;
case 4: strm<<"UNDEFINED "; break;
default: strm<<pvSeverity->getAs<int32>()<<' ';
}
switch(pvStatus ? pvStatus->getAs<int32>() : 0) {
case 0: break;
case 1: strm<<"DEVICE "; break;
case 2: strm<<"DRIVER "; break;
case 3: strm<<"RECORD "; break;
case 4: strm<<"DB "; break;
case 5: strm<<"CONF "; break;
case 6: strm<<"UNDEFINED "; break;
case 7: strm<<"CLIENT "; break;
default: strm<<pvStatus->getAs<int32>()<<' ';
}
if(pvMessage && !pvMessage->get().empty())
strm<<pvMessage->get()<<' ';
}
void printAlarmT(std::ostream& strm, const PVStructure& top)
{
PVStructure::const_shared_pointer sub(top.getSubField<PVStructure>("alarm"));
if(sub)
printAlarmTx(strm, *sub);
}
void printTimeTx(std::ostream& strm, const PVStructure& tsubop)
{
char timeText[32];
epicsTimeStamp epicsTS;
PVScalar::const_shared_pointer secf(tsubop.getSubField<PVScalar>("secondsPastEpoch")),
nsecf(tsubop.getSubField<PVScalar>("nanoseconds")),
tagf(tsubop.getSubField<PVScalar>("userTag"));
epicsTS.secPastEpoch = secf ? secf->getAs<int64>() : 0;
epicsTS.nsec = nsecf ? nsecf->getAs<int32>() : 0;
if(epicsTS.secPastEpoch > POSIX_TIME_AT_EPICS_EPOCH)
epicsTS.secPastEpoch -= POSIX_TIME_AT_EPICS_EPOCH;
else
epicsTS.secPastEpoch = 0;
epicsTimeToStrftime(timeText, sizeof(timeText), "%Y-%m-%d %H:%M:%S.%03f", &epicsTS);
strm <<std::setw(24) <<std::left <<timeText <<' ';
if (tagf) {
int64 tagv = tagf->getAs<int64>();
if(tagv)
strm << tagv << ' ';
}
}
void printTimeT(std::ostream& strm, const PVStructure& top)
{
PVStructure::const_shared_pointer sub(top.getSubField<PVStructure>("timeStamp"));
if(sub)
printTimeTx(strm, *sub);
}
bool printEnumT(std::ostream& strm, const PVStructure& top, bool fromtop)
{
PVStructure::const_shared_pointer value;
if(fromtop) {
value = top.getSubField<PVStructure>("value");
} else {
value = std::tr1::static_pointer_cast<const PVStructure>(top.shared_from_this());
}
PVScalar::const_shared_pointer idx(value->getSubField<PVScalar>("index"));
PVStringArray::const_shared_pointer choices(value->getSubField<PVStringArray>("choices"));
if(!idx || !choices) return false;
if(fromtop) {
strm<<format::indent();
printTimeT(strm, top);
printAlarmT(strm, top);
}
PVStringArray::const_svector ch(choices->view());
uint32 I = idx->getAs<uint32>();
strm<<'('<<I<<')';
if(I>=ch.size()) {
strm<<" <undefined>";
} else {
strm<<' '<<maybeQuote(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.swap(temp);
}
bool printTable(std::ostream& strm, const PVStructure& top)
{
PVStructure::const_shared_pointer cf(top.getSubField<PVStructure>("value"));
if(!cf) return false;
static int indent_index = std::ios_base::xalloc();
long& indent_value(std::ios_base& ios)
{
const FieldConstPtrArray& fields = cf->getStructure()->getFields();
if(fields.empty()) return false;
for(size_t i=0, N=fields.size(); i<N; i++) {
if(fields[i]->getType()!=scalarArray)
return false; // cowardly refusing to handle anything else...
}
return ios.iword(indent_index);
}
// maybe output a line with meta-data
{
bool havets = !!top.getSubField("timeStamp");
bool haveal = !!top.getSubField("alarm");
if(havets || haveal)
strm<<format::indent();
if(havets) {
printTimeT(strm, top);
strm<<' ';
}
if(haveal) {
printAlarmT(strm, top);
strm<<' ';
}
strm<<'\n';
}
std::ostream& operator<<(std::ostream& os, indent_level const& indent)
{
indent_value(os) = indent.level;
return os;
}
PVStringArray::svector labels;
{
PVStringArray::const_shared_pointer lf(top.getSubField<PVStringArray>("labels"));
if(lf) {
PVStringArray::const_svector L(lf->view());
labels = thaw(L);
}
}
std::ostream& operator<<(std::ostream& os, indent const&)
{
long il = indent_value(os);
std::size_t spaces = static_cast<std::size_t>(il) * 4;
return os << string(spaces, ' ');
}
const PVFieldPtrArray& columns = cf->getPVFields();
std::vector<shared_vector<std::string> > coldat(columns.size());
array_at_internal operator<<(std::ostream& str, array_at const& manip)
{
return array_at_internal(manip.index, str);
}
};
std::vector<size_t> widths(columns.size());
labels.reserve(columns.size());
size_t nrows = size_t(-1);
for(size_t i=0, N=columns.size(); i<N; i++) {
if(i>=labels.size()) {
labels.push_back(cf->getStructure()->getFieldName(i));
} else {
csvEscape(labels[i]);
}
widths[i] = labels[i].size();
{
shared_vector<const std::string> ro;
static_cast<const PVScalarArray*>(columns[i].get())->getAs(ro);
coldat[i] = thaw(ro);
}
// truncate if some columns are longer than others
nrows = std::min(nrows, coldat[i].size());
for(size_t j=0, M=coldat[i].size(); j<M; j++) {
csvEscape(coldat[i][j]);
widths[i] = std::max(widths[i], coldat[i][j].size());
}
}
// output header line
strm<<format::indent();
for(size_t c=0, N=coldat.size(); c<N; c++) {
strm<<std::setw(widths[c])<<std::right<<labels[c];
if(c+1!=N) {
strm<<' ';
}
}
strm<<'\n';
for(size_t r=0; r<nrows; r++) {
strm<<format::indent();
for(size_t c=0, N=coldat.size(); c<N; c++) {
strm<<std::setw(widths[c])<<std::right<<coldat[c][r];
if(c+1!=N)
strm<<' ';
}
strm<<'\n';
}
return true;
}
void expandBS(const PVStructure& top, BitSet& mask, bool parents) {
if(mask.get(0)) { // special handling because getSubField(0) not allowed
// wildcard
for(size_t idx=1, N=top.getNumberFields(); idx<N; idx++) {
mask.set(idx);
}
} else {
for(int32 idx = mask.nextSetBit(0), N=top.getNumberFields(); idx>=0 && idx<N; idx=mask.nextSetBit(idx+1)) {
PVField::const_shared_pointer fld = top.getSubFieldT(idx);
// look forward and mark all children
for(size_t i=idx+1, N=fld->getNextFieldOffset(); i<N; i++)
mask.set(i);
if(parents) {
// look back and mark all parents
// we've already stepped past all parents so siblings will not be automatically marked
for(const PVStructure *parent = fld->getParent(); parent; parent = parent->getParent()) {
mask.set(parent->getFieldOffset());
}
}
}
}
}
}
namespace epics { namespace pvData {
void printRaw(std::ostream& strm, const PVStructure::Formatter& format, const PVStructure& cur)
{
typedef std::deque<std::pair<const PVField*, size_t> > todo_t;
todo_t todo;
BitSet show, highlight;
const long orig_indent = format::indent_value(strm);
{
if(format.xshow)
show = *format.xshow;
else
show.set(0);
if(format.xhighlight)
highlight = *format.xhighlight;
expandBS(format.xtop, show, true);
expandBS(format.xtop, highlight, false);
highlight &= show; // can't highlight hidden fields (paranoia)
}
if(!show.get(0)) return; // nothing to do here...
todo.push_front(std::make_pair(&format.xtop, orig_indent));
while(!todo.empty()) {
todo_t::value_type cur(todo.front());
todo.pop_front();
format::indent_value(strm) = cur.second;
bool hl = highlight.get(cur.first->getFieldOffset()) && format.xmode==PVStructure::Formatter::ANSI;
if(hl)
strm<<"\x1b[1m"; // Bold
switch(cur.first->getField()->getType()) {
case structure: {
const PVStructure* str = static_cast<const PVStructure*>(cur.first);
const PVFieldPtrArray& flds = str->getPVFields();
for(PVFieldPtrArray::const_reverse_iterator it(flds.rbegin()), end(flds.rend()); it!=end; ++it) {
const PVField *fld = (*it).get();
if(!show.get(fld->getFieldOffset())) continue;
todo.push_front(std::make_pair(fld, cur.second+1));
}
std::string id(cur.first->getField()->getID());
strm<<format::indent()<<id<<' '<<cur.first->getFieldName();
if(id=="alarm_t") {
strm.put(' ');
printAlarmTx(strm, *str);
} else if(id=="time_t") {
strm.put(' ');
printTimeTx(strm, *str);
} else if(id=="enum_t") {
strm.put(' ');
printEnumT(strm, *str, false);
}
strm.put('\n');
}
break;
case scalar:
case scalarArray:
strm<<format::indent()<<cur.first->getField()->getID()<<' '<<cur.first->getFieldName()
<<' '<<*cur.first
<<'\n';
break;
case structureArray:
case union_:
case unionArray:
strm<<*cur.first;
break;
}
if(hl)
strm<<"\x1b[0m"; // reset
}
format::indent_value(strm) = orig_indent;
}
std::ostream& operator<<(std::ostream& strm, const PVStructure::Formatter& format)
{
if(format.xfmt==PVStructure::Formatter::JSON) {
JSONPrintOptions opts;
opts.multiLine = false;
printJSON(strm, format.xtop, format.xshow ? *format.xshow : BitSet().set(0), opts);
strm<<'\n';
return strm;
} else if(format.xfmt==PVStructure::Formatter::NT) {
std::string id(format.xtop.getStructure()->getID()),
idprefix(id.substr(0, id.find_first_of('.')));
// NTTable
if(idprefix=="epics:nt/NTTable:1") {
if(printTable(strm, format.xtop))
return strm;
} else {
//NTScalar, NTScalarArray, NTEnum, or anything with '.value'
PVField::const_shared_pointer value(format.xtop.getSubField("value"));
if(value) {
switch(value->getField()->getType()) {
case scalar:
strm<<format::indent();
printTimeT(strm, format.xtop);
strm<<std::setprecision(6)<<*static_cast<const PVScalar*>(value.get())<<' ';
printAlarmT(strm, format.xtop);
strm<<'\n';
return strm;
case scalarArray:
strm<<format::indent();
printTimeT(strm, format.xtop);
printAlarmT(strm, format.xtop);
strm<<std::setprecision(6)<<*static_cast<const PVScalarArray*>(value.get())<<'\n';
return strm;
case structure:
if(printEnumT(strm, format.xtop, true)) {
strm<<'\n';
return strm;
}
break;
default:
break;
}
}
}
}
// fall through unhandled as Raw
PVStructure::Formatter format2(format);
if(format2.xmode==PVStructure::Formatter::Auto)
format2.xmode = useEscapes(strm) ? PVStructure::Formatter::ANSI : PVStructure::Formatter::Plain;
printRaw(strm, format2, format.xtop);
return strm;
}
static char hexdigit(char c) {
c &= 0xf;
if(c<9)
return '0'+c;
else
return 'A'+c-10;
}
escape::~escape() {}
std::string escape::str() const
{
std::ostringstream strm;
strm<<(*this);
return strm.str();
}
epicsShareFunc
std::ostream& operator<<(std::ostream& strm, const escape& Q)
{
for(size_t pos = 0, len = Q.orig.size(); pos < len; pos++) {
const char C = Q.orig[pos];
char quote = '\\', next;
// compare me with epicsStrnEscapedFromRaw()
switch(C) {
case '\a': next = 'a'; break;
case '\b': next = 'b'; break;
case '\f': next = 'f'; break;
case '\n': next = 'n'; break;
case '\r': next = 'r'; break;
case '\t': next = 't'; break;
case '\v': next = 'v'; break;
case '\\': next = '\\'; break;
case '\'': next = '\''; break;
case '\"': next = '\"'; if(Q.S==escape::CSV) quote = '"'; break;
default:
if(!isprint(C)) {
// print three charator escape
strm<<"\\x"<<hexdigit(C>>4)<<hexdigit(C);
} else {
// literal
strm.put(C);
}
continue;
}
// print two charactor escape
strm.put(quote);
strm.put(next);
}
return strm;
}
std::ostream& operator<<(std::ostream& strm, const maybeQuote& q)
{
bool esc = false;
for(size_t i=0, N=q.s.size(); i<N && !esc; i++) {
switch(q.s[i]) {
case '\a':
case '\b':
case '\f':
case '\n':
case '\r':
case '\t':
case ' ':
case '\v':
case '\\':
case '\'':
case '\"':
esc = true;
break;
default:
if(!isprint(q.s[i])) {
esc = true;
}
break;
}
}
if(esc) {
strm<<'"'<<escape(q.s)<<'"';
} else {
strm<<q.s;
}
return strm;
}
}} //epics::pvData
}}

View File

@@ -17,7 +17,7 @@ using std::cout;
using std::endl;
using std::string;
namespace epics { namespace pvData {
namespace epics { namespace pvData {
template<typename T>
void copy(
@@ -272,3 +272,4 @@ void copy(
}
}}

View File

@@ -1,10 +0,0 @@
# This is a Makefile fragment, see ../Makefile
SRC_DIRS += $(PVDATA_SRC)/json
INC += pv/json.h
LIBSRCS += parsehelper.cpp
LIBSRCS += parseany.cpp
LIBSRCS += parseinto.cpp
LIBSRCS += print.cpp

View File

@@ -1,280 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <sstream>
#define epicsExportSharedSymbols
#include <pv/pvdVersion.h>
#include <pv/pvData.h>
#include <pv/valueBuilder.h>
#include "pv/json.h"
namespace pvd = epics::pvData;
using pvd::yajl::integer_arg;
using pvd::yajl::size_arg;
namespace {
struct context {
unsigned depth;
enum state_t {
Undefined,
Key,
Array,
} state;
pvd::shared_vector<void> arr;
pvd::ValueBuilder root,
*cur;
std::string msg,
key;
context() :depth(0u), state(Undefined), cur(&root) {}
};
#define TRY context *self = (context*)ctx; try
#define CATCH() catch(std::exception& e) { self->msg = e.what(); return 0; }
int jtree_null(void * ctx)
{
TRY {
self->msg = "NULL value not permitted";
return 0;
}CATCH()
}
int jtree_boolean(void * ctx, int boolVal)
{
TRY {
if(self->depth==0) throw std::runtime_error("Bare value not supported");
switch(self->state) {
case context::Key:
self->cur = &self->cur->add<pvd::pvBoolean>(self->key, boolVal);
self->key.clear();
self->state = context::Undefined;
break;
case context::Array:
{
if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvBoolean)
throw std::runtime_error("Mixed type array not supported");
pvd::shared_vector<pvd::boolean> arr(pvd::static_shared_vector_cast<pvd::boolean>(self->arr));
arr.push_back(boolVal);
self->arr = pvd::static_shared_vector_cast<void>(arr);
break;
}
default:
throw std::logic_error("boolean in bad state");
}
return 1;
}CATCH()
}
int jtree_integer(void * ctx, integer_arg integerVal)
{
TRY {
if(self->depth==0) throw std::runtime_error("Bare value not supported");
switch(self->state) {
case context::Key:
self->cur = &self->cur->add<pvd::pvLong>(self->key, integerVal);
self->key.clear();
self->state = context::Undefined;
break;
case context::Array:
{
if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvLong)
throw std::runtime_error("Mixed type array not supported");
pvd::shared_vector<pvd::int64> arr(pvd::static_shared_vector_cast<pvd::int64>(self->arr));
arr.push_back(integerVal);
self->arr = pvd::static_shared_vector_cast<void>(arr);
break;
}
default:
throw std::logic_error("int64 in bad state");
}
return 1;
}CATCH()
}
int jtree_double(void * ctx, double doubleVal)
{
TRY {
if(self->depth==0) throw std::runtime_error("Bare value not supported");
switch(self->state) {
case context::Key:
self->cur = &self->cur->add<pvd::pvDouble>(self->key, doubleVal);
self->key.clear();
self->state = context::Undefined;
break;
case context::Array:
{
if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvDouble)
throw std::runtime_error("Mixed type array not supported");
pvd::shared_vector<double> arr(pvd::static_shared_vector_cast<double>(self->arr));
arr.push_back(doubleVal);
self->arr = pvd::static_shared_vector_cast<void>(arr);
break;
}
default:
throw std::logic_error("double in bad state");
}
return 1;
}CATCH()
}
int jtree_string(void * ctx, const unsigned char * stringVal,
size_arg stringLen)
{
TRY {
if(self->depth==0) throw std::runtime_error("Bare value not supported");
std::string sval((const char*)stringVal, stringLen);
switch(self->state) {
case context::Key:
self->cur = &self->cur->add<pvd::pvString>(self->key, sval);
self->key.clear();
self->state = context::Undefined;
break;
case context::Array:
{
if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvString)
throw std::runtime_error("Mixed type array not supported");
pvd::shared_vector<std::string> arr(pvd::static_shared_vector_cast<std::string>(self->arr));
arr.push_back(sval);
self->arr = pvd::static_shared_vector_cast<void>(arr);
break;
}
default:
throw std::logic_error("double in bad state");
}
return 1;
}CATCH()
}
int jtree_start_map(void * ctx)
{
TRY {
if(self->depth>0) {
if(self->key.empty())
throw std::logic_error("anonymous dict not top level?");
self->cur = &self->cur->addNested(self->key);
self->key.clear();
}
self->depth++;
return 1;
}CATCH()
}
int jtree_map_key(void * ctx, const unsigned char * key,
size_arg stringLen)
{
TRY {
if(!self->key.empty())
throw std::logic_error("double key?");
if(stringLen==0)
throw std::runtime_error("empty key not allowed");
self->key = std::string((const char*)key, stringLen);
self->state = context::Key;
return 1;
}CATCH()
}
int jtree_end_map(void * ctx)
{
TRY {
if(self->depth>1)
self->cur = &self->cur->endNested();
else if(self->depth==0)
throw std::logic_error("Unbalenced dict");
self->depth--;
return 1;
}CATCH()
}
int jtree_start_array(void * ctx)
{
TRY {
if(self->depth==0) throw std::runtime_error("Bare array not supported");
if(self->state!=context::Key)
throw std::logic_error("bare array not supported");
self->state = context::Array;
return 1;
}CATCH()
}
int jtree_end_array(void * ctx)
{
TRY {
if(self->state!=context::Array)
throw std::logic_error("Bad array parse");
self->cur = &self->cur->add(self->key, pvd::freeze(self->arr));
self->key.clear();
self->state = context::Undefined;
return 1;
}CATCH()
}
yajl_callbacks jtree_cbs = {
&jtree_null,
&jtree_boolean,
&jtree_integer,
&jtree_double,
NULL, // number
&jtree_string,
&jtree_start_map,
&jtree_map_key,
&jtree_end_map,
&jtree_start_array,
&jtree_end_array,
};
struct handler {
yajl_handle handle;
handler(yajl_handle handle) :handle(handle)
{
if(!handle)
throw std::runtime_error("Failed to allocate yajl handle");
}
~handler() {
yajl_free(handle);
}
operator yajl_handle() { return handle; }
};
} // namespace
namespace epics{namespace pvData{
epics::pvData::PVStructure::shared_pointer
parseJSON(std::istream& strm)
{
#ifndef EPICS_YAJL_VERSION
yajl_parser_config conf;
memset(&conf, 0, sizeof(conf));
conf.allowComments = 1;
conf.checkUTF8 = 1;
#endif
context ctxt;
#ifndef EPICS_YAJL_VERSION
handler handle(yajl_alloc(&jtree_cbs, &conf, NULL, &ctxt));
#else
handler handle(yajl_alloc(&jtree_cbs, NULL, &ctxt));
yajl_config(handle, yajl_allow_comments, 1);
#endif
if(!yajl_parse_helper(strm, handle))
throw std::runtime_error(ctxt.msg);
return ctxt.cur->buildPVStructure();
}
}} // namespace epics::pvData

View File

@@ -1,118 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <stdexcept>
#include <sstream>
#define epicsExportSharedSymbols
#include <pv/pvdVersion.h>
#include "pv/json.h"
namespace {
void check_trailing(const std::string& line)
{
size_t idx = line.find_first_not_of(" \t\n\r");
if(idx==line.npos) return;
// TODO: detect the end of potentially multi-line comments...
// for now trailing comments not allowed
throw std::runtime_error("Trailing junk");
}
} // namespace
namespace epics{namespace pvData{
bool yajl_parse_helper(std::istream& src,
yajl_handle handle)
{
unsigned linenum=0;
#ifndef EPICS_YAJL_VERSION
bool done = false;
#endif
std::string line;
while(std::getline(src, line)) {
linenum++;
#ifndef EPICS_YAJL_VERSION
if(done) {
check_trailing(line);
continue;
}
#endif
yajl_status sts = yajl_parse(handle, (const unsigned char*)line.c_str(), line.size());
switch(sts) {
case yajl_status_ok: {
size_t consumed = yajl_get_bytes_consumed(handle);
if(consumed<line.size()) {
check_trailing(line.substr(consumed));
}
#ifndef EPICS_YAJL_VERSION
done = true;
#endif
break;
}
case yajl_status_client_canceled:
return false;
#ifndef EPICS_YAJL_VERSION
case yajl_status_insufficient_data:
// continue with next line
break;
#endif
case yajl_status_error:
{
std::ostringstream msg;
unsigned char *raw = yajl_get_error(handle, 1, (const unsigned char*)line.c_str(), line.size());
if(!raw) {
msg<<"Unknown error on line "<<linenum;
} else {
try {
msg<<"Error on line "<<linenum<<" : "<<(const char*)raw;
}catch(...){
yajl_free_error(handle, raw);
throw;
}
yajl_free_error(handle, raw);
}
throw std::runtime_error(msg.str());
}
}
}
if(!src.eof() || src.bad()) {
std::ostringstream msg;
msg<<"I/O error after line "<<linenum;
throw std::runtime_error(msg.str());
#ifndef EPICS_YAJL_VERSION
} else if(!done) {
switch(yajl_parse_complete(handle)) {
#else
} else {
switch(yajl_complete_parse(handle)) {
#endif
case yajl_status_ok:
break;
case yajl_status_client_canceled:
return false;
#ifndef EPICS_YAJL_VERSION
case yajl_status_insufficient_data:
throw std::runtime_error("unexpected end of input");
#endif
case yajl_status_error:
throw std::runtime_error("Error while completing parsing");
}
}
return true;
}
}} // namespace epics::pvData

View File

@@ -1,346 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <vector>
#include <sstream>
#define epicsExportSharedSymbols
#include <pv/pvdVersion.h>
#include <pv/pvData.h>
#include <pv/valueBuilder.h>
#include <pv/bitSet.h>
#include "pv/json.h"
namespace pvd = epics::pvData;
using pvd::yajl::integer_arg;
using pvd::yajl::size_arg;
namespace {
struct context {
std::string msg;
struct frame {
pvd::PVFieldPtr fld;
pvd::BitSet *assigned;
frame(const pvd::PVFieldPtr& fld, pvd::BitSet *assigned)
:fld(fld), assigned(assigned)
{}
};
typedef std::vector<frame> stack_t;
stack_t stack;
context(const pvd::PVFieldPtr& root, pvd::BitSet *assigned)
{
stack.push_back(frame(root, assigned));
}
};
#define TRY context *self = (context*)ctx; assert(!self->stack.empty()); try
#define CATCH() catch(std::exception& e) { if(self->msg.empty()) self->msg = e.what(); return 0; }
int jtree_null(void * ctx)
{
TRY {
self->msg = "NULL value not permitted";
return 0;
}CATCH()
}
template<typename PVScalarT, typename PVArrayT>
void valueAssign(context *self, typename PVScalarT::value_type val)
{
assert(!self->stack.empty());
context::frame& back = self->stack.back();
pvd::Type type(back.fld->getField()->getType());
if(type==pvd::scalar) {
pvd::PVScalar* fld(static_cast<pvd::PVScalar*>(back.fld.get()));
fld->putFrom(val);
if(back.assigned)
back.assigned->set(fld->getFieldOffset());
self->stack.pop_back();
// structure at the top of the stack
} else if(type==pvd::scalarArray) {
pvd::PVScalarArray *fld(static_cast<pvd::PVScalarArray*>(back.fld.get()));
pvd::shared_vector<const void> carr;
fld->getAs(carr);
switch(carr.original_type())
{
#define CASE_STRING
#define CASE_REAL_INT64
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case epics::pvData::pv##PVACODE: { \
pvd::shared_vector<const PVATYPE> arr(pvd::static_shared_vector_cast<const PVATYPE>(carr)); \
pvd::shared_vector<PVATYPE> tarr(pvd::thaw(arr)); \
tarr.push_back(pvd::castUnsafe<PVATYPE>(val)); \
carr = pvd::static_shared_vector_cast<const void>(pvd::freeze(tarr)); \
} break;
#include <pv/typemap.h>
#undef CASE
#undef CASE_REAL_INT64
#undef CASE_STRING
}
fld->putFrom(carr);
// leave array field at top of stack
} else if(type==pvd::union_) {
pvd::PVUnion* fld(static_cast<pvd::PVUnion*>(back.fld.get()));
pvd::UnionConstPtr utype(fld->getUnion());
if(utype->isVariant()) {
typename PVScalarT::shared_pointer elem(pvd::getPVDataCreate()->createPVScalar<PVScalarT>());
elem->put(val);
fld->set(elem);
} else {
// attempt automagic assignment
const pvd::StringArray& names = utype->getFieldNames();
const pvd::FieldConstPtrArray types = utype->getFields();
assert(names.size()==types.size());
bool assigned = false;
for(size_t i=0, N=names.size(); i<N; i++) {
if(types[i]->getType()!=pvd::scalar) continue;
pvd::PVScalarPtr ufld(fld->select<pvd::PVScalar>(i));
try{
ufld->putFrom(val);
assigned = true;
}catch(std::runtime_error&){
if(i==N-1)
throw;
continue;
}
break;
}
if(!assigned)
throw std::runtime_error("Unable to select union member");
}
if(back.assigned)
back.assigned->set(fld->getFieldOffset());
self->stack.pop_back();
// structure back at the top of the stack
} else {
throw std::invalid_argument("Can't assign value");
}
}
int jtree_boolean(void * ctx, int boolVal)
{
TRY {
valueAssign<pvd::PVBoolean, pvd::PVBooleanArray>(self, !!boolVal);
return 1;
}CATCH()
}
int jtree_integer(void * ctx, integer_arg integerVal)
{
TRY {
valueAssign<pvd::PVLong, pvd::PVLongArray>(self, integerVal);
return 1;
}CATCH()
}
int jtree_double(void * ctx, double doubleVal)
{
TRY {
valueAssign<pvd::PVDouble, pvd::PVDoubleArray>(self, doubleVal);
return 1;
}CATCH()
}
int jtree_string(void * ctx, const unsigned char * stringVal,
size_arg stringLen)
{
TRY {
std::string val((const char*)stringVal, stringLen);
valueAssign<pvd::PVString, pvd::PVStringArray>(self, val);
return 1;
}CATCH()
}
int jtree_start_map(void * ctx)
{
TRY {
assert(!self->stack.empty());
context::frame& back = self->stack.back();
pvd::Type type = back.fld->getField()->getType();
if(type==pvd::structure) {
// will fill in
} else if(type==pvd::structureArray) {
// starting new element in structure array
pvd::PVStructureArray* sarr(static_cast<pvd::PVStructureArray*>(back.fld.get()));
pvd::PVStructurePtr elem(pvd::getPVDataCreate()->createPVStructure(sarr->getStructureArray()->getStructure()));
self->stack.push_back(context::frame(elem, 0));
} else {
throw std::runtime_error("Can't map (sub)structure");
}
assert(self->stack.back().fld->getField()->getType()==pvd::structure);
return 1;
}CATCH()
}
int jtree_map_key(void * ctx, const unsigned char * key,
size_arg stringLen)
{
TRY {
assert(!self->stack.empty());
std::string name((const char*)key, stringLen);
// start_map() ensures we have a structure at the top of the stack
pvd::PVStructure *fld = static_cast<pvd::PVStructure*>(self->stack.back().fld.get());
try {
self->stack.push_back(context::frame(fld->getSubFieldT(name), self->stack.back().assigned));
}catch(std::runtime_error& e){
std::ostringstream strm;
strm<<"At "<<fld->getFullName()<<" : "<<e.what()<<"\n";
throw std::runtime_error(strm.str());
}
return 1;
}CATCH()
}
int jtree_end_map(void * ctx)
{
TRY {
assert(!self->stack.empty());
assert(self->stack.back().fld->getField()->getType()==pvd::structure);
context::frame elem(self->stack.back());
self->stack.pop_back();
if(!self->stack.empty() && self->stack.back().fld->getField()->getType()==pvd::structureArray) {
// append element to struct array
pvd::PVStructureArray *sarr = static_cast<pvd::PVStructureArray*>(self->stack.back().fld.get());
pvd::PVStructureArray::const_svector cval;
sarr->swap(cval);
pvd::PVStructureArray::svector val(pvd::thaw(cval));
val.push_back(std::tr1::static_pointer_cast<pvd::PVStructure>(elem.fld));
sarr->replace(pvd::freeze(val));
}
return 1;
}CATCH()
}
int jtree_start_array(void * ctx)
{
TRY {
assert(!self->stack.empty());
pvd::PVFieldPtr& back(self->stack.back().fld);
pvd::Type type = back->getField()->getType();
if(type!=pvd::structureArray && type!=pvd::scalarArray)
throw std::runtime_error("Can't assign array");
return 1;
}CATCH()
}
int jtree_end_array(void * ctx)
{
TRY {
assert(!self->stack.empty());
if(self->stack.back().assigned)
self->stack.back().assigned->set(self->stack.back().fld->getFieldOffset());
self->stack.pop_back();
return 1;
}CATCH()
}
yajl_callbacks jtree_cbs = {
&jtree_null,
&jtree_boolean,
&jtree_integer,
&jtree_double,
NULL, // number
&jtree_string,
&jtree_start_map,
&jtree_map_key,
&jtree_end_map,
&jtree_start_array,
&jtree_end_array,
};
struct handler {
yajl_handle handle;
handler(yajl_handle handle) :handle(handle)
{
if(!handle)
throw std::runtime_error("Failed to allocate yajl handle");
}
~handler() {
yajl_free(handle);
}
operator yajl_handle() { return handle; }
};
struct noop {
void operator()(pvd::PVField*) {}
};
} // namespace
namespace epics{namespace pvData{
epicsShareFunc
void parseJSON(std::istream& strm,
PVField& dest,
BitSet *assigned)
{
#ifndef EPICS_YAJL_VERSION
yajl_parser_config conf;
memset(&conf, 0, sizeof(conf));
conf.allowComments = 1;
conf.checkUTF8 = 1;
#endif
// we won't create refs to 'dest' which presist beyond this call.
// however, it is convienent to treat 'dest' in the same manner as
// any union/structureArray memebers it may contain.
PVFieldPtr fakedest(&dest, noop());
context ctxt(fakedest, assigned);
#ifndef EPICS_YAJL_VERSION
handler handle(yajl_alloc(&jtree_cbs, &conf, NULL, &ctxt));
#else
handler handle(yajl_alloc(&jtree_cbs, NULL, &ctxt));
yajl_config(handle, yajl_allow_comments, 1);
#endif
if(!yajl_parse_helper(strm, handle))
throw std::runtime_error(ctxt.msg);
if(!ctxt.stack.empty())
throw std::logic_error("field stack not empty");
assert(fakedest.use_count()==1);
}
}} // namespace epics::pvData

View File

@@ -1,231 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <vector>
#include <sstream>
#define epicsExportSharedSymbols
#include <pv/pvdVersion.h>
#include <pv/pvData.h>
#include <pv/valueBuilder.h>
#include <pv/bitSet.h>
#include "pv/json.h"
namespace pvd = epics::pvData;
namespace {
struct args {
std::ostream& strm;
const pvd::JSONPrintOptions& opts;
unsigned indent;
args(std::ostream& strm,
const pvd::JSONPrintOptions& opts)
:strm(strm)
,opts(opts)
,indent(opts.indent)
{}
void doIntent() {
if(!opts.multiLine) return;
strm.put('\n');
unsigned i=indent;
while(i--) strm.put(' ');
}
};
void show_field(args& A, const pvd::PVField* fld, const pvd::BitSet *mask);
void show_struct(args& A, const pvd::PVStructure* fld, const pvd::BitSet *mask)
{
const pvd::StructureConstPtr& type = fld->getStructure();
const pvd::PVFieldPtrArray& children = fld->getPVFields();
const pvd::StringArray& names = type->getFieldNames();
A.strm.put('{');
A.indent++;
bool first = true;
for(size_t i=0, N=names.size(); i<N; i++)
{
if(mask && !mask->get(children[i]->getFieldOffset())) continue;
if(first)
first = false;
else
A.strm.put(',');
A.doIntent();
A.strm<<'\"'<<names[i]<<"\": ";
show_field(A, children[i].get(), mask);
}
A.indent--;
A.doIntent();
A.strm.put('}');
}
void show_field(args& A, const pvd::PVField* fld, const pvd::BitSet *mask)
{
switch(fld->getField()->getType())
{
case pvd::scalar:
{
const pvd::PVScalar *scalar=static_cast<const pvd::PVScalar*>(fld);
if(scalar->getScalar()->getScalarType()==pvd::pvString) {
A.strm<<'\"'<<scalar->getAs<std::string>()<<'\"';
} else {
A.strm<<scalar->getAs<std::string>();
}
}
return;
case pvd::scalarArray:
{
const pvd::PVScalarArray *scalar=static_cast<const pvd::PVScalarArray*>(fld);
const bool isstring = scalar->getScalarArray()->getElementType()==pvd::pvString;
pvd::shared_vector<const void> arr;
scalar->getAs<void>(arr);
pvd::shared_vector<const std::string> sarr(pvd::shared_vector_convert<const std::string>(arr));
A.strm.put('[');
for(size_t i=0, N=sarr.size(); i<N; i++) {
if(i!=0)
A.strm.put(',');
if(isstring)
A.strm.put('\"');
A.strm<<sarr[i];
if(isstring)
A.strm.put('\"');
}
A.strm.put(']');
}
return;
case pvd::structure:
show_struct(A, static_cast<const pvd::PVStructure*>(fld), mask);
return;
case pvd::structureArray:
{
pvd::PVStructureArray::const_svector arr(static_cast<const pvd::PVStructureArray*>(fld)->view());
A.strm.put('[');
A.indent++;
for(size_t i=0, N=arr.size(); i<N; i++) {
if(i!=0)
A.strm.put(',');
A.doIntent();
if(arr[i])
show_struct(A, arr[i].get(), 0);
else
A.strm<<"NULL";
}
A.indent--;
A.doIntent();
A.strm.put(']');
}
return;
case pvd::union_:
{
const pvd::PVUnion *U=static_cast<const pvd::PVUnion*>(fld);
const pvd::PVField::const_shared_pointer& C(U->get());
if(!C) {
A.strm<<"null";
} else {
show_field(A, C.get(), 0);
}
}
return;
case pvd::unionArray: {
const pvd::PVUnionArray *U=static_cast<const pvd::PVUnionArray*>(fld);
pvd::PVUnionArray::const_svector arr(U->view());
A.strm.put('[');
A.indent++;
for(size_t i=0, N=arr.size(); i<N; i++) {
if(i!=0)
A.strm.put(',');
A.doIntent();
if(arr[i])
show_field(A, arr[i].get(), 0);
else
A.strm<<"NULL";
}
A.indent--;
A.doIntent();
A.strm.put(']');
}
return;
}
// should not be reached
if(A.opts.ignoreUnprintable)
A.strm<<"// unprintable field type";
else
throw std::runtime_error("Encountered unprintable field type");
}
void expandBS(const pvd::PVStructure& top, pvd::BitSet& mask, bool parents) {
if(mask.get(0)) { // special handling because getSubField(0) not allowed
// wildcard
for(size_t idx=1, N=top.getNumberFields(); idx<N; idx++) {
mask.set(idx);
}
} else {
for(pvd::int32 idx = mask.nextSetBit(0), N=top.getNumberFields(); idx>=0 && idx<N; idx=mask.nextSetBit(idx+1)) {
pvd::PVField::const_shared_pointer fld = top.getSubFieldT(idx);
// look forward and mark all children
for(size_t i=idx+1, N=fld->getNextFieldOffset(); i<N; i++)
mask.set(i);
if(parents) {
// look back and mark all parents
// we've already stepped past all parents so siblings will not be automatically marked
for(const pvd::PVStructure *parent = fld->getParent(); parent; parent = parent->getParent()) {
mask.set(parent->getFieldOffset());
}
}
}
}
}
} // namespace
namespace epics{namespace pvData{
JSONPrintOptions::JSONPrintOptions()
:multiLine(true)
,ignoreUnprintable(true)
,indent(0)
{}
void printJSON(std::ostream& strm,
const PVStructure& val,
const BitSet& mask,
const JSONPrintOptions& opts)
{
args A(strm, opts);
pvd::BitSet emask(mask);
expandBS(val, emask, true);
if(!emask.get(0)) return;
show_struct(A, &val, &emask);
}
void printJSON(std::ostream& strm,
const PVField& val,
const JSONPrintOptions& opts)
{
args A(strm, opts);
show_field(A, &val, 0);
}
}} // namespace epics::pvData

View File

@@ -1,148 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#ifndef PV_JSON_H
#define PV_JSON_H
#include <istream>
#include <ostream>
#include <string>
#include <map>
#include <pv/pvdVersion.h>
#include <pv/pvData.h>
#ifdef epicsExportSharedSymbols
# define pvjson_epicsExportSharedSymbols
# undef epicsExportSharedSymbols
#endif
#include <yajl_parse.h>
#ifdef pvjson_epicsExportSharedSymbols
# define epicsExportSharedSymbols
# include "shareLib.h"
#endif
#include <shareLib.h>
namespace epics{namespace pvData{
class BitSet;
/** @defgroup pvjson JSON print/parse
*
* Printing PVField as JSON and parsing JSON into PVField.
*
* @{
*/
//! Options used during printing
struct epicsShareClass JSONPrintOptions
{
bool multiLine; //!< include new lines
bool ignoreUnprintable;//!< ignore union/union array when encountered
unsigned indent; //!< Initial indentation (# of spaces)
JSONPrintOptions();
};
/** Print PVStructure as JSON
*
* 'mask' selects those fields which will be printed.
* @version Overload added after 7.0.0
*/
epicsShareFunc
void printJSON(std::ostream& strm,
const PVStructure& val,
const BitSet& mask,
const JSONPrintOptions& opts = JSONPrintOptions());
/** Print PVField as JSON
* @version Overload added after 7.0.0
*/
epicsShareFunc
void printJSON(std::ostream& strm,
const PVField& val,
const JSONPrintOptions& opts = JSONPrintOptions());
// To be deprecated in favor of previous form
FORCE_INLINE
void printJSON(std::ostream& strm,
const PVField::const_shared_pointer& val,
const JSONPrintOptions& opts = JSONPrintOptions())
{
printJSON(strm, *val, opts);
}
/** Parse JSON text into a PVStructure
*
* Restrictions:
*
* - Top level must be {} dict/object
* - field values must be number, string, array, or dict/object
* - array values must be number or string
*/
epicsShareFunc
PVStructure::shared_pointer parseJSON(std::istream& strm);
/** Parse JSON and store into the provided PVStructure.
*
* Restrictions:
*
* - array of union not supported
* - Only scalar value assigned to union
*
* @param strm Read JSON text from stream
* @param dest Store in fields of this structure
* @param assigned Which fields of _dest_ were assigned. (Optional)
* @throws std::runtime_error on failure. dest and assigned may be modified.
* @version Overload added after 7.0.0
*/
epicsShareFunc
void parseJSON(std::istream& strm,
PVField& dest,
BitSet *assigned=0);
// To be deprecated in favor of previous form
FORCE_INLINE
void parseJSON(std::istream& strm,
const PVField::shared_pointer& dest,
BitSet *assigned=0)
{
parseJSON(strm, *dest, assigned);
}
/** Wrapper around yajl_parse()
*
* Parse entire input stream.
* Errors if extranious non-whitespace found after the point were parsing completes.
*
* @param src The stream from which input charactors are read
* @param handle A parser handle previously allocated with yajl_alloc(). Not free'd on success or failure.
*
* @returns true if parsing completes successfully. false if parsing cancelled by callback. throws other errors
*
* @note The form of this call depends on EPICS_YAJL_VERSION
*/
epicsShareFunc
bool yajl_parse_helper(std::istream& src,
yajl_handle handle);
namespace yajl {
// undef implies API version 0
#ifndef EPICS_YAJL_VERSION
typedef long integer_arg;
typedef unsigned size_arg;
#else
typedef long long integer_arg;
typedef size_t size_arg;
#endif
} // namespace epics::pvData::yajl
/** @} */
}} // namespace epics::pvData
#endif // PV_JSON_H

View File

@@ -4,6 +4,7 @@ SRC_DIRS += $(PVDATA_SRC)/misc
INC += pv/noDefaultMethods.h
INC += pv/lock.h
INC += pv/requester.h
INC += pv/serialize.h
INC += pv/bitSet.h
INC += pv/byteBuffer.h
@@ -11,29 +12,32 @@ INC += pv/epicsException.h
INC += pv/serializeHelper.h
INC += pv/event.h
INC += pv/thread.h
INC += pv/executor.h
INC += pv/timeFunction.h
INC += pv/timer.h
INC += pv/queue.h
INC += pv/messageQueue.h
INC += pv/destroyable.h
INC += pv/status.h
INC += pv/sharedPtr.h
INC += pv/debugPtr.h
INC += pv/localStaticLock.h
INC += pv/typeCast.h
INC += pv/sharedVector.h
INC += pv/templateMeta.h
INC += pv/current_function.h
INC += pv/pvUnitTest.h
INC += pv/reftrack.h
INC += pv/anyscalar.h
LIBSRCS += byteBuffer.cpp
LIBSRCS += bitSet.cpp
LIBSRCS += epicsException.cpp
LIBSRCS += requester.cpp
LIBSRCS += serializeHelper.cpp
LIBSRCS += event.cpp
LIBSRCS += executor.cpp
LIBSRCS += timeFunction.cpp
LIBSRCS += timer.cpp
LIBSRCS += status.cpp
LIBSRCS += messageQueue.cpp
LIBSRCS += localStaticLock.cpp
LIBSRCS += typeCast.cpp
LIBSRCS += thread.cpp
LIBSRCS += parseToPOD.cpp
LIBSRCS += pvUnitTest.cpp
LIBSRCS += debugPtr.cpp
LIBSRCS += reftrack.cpp
LIBSRCS += anyscalar.cpp

View File

@@ -1,161 +0,0 @@
#include <epicsAssert.h>
#define epicsExportSharedSymbols
#include <shareLib.h>
#include "pv/anyscalar.h"
namespace epics {namespace pvData {
AnyScalar::AnyScalar(ScalarType type, const void *buf)
{
if(type==pvString) {
new (_wrap.blob) std::string(*static_cast<const std::string*>(buf));
} else {
memcpy(_wrap.blob, buf, ScalarTypeFunc::elementSize(type));
}
_stype = type;
}
AnyScalar::AnyScalar(const AnyScalar& o)
:_stype(o._stype)
{
if(o._stype==pvString) {
new (_wrap.blob) std::string(o._as<std::string>());
} else if(o._stype!=(ScalarType)-1) {
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
}
}
#if __cplusplus>=201103L
AnyScalar::AnyScalar(AnyScalar&& o)
:_stype(o._stype)
{
typedef std::string string;
if(o._stype==pvString) {
new (_wrap.blob) std::string();
_as<std::string>() = std::move(o._as<std::string>());
o._as<string>().~string();
} else if(o._stype!=(ScalarType)-1) {
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
}
o._stype = (ScalarType)-1;
}
#endif
void AnyScalar::clear() {
if(_stype==pvString) {
typedef std::string string;
_as<string>().~string();
}
// other types need no cleanup
_stype = (ScalarType)-1;
}
void AnyScalar::swap(AnyScalar& o) {
typedef std::string string;
switch((int)_stype) {
case -1:
switch((int)o._stype) {
case -1:
// nil <-> nil
break;
case pvString:
// nil <-> string
new (_wrap.blob) std::string();
_as<std::string>().swap(o._as<std::string>());
o._as<std::string>().~string();
break;
default:
// nil <-> non-string
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
break;
}
break;
case pvString:
switch((int)o._stype) {
case -1:
// string <-> nil
new (o._wrap.blob) std::string();
_as<std::string>().swap(o._as<std::string>());
_as<std::string>().~string();
break;
case pvString:
// string <-> string
_as<std::string>().swap(o._as<std::string>());
break;
default: {
// string <-> non-string
std::string temp;
temp.swap(_as<std::string>());
_as<std::string>().~string();
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
new (o._wrap.blob) std::string();
temp.swap(o._as<std::string>());
}
break;
}
break;
default:
switch((int)o._stype) {
case -1:
// non-string <-> nil
memcpy(o._wrap.blob, _wrap.blob, sizeof(_largest_blob));
break;
case pvString: {
// non-string <-> string
std::string temp;
temp.swap(o._as<std::string>());
o._as<std::string>().~string();
memcpy(o._wrap.blob, _wrap.blob, sizeof(_largest_blob));
new (_wrap.blob) std::string();
temp.swap(_as<std::string>());
}
break;
default:
// non-string <-> non-string
_largest_blob temp;
memcpy(&temp, _wrap.blob, sizeof(_largest_blob));
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
memcpy(o._wrap.blob, &temp, sizeof(_largest_blob));
// std::swap(o._wrap.blob, _wrap.blob); // gcc <=4.3 doesn't like this
break;
}
break;
}
std::swap(_stype, o._stype);
}
const void* AnyScalar::bufferUnsafe() const {
if(_stype==pvString) {
return as<std::string>().c_str();
} else {
return _wrap.blob;
}
}
std::ostream& operator<<(std::ostream& strm, const AnyScalar& v)
{
switch(v.type()) {
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case pv ## PVACODE: strm<<v._as<PVATYPE>(); break;
#define CASE_REAL_INT64
#define CASE_STRING
#include "pv/typemap.h"
#undef CASE
#undef CASE_REAL_INT64
#undef CASE_STRING
default:
strm<<"(nil)"; break;
}
return strm;
}
}} // namespace epics::pvData

View File

@@ -42,12 +42,12 @@
#define CHECK_POST() assert(words.empty() || words.back()!=0)
namespace epics { namespace pvData {
BitSet::shared_pointer BitSet::create(uint32 nbits)
{
return BitSet::shared_pointer(new BitSet(nbits));
}
BitSet::BitSet() {}
BitSet::BitSet(uint32 nbits)
@@ -55,19 +55,6 @@ namespace epics { namespace pvData {
words.reserve((nbits == 0) ? 1 : WORD_INDEX(nbits-1) + 1);
}
#if __cplusplus>=201103L
BitSet::BitSet(std::initializer_list<uint32> I)
{
// optimistically guess that highest bit is last (not required)
words.reserve((I.size() == 0) ? 1 : WORD_INDEX(*(I.end()-1)) + 1);
for(uint32 idx : I)
{
set(idx);
}
}
#endif
BitSet::~BitSet() {}
void BitSet::recalculateWordsInUse() {
@@ -89,7 +76,7 @@ namespace epics { namespace pvData {
ensureCapacity(wordIndex+1);
}
BitSet& BitSet::flip(uint32 bitIndex) {
void BitSet::flip(uint32 bitIndex) {
uint32 wordIdx = WORD_INDEX(bitIndex);
expandTo(wordIdx);
@@ -97,27 +84,25 @@ namespace epics { namespace pvData {
words[wordIdx] ^= (((uint64)1) << WORD_OFFSET(bitIndex));
recalculateWordsInUse();
return *this;
}
BitSet& BitSet::set(uint32 bitIndex) {
void BitSet::set(uint32 bitIndex) {
uint32 wordIdx = WORD_INDEX(bitIndex);
expandTo(wordIdx);
words[wordIdx] |= (((uint64)1) << WORD_OFFSET(bitIndex));
return *this;
}
BitSet& BitSet::clear(uint32 bitIndex) {
void BitSet::clear(uint32 bitIndex) {
uint32 wordIdx = WORD_INDEX(bitIndex);
if (wordIdx < words.size()) {
words[wordIdx] &= ~(((uint64)1) << WORD_OFFSET(bitIndex));
if (wordIdx >= words.size())
return;
recalculateWordsInUse();
}
return *this;
words[wordIdx] &= ~(((uint64)1) << WORD_OFFSET(bitIndex));
recalculateWordsInUse();
}
void BitSet::set(uint32 bitIndex, bool value) {
@@ -211,20 +196,6 @@ namespace epics { namespace pvData {
return words.size() * BITS_PER_WORD;
}
bool BitSet::logical_and(const BitSet& set) const
{
size_t nwords = std::min(words.size(), set.words.size());
for(size_t i=0; i<nwords; i++) {
if(words[i] & set.words[i])
return true;
}
return false;
}
bool BitSet::logical_or(const BitSet& set) const
{
return !words.empty() || !set.words.empty();
}
BitSet& BitSet::operator&=(const BitSet& set) {
// Check for self-assignment!
if (this == &set) return *this;
@@ -327,7 +298,7 @@ namespace epics { namespace pvData {
SerializeHelper::writeSize(len, buffer, flusher);
flusher->ensureBuffer(len);
n = len / 8;
n = len / 8;
for (uint32 i = 0; i < n; i++)
buffer->putLong(words[i]);
@@ -338,7 +309,7 @@ namespace epics { namespace pvData {
void BitSet::deserialize(ByteBuffer* buffer, DeserializableControl* control) {
uint32 bytes = static_cast<uint32>(SerializeHelper::readSize(buffer, control)); // in bytes
uint32 bytes = static_cast<uint32>(SerializeHelper::readSize(buffer, control)); // in bytes
size_t wordsInUse = (bytes + 7) / BYTES_PER_WORD;
words.resize(wordsInUse);

View File

@@ -1,165 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#if __cplusplus>=201103L
#include <epicsMutex.h>
#include <epicsGuard.h>
#define epicsExportSharedSymbols
#include <pv/debugPtr.h>
namespace {
typedef epicsGuard<epicsMutex> Guard;
}
namespace epics {
namespace debug {
// joins together a group of ptr_base instances
// which all have the same dtor
struct tracker {
epicsMutex mutex;
ptr_base::ref_set_t refs;
};
void shared_ptr_base::track_new()
{
if(track) {
Guard G(track->mutex);
track->refs.insert(this);
}
snap_stack();
}
// create new tracker if ptr!=nullptr, otherwise clear
void shared_ptr_base::track_new(void* ptr)
{
track_clear();
if(ptr){
track.reset(new tracker);
Guard G(track->mutex);
track->refs.insert(this);
}
snap_stack();
}
void shared_ptr_base::track_assign(const shared_ptr_base &o)
{
if(track!=o.track) {
track_clear();
track = o.track;
if(track) {
Guard G(track->mutex);
track->refs.insert(this);
}
snap_stack();
}
}
void shared_ptr_base::track_clear()
{
if(track) {
Guard G(track->mutex);
track->refs.erase(this);
}
track.reset();
#ifndef EXCEPT_USE_NONE
m_depth = 0;
#endif
}
void shared_ptr_base::swap(shared_ptr_base &o)
{
// we cheat a bit here to avoid lock order, and to lock only twice
if(track) {
Guard G(track->mutex);
track->refs.insert(&o);
track->refs.erase(this);
}
track.swap(o.track);
if(track) {
Guard G(track->mutex);
track->refs.insert(this);
track->refs.erase(&o);
}
//TODO: keep original somehow???
snap_stack();
o.snap_stack();
}
void shared_ptr_base::snap_stack()
{
if(!track) {
#ifndef EXCEPT_USE_NONE
m_depth = 0;
#endif
return;
}
#if defined(EXCEPT_USE_BACKTRACE)
{
m_depth=backtrace(m_stack,EXCEPT_DEPTH);
}
#else
{}
#endif
}
void shared_ptr_base::show_stack(std::ostream& strm) const
{
strm<<"ptr "<<this;
#ifndef EXCEPT_USE_NONE
if(m_depth<=0) return;
#endif
#if 0 && defined(EXCEPT_USE_BACKTRACE)
{
char **symbols=backtrace_symbols(m_stack, m_depth);
strm<<": ";
for(int i=0; i<m_depth; i++) {
strm<<symbols[i]<<", ";
}
std::free(symbols);
}
#elif !defined(EXCEPT_USE_NONE)
{
strm<<": ";
for(int i=0; i<m_depth; i++) {
strm<<std::hex<<m_stack[i]<<" ";
}
}
#endif
}
void ptr_base::show_refs(std::ostream& strm, bool self, bool weak) const
{
if(!track) {
strm<<"# No refs\n";
} else {
Guard G(track->mutex);
for(auto ref : track->refs) {
if(!self && ref==this) continue;
strm<<'#';
ref->show_stack(strm);
strm<<'\n';
}
}
}
void ptr_base::spy_refs(ref_set_t &refs) const
{
if(track) {
Guard G(track->mutex);
refs.insert(track->refs.begin(), track->refs.end());
}
}
}} // namespace epics::debug
#endif // __cplusplus>=201103L

View File

@@ -27,7 +27,7 @@
using std::string;
namespace epics { namespace pvData {
namespace epics { namespace pvData {
Event::~Event() {

98
src/misc/executor.cpp Normal file
View File

@@ -0,0 +1,98 @@
/* executor.cpp */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mrk
*/
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <epicsEvent.h>
#include <epicsMutex.h>
#include <epicsThread.h>
#define epicsExportSharedSymbols
#include <pv/executor.h>
using std::string;
namespace epics { namespace pvData {
// special instance to stop the executor thread
class ExecutorShutdown : public Command {
virtual void command();
};
void ExecutorShutdown::command()
{
}
static
std::tr1::shared_ptr<Command> shutdown(new ExecutorShutdown());
Executor::Executor(string const & threadName,ThreadPriority priority)
: thread(threadName,priority,this)
{
}
Executor::~Executor()
{
execute(shutdown);
stopped.wait();
// The thread signals 'stopped' while still holding
// the lock. By taking it we wait for the run() function
// to actually return
Lock xx(mutex);
head.reset();
tail.reset();
}
void Executor::run()
{
Lock xx(mutex);
while(true) {
while(!head.get()) {
xx.unlock();
moreWork.wait();
xx.lock();
}
CommandPtr command = head;
head = command->next;
if(!command.get()) continue;
if(command.get()==shutdown.get()) break;
xx.unlock();
try {
command->command();
}catch(std::exception& e){
//TODO: feed into logging mechanism
fprintf(stderr, "Executor: Unhandled exception: %s",e.what());
}catch(...){
fprintf(stderr, "Executor: Unhandled exception");
}
xx.lock();
}
stopped.signal();
}
void Executor::execute(CommandPtr const & command)
{
Lock xx(mutex);
command->next.reset();
if(!head.get()) {
head = command;
moreWork.signal();
return;
}
CommandPtr tail = head;
while(tail->next) tail = tail->next;
tail->next = command;
}
}}

View File

@@ -0,0 +1,39 @@
/* localStaticLock.cpp */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mse
*/
#define epicsExportSharedSymbols
#include <pv/localStaticLock.h>
static int nifty_counter;
static epics::pvData::Mutex* g_localStaticInitMutex;
epics::pvData::Mutex& getLocalStaticInitMutex()
{
return *g_localStaticInitMutex;
}
// The counter is initialized at load-time, i.e., before any of the static objects are initialized.
MutexInitializer::MutexInitializer ()
{
if (0 == nifty_counter++)
{
// Initialize static members.
g_localStaticInitMutex = new epics::pvData::Mutex();
}
}
MutexInitializer::~MutexInitializer ()
{
if (0 == --nifty_counter)
{
// Clean-up.
delete g_localStaticInitMutex;
}
}

115
src/misc/messageQueue.cpp Normal file
View File

@@ -0,0 +1,115 @@
/* messageQueue.cpp */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mrk
*/
#include <string>
#define epicsExportSharedSymbols
#include <pv/messageQueue.h>
using std::string;
namespace epics { namespace pvData {
MessageNode::MessageNode()
: messageType(infoMessage)
{}
string MessageNode::getMessage() const
{
return message;
}
MessageType MessageNode::getMessageType() const
{
return messageType;
}
MessageQueuePtr MessageQueue::create(int size)
{
MessageNodePtrArray nodeArray;
nodeArray.reserve(size);
for(int i=0; i<size; i++) {
nodeArray.push_back(
MessageNodePtr(new MessageNode()));
}
return std::tr1::shared_ptr<MessageQueue>(new MessageQueue(nodeArray));
}
MessageQueue::MessageQueue(MessageNodePtrArray &data)
: Queue<MessageNode>(data),
overrun(0)
{ }
MessageQueue::~MessageQueue()
{
}
MessageNodePtr &MessageQueue::get() {
if(getNumberUsed()==0) return nullNode;
lastGet = getUsed();
return lastGet;
}
void MessageQueue::release() {
if(lastGet.get()==NULL) return;
releaseUsed(lastGet);
lastGet.reset();
}
bool MessageQueue::put(string message,MessageType messageType,bool replaceLast)
{
MessageNodePtr node = getFree();
if(node.get()!= NULL) {
node->message = message;
node->messageType = messageType;
lastPut = node;
setUsed(node);
return true;
}
overrun++;
if(replaceLast) {
node = lastPut;
node->message = message;
node->messageType = messageType;
return true;
}
return false;
}
bool MessageQueue::isEmpty()
{
int free = getNumberFree();
if(free==capacity()) return true;
return false;
}
bool MessageQueue::isFull()
{
if(getNumberFree()==0) return true;
return false;
}
int MessageQueue::getClearOverrun()
{
int num = overrun;
overrun = 0;
return num;
}
MessageQueuePtr createMessageQueue(int size)
{
MessageNodePtrArray nodeArray;
nodeArray.reserve(size);
for(int i=0; i<size; i++) {
nodeArray.push_back(
MessageNodePtr(new MessageNode()));
}
return std::tr1::shared_ptr<MessageQueue>(new MessageQueue(nodeArray));
}
}}

View File

@@ -25,6 +25,406 @@ using std::string;
#define NEED_LONGLONG
#endif
#ifndef EPICS_VERSION_INT
#define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P))
#define EPICS_VERSION_INT VERSION_INT(EPICS_VERSION, EPICS_REVISION, EPICS_MODIFICATION, EPICS_PATCH_LEVEL)
#endif
#if EPICS_VERSION_INT < VERSION_INT(3,15,0,1)
/* These integer conversion primitives added to epicsStdlib.c in 3.15.0.1 */
#define S_stdlib_noConversion 1 /* No digits to convert */
#define S_stdlib_extraneous 2 /* Extraneous characters */
#define S_stdlib_underflow 3 /* Too small to represent */
#define S_stdlib_overflow 4 /* Too large to represent */
#define S_stdlib_badBase 5 /* Number base not supported */
static int
epicsParseLong(const char *str, long *to, int base, char **units)
{
int c;
char *endp;
long value;
while ((c = *str) && isspace(c))
++str;
errno = 0;
value = strtol(str, &endp, base);
if (endp == str)
return S_stdlib_noConversion;
if (errno == EINVAL) /* Not universally supported */
return S_stdlib_badBase;
if (errno == ERANGE)
return S_stdlib_overflow;
while ((c = *endp) && isspace(c))
++endp;
if (c && !units)
return S_stdlib_extraneous;
*to = value;
if (units)
*units = endp;
return 0;
}
static int
epicsParseULong(const char *str, unsigned long *to, int base, char **units)
{
int c;
char *endp;
unsigned long value;
while ((c = *str) && isspace(c))
++str;
errno = 0;
value = strtoul(str, &endp, base);
if (endp == str)
return S_stdlib_noConversion;
if (errno == EINVAL) /* Not universally supported */
return S_stdlib_badBase;
if (errno == ERANGE)
return S_stdlib_overflow;
while ((c = *endp) && isspace(c))
++endp;
if (c && !units)
return S_stdlib_extraneous;
*to = value;
if (units)
*units = endp;
return 0;
}
static int
epicsParseDouble(const char *str, double *to, char **units)
{
int c;
char *endp;
double value;
while ((c = *str) && isspace(c))
++str;
errno = 0;
value = epicsStrtod(str, &endp);
if (endp == str)
return S_stdlib_noConversion;
if (errno == ERANGE)
return (value == 0) ? S_stdlib_underflow : S_stdlib_overflow;
while ((c = *endp) && isspace(c))
++endp;
if (c && !units)
return S_stdlib_extraneous;
*to = value;
if (units)
*units = endp;
return 0;
}
/* These call the primitives */
static int
epicsParseInt8(const char *str, epicsInt8 *to, int base, char **units)
{
long value;
int status = epicsParseLong(str, &value, base, units);
if (status)
return status;
if (value < -0x80 || value > 0x7f)
return S_stdlib_overflow;
*to = (epicsInt8)value;
return 0;
}
static int
epicsParseUInt8(const char *str, epicsUInt8 *to, int base, char **units)
{
unsigned long value;
int status = epicsParseULong(str, &value, base, units);
if (status)
return status;
if (value > 0xff && value <= ~0xffUL)
return S_stdlib_overflow;
*to = (epicsUInt8)value;
return 0;
}
static int
epicsParseInt16(const char *str, epicsInt16 *to, int base, char **units)
{
long value;
int status = epicsParseLong(str, &value, base, units);
if (status)
return status;
if (value < -0x8000 || value > 0x7fff)
return S_stdlib_overflow;
*to = (epicsInt16)value;
return 0;
}
static int
epicsParseUInt16(const char *str, epicsUInt16 *to, int base, char **units)
{
unsigned long value;
int status = epicsParseULong(str, &value, base, units);
if (status)
return status;
if (value > 0xffff && value <= ~0xffffUL)
return S_stdlib_overflow;
*to = (epicsUInt16)value;
return 0;
}
static int
epicsParseInt32(const char *str, epicsInt32 *to, int base, char **units)
{
long value;
int status = epicsParseLong(str, &value, base, units);
if (status)
return status;
#if (LONG_MAX > 0x7fffffff)
if (value < -0x80000000L || value > 0x7fffffffL)
return S_stdlib_overflow;
#endif
*to = (epicsInt32)value;
return 0;
}
static int
epicsParseUInt32(const char *str, epicsUInt32 *to, int base, char **units)
{
unsigned long value;
int status = epicsParseULong(str, &value, base, units);
if (status)
return status;
#if (ULONG_MAX > 0xffffffff)
if (value > 0xffffffffUL && value <= ~0xffffffffUL)
return S_stdlib_overflow;
#endif
*to = (epicsUInt32)value;
return 0;
}
static int
epicsParseFloat(const char *str, float *to, char **units)
{
double value, abs;
int status = epicsParseDouble(str, &value, units);
if (status)
return status;
abs = fabs(value);
if (value > 0 && abs <= FLT_MIN)
return S_stdlib_underflow;
if (finite(value) && abs >= FLT_MAX)
return S_stdlib_overflow;
*to = (float)value;
return 0;
}
#endif
// Sometimes we have to provide our own copy of strtoll()
#if defined(_WIN32) && !defined(_MINGW)
// On Windows with MSVC, Base-3.15 provides strtoll()
# define NEED_OLL_FUNCS (EPICS_VERSION_INT < VERSION_INT(3,15,0,1))
#elif defined(vxWorks)
// On VxWorks, Base-3.15 provides strtoll()
# define NEED_OLL_FUNCS (EPICS_VERSION_INT < VERSION_INT(3,15,0,1))
#else
// Other architectures all provide strtoll()
# define NEED_OLL_FUNCS 0
#endif
#if defined(NEED_LONGLONG) && NEED_OLL_FUNCS
static
long long strtoll(const char *ptr, char ** endp, int base)
{
size_t inlen = strlen(ptr);
long long result;
unsigned char offset=0;
assert(base==0);
if(ptr[0]=='-')
offset=1;
try {
std::istringstream strm(ptr);
assert(strm.rdbuf()->in_avail()>=0
&& inlen==(size_t)strm.rdbuf()->in_avail());
if(ptr[offset]=='0') {
if(ptr[offset+1]=='x')
strm >> std::hex;
else
strm >> std::oct;
}
strm >> result;
if(strm.fail())
goto noconvert;
assert(strm.rdbuf()->in_avail()>=0
&& inlen>=(size_t)strm.rdbuf()->in_avail());
size_t consumed = inlen - strm.rdbuf()->in_avail();
*endp = (char*)ptr + consumed;
return result;
} catch(...) {
goto noconvert;
}
return result;
noconvert:
*endp = (char*)ptr;
return 0;
}
#if defined(vxWorks)
/* The VxWorks version of std::istringstream >> uint64_t is buggy,
* provide our own implementation
*/
static
unsigned long long strtoull(const char *nptr, char **endptr, int base)
{
const char *s = nptr;
unsigned long long acc;
int c;
unsigned long long cutoff;
int neg = 0, any, cutlim;
do
c = *s++;
while (isspace(c));
if (c == '-')
{
neg = 1;
c = *s++;
}
else if (c == '+')
c = *s++;
if ((base == 0 || base == 16) &&
c == '0' && (*s == 'x' || *s == 'X'))
{
c = s[1];
s += 2;
base = 16;
}
if (base == 0)
base = c == '0' ? 8 : 10;
cutoff = (unsigned long long) UINT64_MAX / (unsigned long long) base;
cutlim = (unsigned long long) UINT64_MAX % (unsigned long long) base;
for (acc = 0, any = 0;; c = *s++)
{
if (isdigit(c))
c -= '0';
else if (isalpha(c))
c -= isupper(c) ? 'A' - 10 : 'a' - 10;
else
break;
if (c >= base)
break;
if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
any = -1;
else
{
any = 1;
acc *= base;
acc += c;
}
}
if (any < 0)
{
acc = UINT64_MAX;
errno = ERANGE;
}
else if (neg)
acc = -acc;
if (endptr != 0)
*endptr = any ? (char *) s - 1 : (char *) nptr;
return (acc);
}
#else
static
unsigned long long strtoull(const char *ptr, char ** endp, int base)
{
size_t inlen = strlen(ptr);
unsigned long long result;
assert(base==0);
try {
std::istringstream strm(ptr);
assert(strm.rdbuf()->in_avail()>=0
&& inlen==(size_t)strm.rdbuf()->in_avail());
if(ptr[0]=='0') {
if(ptr[1]=='x')
strm >> std::hex;
else
strm >> std::oct;
}
strm >> result;
if(strm.fail())
goto noconvert;
assert(strm.rdbuf()->in_avail()>=0
&& inlen>=(size_t)strm.rdbuf()->in_avail());
size_t consumed = inlen - strm.rdbuf()->in_avail();
*endp = (char*)ptr + consumed;
return result;
} catch(...) {
goto noconvert;
}
return result;
noconvert:
*endp = (char*)ptr;
return 0;
}
#endif
#endif
/* do we need long long? */
#ifdef NEED_LONGLONG
static int
@@ -107,20 +507,20 @@ void handleParseError(int err)
namespace epics { namespace pvData { namespace detail {
void parseToPOD(const char* in, boolean *out)
void parseToPOD(const string & in, boolean *out)
{
if(epicsStrCaseCmp(in,"true")==0)
if(epicsStrCaseCmp(in.c_str(),"true")==0)
*out = 1;
else if(epicsStrCaseCmp(in,"false")==0)
else if(epicsStrCaseCmp(in.c_str(),"false")==0)
*out = 0;
else
throw std::runtime_error("parseToPOD: string no match true/false");
}
#define INTFN(T, S) \
void parseToPOD(const char* in, T *out) { \
void parseToPOD(const string& in, T *out) { \
epics ## S temp; \
int err = epicsParse ## S (in, &temp, 0, NULL); \
int err = epicsParse ## S (in.c_str(), &temp, 0, NULL); \
if(err) handleParseError(err); \
else *out = temp; \
}
@@ -132,31 +532,31 @@ INTFN(uint16_t, UInt16);
INTFN(int32_t, Int32);
INTFN(uint32_t, UInt32);
void parseToPOD(const char* in, int64_t *out) {
void parseToPOD(const string& in, int64_t *out) {
#ifdef NEED_LONGLONG
int err = epicsParseLongLong(in, out, 0, NULL);
int err = epicsParseLongLong(in.c_str(), out, 0, NULL);
#else
int err = epicsParseLong(in, out, 0, NULL);
int err = epicsParseLong(in.c_str(), out, 0, NULL);
#endif
if(err) handleParseError(err);
}
void parseToPOD(const char* in, uint64_t *out) {
void parseToPOD(const string& in, uint64_t *out) {
#ifdef NEED_LONGLONG
int err = epicsParseULongLong(in, out, 0, NULL);
int err = epicsParseULongLong(in.c_str(), out, 0, NULL);
#else
int err = epicsParseULong(in, out, 0, NULL);
int err = epicsParseULong(in.c_str(), out, 0, NULL);
#endif
if(err) handleParseError(err);
}
void parseToPOD(const char* in, float *out) {
int err = epicsParseFloat(in, out, NULL);
void parseToPOD(const string& in, float *out) {
int err = epicsParseFloat(in.c_str(), out, NULL);
if(err) handleParseError(err);
}
void parseToPOD(const char* in, double *out) {
int err = epicsParseDouble(in, out, NULL);
void parseToPOD(const string& in, double *out) {
int err = epicsParseDouble(in.c_str(), out, NULL);
if(err) handleParseError(err);
#if defined(vxWorks)
/* vxWorks strtod returns [-]epicsINF when it should return ERANGE error.
@@ -164,7 +564,7 @@ void parseToPOD(const char* in, double *out) {
* this into an ERANGE error
*/
else if (*out == epicsINF || *out == -epicsINF) {
const char* s = in;
const char* s = in.c_str();
int c;
/* skip spaces and the sign */

View File

@@ -1,251 +0,0 @@
#ifndef PV_ANYSCALAR_H
#define PV_ANYSCALAR_H
#if __cplusplus>=201103L
# include <type_traits>
#endif
#include <ostream>
#include <exception>
#include <map>
#include <epicsAssert.h>
#include <pv/templateMeta.h>
#include <pv/typeCast.h>
#include <pv/pvIntrospect.h> /* for ScalarType enum */
namespace epics{namespace pvData{
namespace detail {
// special mangling for AnyScalar ctor to map from argument type to storage type.
// allow construction from constants.
template <typename T>
struct any_storage_type { typedef T type; };
template<> struct any_storage_type<int> { typedef int32 type; };
template<> struct any_storage_type<unsigned> { typedef uint32 type; };
template<> struct any_storage_type<char*> { typedef std::string type; };
template<> struct any_storage_type<const char*> { typedef std::string type; };
#if __cplusplus>=201103L
// std::max() isn't constexpr until c++14 :(
constexpr size_t cmax(size_t A, size_t B) {
return A>B ? A : B;
}
#endif
}// namespace detail
/** A type-safe variant union capable of holding
* any of the PVD scalar types (POD or string)
*
@code
AnyScalar A(5);
assert(A.type()==pvInt);
assert(A.ref<int32>()==5);
assert(A.as<int32>()==5);
assert(A.as<double>()==5.0);
assert(A.ref<double>()==5.0); // throws AnyScalar::bad_cast
@endcode
*/
class epicsShareClass AnyScalar {
public:
struct bad_cast : public std::exception {
#if __cplusplus>=201103L
bad_cast() noexcept {}
virtual ~bad_cast() noexcept {}
virtual const char* what() const noexcept
#else
bad_cast() throw() {}
virtual ~bad_cast() throw() {}
virtual const char* what() const throw()
#endif
{ return "bad_cast() type mis-match"; }
};
private:
ScalarType _stype;
// always reserve enough storage for std::string or double (assumed worst case)
#if __cplusplus>=201103L
struct wrap_t {
typename std::aligned_storage<detail::cmax(sizeof(std::string), sizeof(double)),
detail::cmax(alignof(std::string), alignof(double))
>::type blob[1];
} _wrap;
#else
struct wrap_t {
union blob_t {
char data[sizeof(std::string)];
double align_f; // assume std::string alignment <= 8
} blob[1];
} _wrap;
#endif
// assumed largest non-string type
typedef double _largest_blob;
template<typename T>
inline T& _as() {
return *reinterpret_cast<T*>(_wrap.blob);
}
template<typename T>
inline const T& _as() const {
return *reinterpret_cast<const T*>(_wrap.blob);
}
public:
//! Construct empty
//! @post empty()==true
AnyScalar() : _stype((ScalarType)-1) {}
//! Construct from provided value.
template<typename T>
explicit AnyScalar(T v)
{
typedef typename meta::strip_const<T>::type T2;
typedef typename detail::any_storage_type<T2>::type TT;
STATIC_ASSERT(sizeof(TT)<=sizeof(_wrap.blob));
new (_wrap.blob) TT(v);
// this line fails to compile when type T can't be mapped to one of
// the PVD scalar types.
_stype = (ScalarType)ScalarTypeID<TT>::value;
}
//! Construct from un-typed pointer.
//! Caller is responsible to ensure that buf actually points to the provided type
//! @version Added after 7.0.0
AnyScalar(ScalarType type, const void *buf);
AnyScalar(const AnyScalar& o);
#if __cplusplus>=201103L
AnyScalar(AnyScalar&& o);
#endif
inline ~AnyScalar() {clear();}
inline AnyScalar& operator=(const AnyScalar& o) {
AnyScalar(o).swap(*this);
return *this;
}
template<typename T>
inline AnyScalar& operator=(T v) {
AnyScalar(v).swap(*this);
return *this;
}
#if __cplusplus>=201103L
inline AnyScalar& operator=(AnyScalar&& o) {
clear();
swap(o);
return *this;
}
#endif
//! Reset internal state.
//! @version Added after 7.0.0
//! @post empty()==true
void clear();
void swap(AnyScalar& o);
//! Type code of contained value. Or (ScalarType)-1 is empty.
inline ScalarType type() const {
return _stype;
}
inline void* unsafe() { return _wrap.blob; }
inline const void* unsafe() const { return _wrap.blob; }
inline bool empty() const { return _stype==(ScalarType)-1; }
#if __cplusplus>=201103L
explicit operator bool() const { return !empty(); }
#else
private:
typedef void (AnyScalar::*bool_type)(AnyScalar&);
public:
operator bool_type() const { return !empty() ? &AnyScalar::swap : 0; }
#endif
//! Provide read-only access to underlying buffer.
//! For a string this is std::string::c_str().
//! @version Added after 7.0.0
const void* bufferUnsafe() const;
/** Return typed reference to wrapped value. Non-const reference allows value modification
*
* @throws bad_cast when the requested type does not match the stored type
@code
AnyScalar v(42);
v.ref<uint32>() = 43;
assert(v.ref<uint32>() == 43);
@endcode
*/
template<typename T>
inline
// T -> strip_const -> map to storage type -> add reference
typename detail::any_storage_type<typename meta::strip_const<T>::type>::type&
ref() {
typedef typename meta::strip_const<T>::type T2;
typedef typename detail::any_storage_type<T2>::type TT;
if(_stype!=(ScalarType)ScalarTypeID<TT>::value)
throw bad_cast();
return reinterpret_cast<TT&>(_wrap.blob);
}
/** Return typed reference to wrapped value. Const reference does not allow modification.
*
* @throws bad_cast when the requested type does not match the stored type
@code
const AnyScalar v(42);
assert(v.ref<uint32>() == 42);
@endcode
*/
template<typename T>
inline
// T -> strip_const -> map to storage type -> add const reference
typename meta::decorate_const<typename detail::any_storage_type<typename meta::strip_const<T>::type>::type>::type&
ref() const {
typedef typename meta::strip_const<T>::type T2;
typedef typename detail::any_storage_type<T2>::type TT;
if(_stype!=(ScalarType)ScalarTypeID<TT>::value)
throw bad_cast();
return reinterpret_cast<typename meta::decorate_const<TT>::type&>(_wrap.blob);
}
/** copy out wrapped value, with a value conversion.
*
* @throws bad_cast when empty()==true
*/
template<typename T>
inline
T as() const {
typedef typename meta::strip_const<T>::type T2;
typedef typename detail::any_storage_type<T2>::type TT;
if(_stype==(ScalarType)-1)
throw bad_cast();
TT ret;
castUnsafeV(1, (ScalarType)ScalarTypeID<T2>::value, &ret,
_stype, _wrap.blob);
return ret;
}
private:
friend epicsShareFunc std::ostream& operator<<(std::ostream& strm, const AnyScalar& v);
};
epicsShareExtern
std::ostream& operator<<(std::ostream& strm, const AnyScalar& v);
}} // namespace epics::pvData
#endif // PV_ANYSCALAR_H

View File

@@ -9,10 +9,6 @@
#ifndef BITSET_H
#define BITSET_H
#if __cplusplus>=201103L
# include <initializer_list>
#endif
#include <vector>
#include <pv/pvType.h>
@@ -50,8 +46,6 @@ namespace epics { namespace pvData {
* synchronization.
*
* Based on Java implementation.
*
* @since 7.0.0 Many methods return BitSet& to facilite method chaining.
*/
class epicsShareClass BitSet : public Serializable {
public:
@@ -71,16 +65,6 @@ namespace epics { namespace pvData {
*/
BitSet(uint32 nbits);
#if __cplusplus>=201103L
/** Initialize from a list of indicies
@code
BitSet X({1, 5});
assert(X.get(1) && X.get(5));
@endcode
*/
BitSet(std::initializer_list<uint32> I);
#endif
/**
* Destructor.
*/
@@ -92,21 +76,21 @@ namespace epics { namespace pvData {
*
* @param bitIndex the index of the bit to flip
*/
BitSet& flip(uint32 bitIndex);
void flip(uint32 bitIndex);
/**
* Sets the bit at the specified index to @c true.
*
* @param bitIndex a bit index
*/
BitSet& set(uint32 bitIndex);
void set(uint32 bitIndex);
/**
* Sets the bit specified by the index to @c false.
*
* @param bitIndex the index of the bit to be cleared
*/
BitSet& clear(uint32 bitIndex);
void clear(uint32 bitIndex);
/**
* Sets the bit at the specified index to the specified value.
@@ -183,13 +167,8 @@ namespace epics { namespace pvData {
*/
uint32 size() const;
//! Returns true if any bit is set in both *this and other
bool logical_and(const BitSet& other) const;
//! Returns true if any bit is set in both *this or other
bool logical_or(const BitSet& other) const;
/**
* Performs a bitwise <b>AND</b> of this target bit set with the
* Performs a logical <b>AND</b> of this target bit set with the
* argument bit set. This bit set is modified so that each bit in it
* has the value @c true if and only if it both initially
* had the value @c true and the corresponding bit in the
@@ -200,7 +179,7 @@ namespace epics { namespace pvData {
BitSet& operator&=(const BitSet& set);
/**
* Performs a bitwise <b>OR</b> of this bit set with the bit set
* Performs a logical <b>OR</b> of this bit set with the bit set
* argument. This bit set is modified so that a bit in it has the
* value @c true if and only if it either already had the
* value @c true or the corresponding bit in the bit set
@@ -211,7 +190,7 @@ namespace epics { namespace pvData {
BitSet& operator|=(const BitSet& set);
/**
* Performs a bitwise <b>XOR</b> of this bit set with the bit set
* Performs a logical <b>XOR</b> of this bit set with the bit set
* argument. This bit set is modified so that a bit in it has the
* value @c true if and only if one of the following
* statements holds:

File diff suppressed because it is too large Load Diff

View File

@@ -1,328 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/* Author: Michael Davidsaver */
/* wrapper around shared_ptr which tracks backwards references.
* Can help to find ref. leaks, loops, and other exciting bugs.
* See comments in sharedPtr.h
*/
#ifndef DEBUGPTR_H
#define DEBUGPTR_H
#if __cplusplus<201103L
# error c++11 required
#endif
#include <ostream>
#include <memory>
#include <set>
#include <pv/epicsException.h>
#include <shareLib.h>
//! User code should test this macro
//! before calling epics::debug::shared_ptr::show_refs()
#define HAVE_SHOW_REFS
namespace epics {
namespace debug {
struct tracker;
class shared_ptr_base;
class epicsShareClass ptr_base {
friend class shared_ptr_base;
template<typename A>
friend class shared_ptr;
template<typename A>
friend class weak_ptr;
protected:
typedef std::shared_ptr<tracker> track_t;
track_t track;
ptr_base() noexcept : track() {}
ptr_base(const track_t& track) :track(track) {}
ptr_base(const ptr_base&) = delete;
ptr_base(ptr_base&&) = delete;
ptr_base& operator=(const ptr_base&) = delete;
public:
typedef std::set<const shared_ptr_base *> ref_set_t;
void show_refs(std::ostream&, bool self=true, bool weak=false) const;
void spy_refs(ref_set_t&) const;
};
class epicsShareClass weak_ptr_base : public ptr_base {
protected:
weak_ptr_base() {}
weak_ptr_base(const track_t& track) :ptr_base(track) {}
};
class epicsShareClass shared_ptr_base : public ptr_base {
protected:
shared_ptr_base() noexcept
#ifndef EXCEPT_USE_NONE
:m_stack(), m_depth(0)
#endif
{}
shared_ptr_base(const track_t& track) :ptr_base(track)
#ifndef EXCEPT_USE_NONE
,m_stack(), m_depth(0)
#endif
{}
~shared_ptr_base() {track_clear();}
// add ourselves to tracker
void track_new();
// create new tracker if ptr!=nullptr, otherwise clear
void track_new(void* ptr);
// copy tracker and add ourself
void track_assign(const shared_ptr_base& o);
void track_clear();
void swap(shared_ptr_base& o);
void snap_stack();
#ifndef EXCEPT_USE_NONE
void *m_stack[EXCEPT_DEPTH];
int m_depth; // always <= EXCEPT_DEPTH
#endif
public:
void show_stack(std::ostream&) const;
};
template<typename T>
class shared_ptr;
template<typename T>
class weak_ptr;
template<class Base>
class enable_shared_from_this;
template<typename Store, typename Actual>
inline void
do_enable_shared_from_this(const shared_ptr<Store>& dest,
enable_shared_from_this<Actual>* self
);
template<typename T>
inline void
do_enable_shared_from_this(const shared_ptr<T>&, ...) {}
template<typename T>
class shared_ptr : public shared_ptr_base {
typedef ::std::shared_ptr<T> real_type;
real_type real;
template<typename A>
friend class shared_ptr;
template<typename A>
friend class weak_ptr;
// ctor for casts
shared_ptr(const real_type& r, const ptr_base::track_t& t)
:shared_ptr_base(t), real(r)
{track_new();}
public:
typedef typename real_type::element_type element_type;
typedef weak_ptr<T> weak_type;
// new NULL
shared_ptr() noexcept {}
// copy existing same type
shared_ptr(const shared_ptr& o) :shared_ptr_base(o.track), real(o.real) {track_new();}
// copy existing of implicitly castable type
template<typename A>
shared_ptr(const shared_ptr<A>& o) :shared_ptr_base(o.track), real(o.real) {track_new();}
// construct around new pointer
template<typename A, class ... Args>
explicit shared_ptr(A* a, Args ... args) : shared_ptr_base(), real(a, args...) {
track_new(a);
do_enable_shared_from_this(*this, a);
}
// make strong ref from weak
template<typename A>
shared_ptr(const weak_ptr<A>& o) :shared_ptr_base(o.track), real(o.real) {track_new();}
// takeover from unique_ptr
template<typename A>
shared_ptr(std::unique_ptr<A>&& a) : shared_ptr_base(), real(a.release()) {track_new();}
~shared_ptr() {}
shared_ptr& operator=(const shared_ptr& o) {
if(this!=&o) {
real = o.real;
track_assign(o);
}
return *this;
}
template<typename A>
shared_ptr& operator=(const shared_ptr<A>& o) {
if(get()!=o.get()) {
real = o.real;
track_assign(o);
}
return *this;
}
void reset() noexcept { real.reset(); track_clear(); }
template<typename A, class ... Args>
void reset(A* a, Args ... args)
{
real.reset(a, args...);
track_new(a);
do_enable_shared_from_this(*this, a);
}
void swap(shared_ptr &o) noexcept
{
if(this!=&o) {
real.swap(o.real);
shared_ptr_base::swap(o);
}
}
// proxy remaining to underlying shared_ptr
T* get() const noexcept { return real.get(); }
typename std::add_lvalue_reference<T>::type operator*() const noexcept { return *real; }
T* operator->() const noexcept { return real.get(); }
long use_count() const noexcept { return real.use_count(); }
bool unique() const noexcept { return real.unique(); }
explicit operator bool() const noexcept { return bool(real); }
bool operator==(const shared_ptr<T>& o) const { return real==o.real; }
bool operator!=(const shared_ptr<T>& o) const { return real!=o.real; }
bool operator<(const shared_ptr<T>& o) const { return real<o.real; }
template<typename A>
bool owner_before(const shared_ptr<A>& o) { return real.owner_before(o); }
template<typename A>
bool owner_before(const weak_ptr<A>& o) { return real.owner_before(o); }
template<typename TO, typename FROM>
friend
shared_ptr<TO> static_pointer_cast(const shared_ptr<FROM>& src);
template<typename TO, typename FROM>
friend
shared_ptr<TO> const_pointer_cast(const shared_ptr<FROM>& src);
template<typename TO, typename FROM>
friend
shared_ptr<TO> dynamic_pointer_cast(const shared_ptr<FROM>& src);
template<typename Store, typename Actual>
friend void
do_enable_shared_from_this(const shared_ptr<Store>& dest,
enable_shared_from_this<Actual>* self
);
};
template<typename TO, typename FROM>
shared_ptr<TO> static_pointer_cast(const shared_ptr<FROM>& src) {
return shared_ptr<TO>(std::static_pointer_cast<TO>(src.real), src.track);
}
template<typename TO, typename FROM>
shared_ptr<TO> const_pointer_cast(const shared_ptr<FROM>& src) {
return shared_ptr<TO>(std::const_pointer_cast<TO>(src.real), src.track);
}
template<typename TO, typename FROM>
shared_ptr<TO> dynamic_pointer_cast(const shared_ptr<FROM>& src) {
return shared_ptr<TO>(std::dynamic_pointer_cast<TO>(src.real), src.track);
}
template<typename T>
class weak_ptr : public weak_ptr_base {
typedef ::std::weak_ptr<T> real_type;
real_type real;
template<typename A>
friend class shared_ptr;
template<typename A>
friend class weak_ptr;
public:
typedef typename real_type::element_type element_type;
typedef weak_ptr<T> weak_type;
// new NULL
weak_ptr() noexcept {}
// copy existing same type
weak_ptr(const weak_ptr& o) :weak_ptr_base(o.track), real(o.real) {}
// copy existing of similar type
template<typename A>
weak_ptr(const weak_ptr<A>& o) :weak_ptr_base(o.track), real(o.real) {}
// create week ref from strong ref
template<typename A>
weak_ptr(const shared_ptr<A>& o) :weak_ptr_base(o.track), real(o.real) {}
~weak_ptr() {}
weak_ptr& operator=(const weak_ptr& o) {
if(this!=&o) {
real = o.real;
track = o.track;
}
return *this;
}
template<typename A>
weak_ptr& operator=(const shared_ptr<A>& o) {
real = o.real;
track = o.track;
return *this;
}
shared_ptr<T> lock() const noexcept { return shared_ptr<T>(real.lock(), track); }
void reset() noexcept { track.reset(); real.reset(); }
long use_count() const noexcept { return real.use_count(); }
bool unique() const noexcept { return real.unique(); }
};
template<class Base>
class enable_shared_from_this {
mutable weak_ptr<Base> xxInternalSelf;
template<typename Store, typename Actual>
friend
void
do_enable_shared_from_this(const shared_ptr<Store>& dest,
enable_shared_from_this<Actual>* self
);
public:
shared_ptr<Base> shared_from_this() const {
return shared_ptr<Base>(xxInternalSelf);
}
};
template<typename Store, typename Actual>
inline void
do_enable_shared_from_this(const shared_ptr<Store>& dest,
enable_shared_from_this<Actual>* self
)
{
shared_ptr<Actual> actual(dynamic_pointer_cast<Actual>(dest));
if(!actual)
throw std::logic_error("epics::debug::enabled_shared_from_this fails");
self->xxInternalSelf = actual;
}
}} // namespace epics::debug
template<typename T>
inline std::ostream& operator<<(std::ostream& strm, const epics::debug::shared_ptr<T>& ptr)
{
strm<<ptr.get();
return strm;
}
#endif // DEBUGPTR_H

40
src/misc/pv/destroyable.h Normal file
View File

@@ -0,0 +1,40 @@
/* destroyable.h */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mse
*/
#ifndef DESTROYABLE_H
#define DESTROYABLE_H
#include <pv/sharedPtr.h>
#include <shareLib.h>
namespace epics { namespace pvData {
/**
* @brief Instance declaring destroy method.
*
* @author mse
*/
class epicsShareClass Destroyable {
public:
POINTER_DEFINITIONS(Destroyable);
/**
* Destroy this instance.
*/
virtual void destroy() = 0;
protected:
/**
* Do not allow delete on this instance and derived classes, destroy() must be used instead.
*/
virtual ~Destroyable() {};
};
}}
#endif /* DESTROYABLE_H */

View File

@@ -33,6 +33,10 @@
#ifndef EPICSEXCEPTION_H_
#define EPICSEXCEPTION_H_
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <stdexcept>
#include <string>
#include <cstdio>
@@ -177,7 +181,7 @@ do { \
#define PRINT_EXCEPTION(EI) PRINT_EXCEPTION2(EI,stderr)
#if !defined(__GNUC__) || __GNUC__ < 4
#ifndef __GNUC__
# define SHOW_EXCEPTION(EI) ::epics::pvData::detail::showException(EI)
#else
# define SHOW_EXCEPTION(EI) \
@@ -207,7 +211,7 @@ public:
/**
* Constructor.
*/
explicit BaseException(const std::string& msg) : std::logic_error(msg) {}
explicit BaseException(const std::string msg) : std::logic_error(msg) {}
/**
* Destructor.
@@ -229,6 +233,7 @@ private:
#endif
#define THROW_BASE_EXCEPTION(msg) THROW_EXCEPTION2(::epics::pvData::BaseException, msg)
#define THROW_BASE_EXCEPTION_CAUSE(msg, cause) THROW_EXCEPTION2(::epics::pvData::BaseException, msg)
}
}

90
src/misc/pv/executor.h Normal file
View File

@@ -0,0 +1,90 @@
/* executor.h */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mrk
*/
#ifndef EXECUTOR_H
#define EXECUTOR_H
#include <memory>
#include <pv/pvType.h>
#include <pv/lock.h>
#include <pv/event.h>
#include <pv/thread.h>
#include <pv/sharedPtr.h>
#include <shareLib.h>
namespace epics { namespace pvData {
class Command;
class Executor;
typedef std::tr1::shared_ptr<Command> CommandPtr;
typedef std::tr1::shared_ptr<Executor> ExecutorPtr;
/**
* @brief A command to be called by Executor
*
*/
class epicsShareClass Command {
public:
POINTER_DEFINITIONS(Command);
/**
*
* Destructor
*/
virtual ~Command(){}
/**
*
* The command that is executed.
*/
virtual void command() = 0;
private:
CommandPtr next;
friend class Executor;
};
/**
* @brief A class that executes commands.
*
*/
class epicsShareClass Executor : public Runnable{
public:
POINTER_DEFINITIONS(Executor);
/**
* Constructor
*
* @param threadName name for the executor thread.
* @param priority The thread priority.
*/
Executor(std::string const & threadName,ThreadPriority priority);
/**
* Destructor
*/
~Executor();
/**
*
* Request to execute a command.
* @param command A shared pointer to the command instance.
*/
void execute(CommandPtr const &command);
/**
*
* The thread run method.
*/
virtual void run();
private:
CommandPtr head;
CommandPtr tail;
epics::pvData::Mutex mutex;
epics::pvData::Event moreWork;
epics::pvData::Event stopped;
epics::pvData::Thread thread;
};
}}
#endif /* EXECUTOR_H */

View File

@@ -0,0 +1,32 @@
/* localStaticLock.h */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mse
*/
#ifndef LOCALSTATICLOCK_H
#define LOCALSTATICLOCK_H
#include <pv/lock.h>
#include <shareLib.h>
epicsShareExtern epics::pvData::Mutex& getLocalStaticInitMutex();
#if defined(__GNUC__) && __GNUC__ >= 4
// noop
#define LOCAL_STATIC_LOCK
#else
#define LOCAL_STATIC_LOCK epics::pvData::Lock localStaticInitMutexLock(getLocalStaticInitMutex());
#endif
static class epicsShareClass MutexInitializer {
public:
MutexInitializer ();
~MutexInitializer ();
} localStaticMutexInitializer; // Note object here in the header.
#endif /* LOCALSTATICLOCK_H */

View File

@@ -33,8 +33,7 @@ typedef epicsMutex Mutex;
* This is based on item 14 of
* * Effective C++, Third Edition, Scott Meyers
*/
class Lock {
EPICS_NOT_COPYABLE(Lock)
class epicsShareClass Lock : private NoDefaultMethods {
public:
/**
* Constructor

128
src/misc/pv/messageQueue.h Normal file
View File

@@ -0,0 +1,128 @@
/* messageQueue.h */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mrk
*/
#ifndef MESSAGEQUEUE_H
#define MESSAGEQUEUE_H
#include <memory>
#include <vector>
#include <cstddef>
#include <stdexcept>
#include <pv/pvType.h>
#include <pv/requester.h>
#include <pv/queue.h>
#include <shareLib.h>
namespace epics { namespace pvData {
class MessageNode;
class MessageQueue;
typedef std::tr1::shared_ptr<MessageNode> MessageNodePtr;
typedef std::vector<MessageNodePtr> MessageNodePtrArray;
typedef std::tr1::shared_ptr<MessageQueue> MessageQueuePtr;
/**
* @brief A node that can be put on a MessageQueue.
*
*/
class epicsShareClass MessageNode {
public:
/**
* Constructor
*/
MessageNode();
/**
*
* Get the message value.
* @return The message value.
*/
std::string getMessage() const;
/**
* Get the message type.
* @return The message type which is defined in Requester.
*/
MessageType getMessageType() const;
private:
std::string message;
MessageType messageType;
friend class MessageQueue;
};
/**
* @brief A bounded queue for messages.
*
*
*/
class epicsShareClass MessageQueue : public Queue<MessageNode> {
public:
POINTER_DEFINITIONS(MessageQueue);
/**
* Factory method to create a MessageQueue.
* @param size The number of MessageNodes in the queue.
* @return shared_ptr to MessageQueue.
*/
static MessageQueuePtr create(int size);
/**
* Constructor
* @param nodeArray an array of shared_ptr to MessageNodes,
*/
MessageQueue(MessageNodePtrArray &nodeArray);
/**
* Destructor
*/
virtual ~MessageQueue();
/**
* get the next MessageNode of the queue.
* @return A shared_ptr to the MessageNode.
* This will be a null pointer if queue is empty.
* If get is successful then release for this MessageNode
* must be called before another get can be issued.
*/
MessageNodePtr &get();
/**
* Release the MessageNode that was returned by the previous call to get.
*/
void release();
/**
*
* put a message into the message queue
* @param message The message string.
* @param messageType The message type as defined in Requester,
* @param replaceLast If true and queue is full then replace.
* @return (false,true) if a message (was not, was) put in queiue.
*/
bool put(std::string message,MessageType messageType,bool replaceLast);
/**
* Is queue empty?
* @return (false,true) if (is not, is) empty.
*/
bool isEmpty() ;
/**
* Is queue full?
* @return (false,true) if (is not, is) full.
*/
bool isFull() ;
/**
*
* Clear number of times queue was overrun and return the number
* of times the queue was overrun.
*/
int getClearOverrun();
private:
MessageNodePtr nullNode;
MessageNodePtr lastGet;
MessageNodePtr lastPut;
uint32 overrun;
};
}}
#endif /* MESSAGEQUEUE_H */

View File

@@ -11,54 +11,30 @@
#include <shareLib.h>
/** @macro EPICS_NOT_COPYABLE(CLASS)
* @brief Disable implicit copyable
*
* Prevent the default copy constructor and assignment
* operator from being usable.
*
* For >= C++11 explicitly disable. Attempts to copy/assign will
* fail to compile.
*
* For C++98 make these private, and don't implement them.
* User code will fail to compile, implementation code will fail to link.
@code
struct MyClass {
EPICS_NOT_COPYABLE(MyClass)
public:
...
};
@endcode
*
* @note This macro contains 'private:'.
namespace epics { namespace pvData {
/* This is based on Item 6 of
* Effective C++, Third Edition, Scott Meyers
*/
#if __cplusplus>=201103L
# define EPICS_NOT_COPYABLE(CLASS) private: CLASS(const CLASS&) = delete; CLASS& operator=(const CLASS&) = delete;
#else
# define EPICS_NOT_COPYABLE(CLASS) private: CLASS(const CLASS&); CLASS& operator=(const CLASS&);
#endif
namespace epics { namespace pvData {
/**
* @brief Base class for not allowing default methods.
*
* Note that copy constructor a copy methods are declared private.
*
* @deprecated Deprecated in favor of EPICS_NOT_COPYABLE() pvDataCPP 7.0.0
*/
class NoDefaultMethods {
public:
NoDefaultMethods() {}
private:
#if __cplusplus>=201103L
NoDefaultMethods(const NoDefaultMethods&) = delete;
NoDefaultMethods & operator=(const NoDefaultMethods &) = delete;
#else
class epicsShareClass NoDefaultMethods {
protected:
/**
* Constructor
*/
NoDefaultMethods(){};
/**
* Destructor
*/
~NoDefaultMethods(){}
private:
// do not implement
NoDefaultMethods(const NoDefaultMethods&);
NoDefaultMethods & operator=(const NoDefaultMethods &);
#endif
};
}}

View File

@@ -1,188 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#ifndef PVUNITTEST_H
#define PVUNITTEST_H
#include <sstream>
#include <typeinfo>
#include <epicsUnitTest.h>
#include <pv/sharedPtr.h>
#include <pv/epicsException.h>
#include <pv/pvData.h>
namespace detail {
template<class C, void (C::*M)()>
void test_method(const char *kname, const char *mname)
{
try {
testDiag("------- %s::%s --------", kname, mname);
C inst;
(inst.*M)();
} catch(std::exception& e) {
PRINT_EXCEPTION(e);
testAbort("unexpected exception: %s", e.what());
}
}
class epicsShareClass testPassx
{
std::ostringstream strm;
const bool dotest, pass;
bool alive;
public:
testPassx() :dotest(false), pass(false), alive(true) {}
explicit testPassx(bool r) :dotest(true), pass(r), alive(true) {}
~testPassx();
template<typename T>
inline testPassx& operator<<(const T& v) {
strm<<v;
return *this;
}
// allow testPassx to be returned
// move ctor masquerading as copy ctor
testPassx(testPassx& o);
private:
testPassx& operator=(const testPassx&);
};
template<typename LHS, typename RHS>
inline testPassx testEqualx(const char *nLHS, const char *nRHS, const LHS& l, const RHS& r)
{
return testPassx(l==r)<<nLHS<<" ("<<l<<") == "<<nRHS<<" ("<<r<<")";
}
template<typename LHS, typename RHS>
inline testPassx testNotEqualx(const char *nLHS, const char *nRHS, const LHS& l, const RHS& r)
{
return testPassx(l!=r)<<nLHS<<" ("<<l<<") != "<<nRHS<<" ("<<r<<")";
}
}//namespace detail
/** @defgroup testhelpers Unit testing helpers
*
* Helper functions for writing unit tests.
*
@include unittest.cpp
*
* @{
*/
/** Run a class method as a test.
*
* Each invocation of TEST_METHOD() constructs a new instance of 'klass' on the stack.
* Thus constructor and destructor can be used for common test setup and tear down.
@code
namespace { // anon
struct MyTest {
MyTest() { } // setup
~MyTest() { } // tear down
void test1() {}
void test2() {}
};
} // namespace anon
MAIN(somename) {
testPlan(0);
TEST_METHOD(MyTest, test1)
TEST_METHOD(MyTest, test2)
return testDone();
}
@endcode
*/
#define TEST_METHOD(klass, method) ::detail::test_method<klass, &klass::method>(#klass, #method)
/** Compare equality. print left and right hand values and expression strings
*
@code
int x=5;
testEqual(x, 5);
// prints "ok 1 - x (5) == 5 (5)\n"
testEqual(x, 6)<<" oops";
// prints "not ok 1 - x (5) == 6 (6) oops\n"
@endcode
*/
#define testEqual(LHS, RHS) ::detail::testEqualx(#LHS, #RHS, LHS, RHS)
#define testNotEqual(LHS, RHS) ::detail::testNotEqualx(#LHS, #RHS, LHS, RHS)
/** Pass/fail from boolean
*
@code
bool y=true;
testTrue(y);
// prints "ok 1 - y\n"
testTrue(!y)<<" oops";
// prints "not ok 1 - !y oops\n"
@endcode
*/
#define testTrue(B) ::detail::testPassx(!!(B))<<#B
/** Test that a given block throws an exception
*
@code
testThrows(std::runtime_error, somefunc(5))
@endcode
*/
#define testThrows(EXC, CODE) try{ CODE; testFail("unexpected success of " #CODE); }catch(EXC& e){testPass("catch expected exception: %s", e.what());}
/** Print test output w/o testing
*
@code
testShow()<<"Foo";
@endcode
*/
#define testShow() ::detail::testPassx()
/** Compare value of PVStructure field
*
@code
PVStructurePtr x(.....);
testFieldEqual<epics::pvData::PVInt>(x, "alarm.severity", 1);
@endcode
*/
template<typename PVD>
::detail::testPassx
testFieldEqual(const std::tr1::shared_ptr<const epics::pvData::PVStructure>& val, const char *name, typename PVD::value_type expect)
{
if(!val) {
return ::detail::testPassx(false)<<" null structure pointer";
}
typename PVD::const_shared_pointer fval(val->getSubField<PVD>(name));
if(!fval) {
epics::pvData::PVUnion::const_shared_pointer uval(val->getSubField<epics::pvData::PVUnion>(name));
if(uval)
fval = uval->get<PVD>();
}
if(!fval) {
return ::detail::testPassx(false)<<" field '"<<name<<"' with type "<<typeid(PVD).name()<<" does not exist";
} else {
typename PVD::value_type actual(fval->get());
return ::detail::testPassx(actual==expect)<<name<<" ("<<actual<<") == "<<expect;
}
}
template<typename PVD>
::detail::testPassx
testFieldEqual(const std::tr1::shared_ptr<const epics::pvData::PVStructure>& val, const char *name, typename PVD::const_svector expect)
{
if(!val) {
return ::detail::testPassx(false)<<" null structure pointer";
}
typename PVD::const_shared_pointer fval(val->getSubField<PVD>(name));
if(!fval) {
return ::detail::testPassx(false)<<" field '"<<name<<"' with type "<<typeid(PVD).name()<<" does not exist";
} else {
typename PVD::const_svector actual(fval->view());
return ::detail::testPassx(actual==expect)<<name<<" ("<<actual<<") == "<<expect;
}
}
/** @} */
#endif // PVUNITTEST_H

183
src/misc/pv/queue.h Normal file
View File

@@ -0,0 +1,183 @@
/* queue.h */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mrk
*/
#ifndef QUEUE_H
#define QUEUE_H
#include <vector>
#include <cstddef>
#include <stdexcept>
#include <pv/sharedPtr.h>
namespace epics { namespace pvData {
/**
* @brief Template class for a bounded queue.
*
* An instance can make a queueElement be any class desired
* but must create a std::vector of shared_ptr to queueElements.
*/
template <typename T>
class Queue
{
public:
POINTER_DEFINITIONS(Queue);
typedef std::tr1::shared_ptr<T> queueElementPtr;
typedef std::vector<queueElementPtr> queueElementPtrArray;
/**
* Constructor
* @param elementArray The vector of shared_ptr to queue elements.
*/
Queue(queueElementPtrArray & elementArray);
/**
* Destructor
*/
virtual ~Queue();
/**
* Clear the queue.
*/
void clear();
/**
* get the capacity of the queue, i. e. number of queue elements,
* @return The capacity.
*/
int capacity();
/**
* Get the number of free elements in the queue.
* @return The number.
*/
int getNumberFree();
/**
* Get the number of used elements in the queue.
* This is the number that have been setUsed but not released.
* @return The number.
*/
int getNumberUsed();
/**
* Get the next free element.
* @return a shared_ptr to the queue element.
* This is null if queue was full.
*/
queueElementPtr & getFree();
/**
* Set the element returned by getFree as used.
* Until this is called getUsed will not return it.
* @param element The element. It must be the element returned
* by the most recent call to getUsed.
*/
void setUsed(queueElementPtr const &element);
/**
* Get the oldest used element;
* @return a shared_ptr to the queue element.
* This is null if no used element is available.`
*/
queueElementPtr & getUsed();
/**
* Release the element obtained by the most recent call to getUsed.
* @param element The element.
*/
void releaseUsed(queueElementPtr const &element);
private:
queueElementPtr nullElement;
queueElementPtrArray elements;
// TODO use size_t instead
int size;
int numberFree;
int numberUsed;
int nextGetFree;
int nextSetUsed;
int nextGetUsed;
int nextReleaseUsed;
};
template <typename T>
Queue<T>::Queue(std::vector<queueElementPtr> &xxx)
: size(static_cast<int>(xxx.size())),
numberFree(size),
numberUsed(0),
nextGetFree(0),
nextSetUsed(0),
nextGetUsed(0),
nextReleaseUsed(0)
{
elements.swap(xxx);
}
template <typename T>
Queue<T>::~Queue(){}
template <typename T>
int Queue<T>::capacity(){return size;}
template <typename T>
int Queue<T>::getNumberFree(){return numberFree;}
template <typename T>
int Queue<T>::getNumberUsed(){return numberUsed;}
template <typename T>
void Queue<T>::clear()
{
numberFree = size;
numberUsed = 0;
nextGetFree = 0;
nextSetUsed = 0;
nextGetUsed = 0;
nextReleaseUsed = 0;
}
template <typename T>
std::tr1::shared_ptr<T> & Queue<T>::getFree()
{
if(numberFree==0) return nullElement;
numberFree--;
int ind = nextGetFree;
std::tr1::shared_ptr<T> queueElement = elements[nextGetFree++];
if(nextGetFree>=size) nextGetFree = 0;
return elements[ind];
}
template <typename T>
void Queue<T>::setUsed(std::tr1::shared_ptr<T> const &element)
{
if(element!=elements[nextSetUsed++]) {
throw std::logic_error("not correct queueElement");
}
numberUsed++;
if(nextSetUsed>=size) nextSetUsed = 0;
}
template <typename T>
std::tr1::shared_ptr<T> & Queue<T>::getUsed()
{
if(numberUsed==0) return nullElement;
int ind = nextGetUsed;
std::tr1::shared_ptr<T> queueElement = elements[nextGetUsed++];
if(nextGetUsed>=size) nextGetUsed = 0;
return elements[ind];
}
template <typename T>
void Queue<T>::releaseUsed(std::tr1::shared_ptr<T> const &element)
{
if(element!=elements[nextReleaseUsed++]) {
throw std::logic_error(
"not queueElement returned by last call to getUsed");
}
if(nextReleaseUsed>=size) nextReleaseUsed = 0;
numberUsed--;
numberFree++;
}
}}
#endif /* QUEUE_H */

View File

@@ -1,156 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#ifndef REFTRACK_H
#define REFTRACK_H
/** @page pvd_reftrack RefTrack
*
* reftrack.h is a utility for listing, finding, and reading global atomic counters.
* By convention used to expose object instance counters as a way of detecting (slow)
* reference/resource leaks before they cause problems.
*
* cf. the IOC shell commands "refshow", "refsave", and "refdiff".
*
* Example usage:
*
* @code
* // my header.h
* struct MyClass {
* MyClass();
* ~MyClass();
* static size_t num_instances;
* ...
* };
* ...
* // my src.cpp
* size_t MyClass::num_instances;
* MyClass::MyClass() {
* REFTRACE_INCREMENT(num_instances);
* }
* MyClass::~MyClass() {
* REFTRACE_DECREMENT(num_instances);
* }
* // in some IOC registrar or global ctor
* registerRefCounter("MyClass", &MyClass::num_instances);
* @endcode
*/
#ifdef __cplusplus
#include <map>
#include <string>
#include <ostream>
#include <stdlib.h>
#include <epicsVersion.h>
#include <epicsAtomic.h>
#define REFTRACE_INCREMENT(counter) ::epics::atomic::increment(counter)
#define REFTRACE_DECREMENT(counter) ::epics::atomic::decrement(counter)
#include <shareLib.h>
namespace epics {
//! Register new global reference counter
epicsShareFunc
void registerRefCounter(const char *name, const size_t* counter);
//! Remove registration of global reference counter (if dynamically allocated)
epicsShareFunc
void unregisterRefCounter(const char *name, const size_t* counter);
//! Fetch current value of single reference counter
epicsShareFunc
size_t readRefCounter(const char *name);
//! Represent a snapshot of many reference counters
class epicsShareClass RefSnapshot
{
public:
//! A single count
struct Count {
size_t current;
long delta; //!< current - previous
Count() :current(0u), delta(0) {}
explicit Count(size_t c, long d) :current(c), delta(d) {}
bool operator==(const Count& o) const
{ return current==o.current && delta==o.delta; }
};
private:
typedef std::map<std::string, Count> cnt_map_t;
cnt_map_t counts;
public:
typedef cnt_map_t::const_iterator iterator;
typedef cnt_map_t::const_iterator const_iterator;
/** Fetch values of all reference counters.
*
* This involves many atomic reads, not a single operation.
*/
void update();
const Count& operator[](const std::string& name) const;
iterator begin() const { return counts.begin(); }
iterator end() const { return counts.end(); }
size_t size() const { return counts.size(); }
inline void swap(RefSnapshot& o)
{
counts.swap(o.counts);
}
/** Compute the difference lhs - rhs
*
* Returned RefSnapshot has Count::current=lhs.current
* and Count::delta= lhs.current - rhs.current
*/
RefSnapshot operator-(const RefSnapshot& rhs) const;
};
//! Print all counters with a non-zero delta
epicsShareFunc
std::ostream& operator<<(std::ostream& strm, const RefSnapshot& snap);
//! Helper to run a thread which periodically prints (via show() )
//! global reference counter deltas.
class epicsShareClass RefMonitor
{
struct Impl;
Impl *impl;
public:
RefMonitor();
virtual ~RefMonitor();
void start(double period=10.0);
void stop();
bool running() const;
//! call show() with current snapshot
void current();
protected:
//! Default prints to stderr
//! @param complete when false show only non-zero delta, when true show non-zero count or delta
virtual void show(const RefSnapshot& snap, bool complete=false);
};
} // namespace epics
extern "C" {
#endif /* __cplusplus */
/** Fetch and print current snapshot
* @return NULL or a char* which must be free()'d
*/
char* epicsRefSnapshotCurrent();
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif // REFTRACK_H

71
src/misc/pv/requester.h Normal file
View File

@@ -0,0 +1,71 @@
/* requester.h */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mrk
*/
#ifndef REQUESTER_H
#define REQUESTER_H
#include <string>
#include <pv/pvType.h>
#include <pv/sharedPtr.h>
#include <shareLib.h>
namespace epics { namespace pvData {
class Requester;
typedef std::tr1::shared_ptr<Requester> RequesterPtr;
enum MessageType {
infoMessage,warningMessage,errorMessage,fatalErrorMessage
};
#define MESSAGE_TYPE_COUNT 4
epicsShareExtern std::string getMessageTypeName(MessageType messageType);
/**
* @brief Callback class for passing messages to a requester.
*
* This is used by many other classes and also extended by other classes.
* The request is passed a message and a messageType.
* A message is just a string and a messageType is:
@code
enum MessageType {
infoMessage,warningMessage,errorMessage,fatalErrorMessage
};
@endcode
*
*/
class epicsShareClass Requester {
public:
POINTER_DEFINITIONS(Requester);
/**
* Destructor
*/
virtual ~Requester(){}
/**
* The requester must have a name.
* @return The requester's name.
*/
virtual std::string getRequesterName() = 0;
/**
*
* A message for the requester.
* @param message The message.
* @param messageType The type of message:
@code
enum MessageType {
infoMessage,warningMessage,errorMessage,fatalErrorMessage
};
@endcode
*/
virtual void message(std::string const & message,MessageType messageType);
};
}}
#endif /* REQUESTER_H */

View File

@@ -16,14 +16,6 @@
#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
namespace epics { namespace pvData {
class SerializableControl;
@@ -55,7 +47,11 @@ namespace epics { namespace pvData {
* @param size The number of bytes.
*/
virtual void ensureBuffer(std::size_t size) =0;
virtual void alignBuffer(std::size_t alignment) PVD_DEPRECATED("Deprecated for lack of use") {}
/**
* Add pad bytes to buffer.
* @param alignment alignment required.
*/
virtual void alignBuffer(std::size_t alignment) =0;
/**
* Method for serializing primitive array data.
* Hook for supplying custom serialization implementation.
@@ -102,8 +98,14 @@ namespace epics { namespace pvData {
* @param size The number of bytes.
*/
virtual void ensureData(std::size_t size) =0;
// Deprecated for lack of use
virtual void alignData(std::size_t alignment) PVD_DEPRECATED("Deprecated for lack of use") {};
/**
* Align buffer.
* Note that this takes care only current buffer alignment.
* If streaming protocol is used,
* care must be taken that entire stream is aligned.
* @param alignment size in bytes, must be power of two.
*/
virtual void alignData(std::size_t alignment) =0;
/**
* Method for deserializing array data.
* Hook for supplying custom deserialization implementation.
@@ -148,7 +150,7 @@ namespace epics { namespace pvData {
*/
virtual void serialize(ByteBuffer *buffer,
SerializableControl *flusher) const = 0;
/**
/**
* Deserialize buffer.
* @param buffer serialization buffer.
* @param flusher deserialization control.
@@ -211,7 +213,7 @@ namespace epics { namespace pvData {
*/
virtual void serialize(ByteBuffer *buffer,
SerializableControl *flusher,BitSet *bitSet) const = 0;
/**
/**
* Deserialize buffer.
* @param buffer serialization buffer.
* @param flusher deserialization control.

View File

@@ -26,8 +26,7 @@ namespace epics {
* @brief Serialization helper.
*
*/
class epicsShareClass SerializeHelper {
EPICS_NOT_COPYABLE(SerializeHelper)
class epicsShareClass SerializeHelper : public NoDefaultMethods {
public:
/**

View File

@@ -9,9 +9,7 @@
#ifndef SHAREDPTR_H
#define SHAREDPTR_H
#include <memory> /* for auto_ptr */
/** @file sharedPtr.h
/*
* Pulls in the std::tr1 namespace with the following names
*
* class shared_ptr
@@ -25,58 +23,12 @@
* function enable_shared_from_this
*/
/* where should we look? (In decending order of preference)
*
* # manual (per source file) selection
* # c++11 version of <memory>, then alias into tr1
* # <tr1/memory>
* # boost version of tr1/memory
*/
// where should we look?
/* Debugging shared_ptr with debugPtr.h requires >= c++11
*
* Define DEBUG_SHARED_PTR globally to cause epics::debug::shared_ptr
* to be injected as std::tr1::shared_ptr and the macro
* HAVE_SHOW_REFS will be defined.
*
* epics::debug::shared_ptr wraps std::shared_ptr with additional
* tracking of backwards references.
* std::shared_ptr::use_count() gives the number of shared_ptr
* (strong refs) to the referenced object.
*
* If use_count()==5 then epics::debug::shared_ptr::show_refs() will print
* 5 lines of the format
*
* # <addr>: <IP0> <IP1> ...
*
* Given the numberic address of each shared_ptr as well as the call stack
* at the point where it was initialized.
* Use the 'addr2line' utility to interpret the stack addresses.
*
* On linux w/ ASLR it is necessary to turn on static linking to meaningfully
* interpret call stack addresses.
* Append "STATIC_BUILD=YES" to configure/CONFIG_SITE
*/
//#define DEBUG_SHARED_PTR
#if defined(SHARED_FROM_MANUAL)
// define SHARED_FROM_MANUAL if from some reason it is desirable to manually select
// which shared_ptr implementation to use
#elif __cplusplus>=201103L || (defined(_MSC_VER) && (_MSC_VER>=1600)) || defined(_LIBCPP_VERSION)
// MSVC has been bad about incrementing __cplusplus, even when new features are added. shared_ptr from MSVC 2010
// the llvm libc++ doesn't bother with tr1, and puts shared_ptr in std:: even with -std=c++98
# define SHARED_FROM_STD
#elif defined(__GNUC__) && __GNUC__>=4 && !defined(vxWorks)
#if defined(__GNUC__) && __GNUC__>=4 && !defined(vxWorks)
// GCC >=4.0.0
# define SHARED_FROM_TR1
#elif defined(_MSC_VER) && _MSC_VER==1500
// MSVC 2009 (eg. Visual C++ for Python 2.7)
// Dinkumware _CPPLIB_VER=505
// Has std::tr1::shared_ptr in <memory>
# define SHARED_TR1_FROM_STD
#elif defined(_MSC_VER) && (_MSC_VER>1500 || defined(_HAS_TR1))
// MSVC > 2008, or 2008 w/ SP1
# define SHARED_FROM_TR1
@@ -85,51 +37,34 @@
# define SHARED_FROM_BOOST
#endif
// go and get it
#if defined(_MSC_VER) && (_MSC_VER>=1600)
// MSVC 2010 has it in <memory>
# undef SHARED_FROM_BOOST
# undef SHARED_FROM_TR1
#endif
#if defined(SHARED_FROM_MANUAL)
// no-op
#elif defined(SHARED_FROM_STD)
#if defined(__clang__)
# undef SHARED_FROM_BOOST
# undef SHARED_FROM_TR1
#include <memory>
#ifndef DEBUG_SHARED_PTR
namespace std {
// import std classes into std::tr1
namespace std {
namespace tr1 {
using ::std::shared_ptr;
using ::std::weak_ptr;
using ::std::static_pointer_cast;
using ::std::dynamic_pointer_cast;
using ::std::const_pointer_cast;
using ::std::enable_shared_from_this;
using ::std::bad_weak_ptr;
using std::shared_ptr;
using std::weak_ptr;
using std::static_pointer_cast;
using std::dynamic_pointer_cast;
using std::const_pointer_cast;
using std::enable_shared_from_this;
}
}
#endif
#else // DEBUG_SHARED_PTR
// go and get it
#include "debugPtr.h"
namespace std {
namespace tr1 {
using ::epics::debug::shared_ptr;
using ::epics::debug::weak_ptr;
using ::epics::debug::static_pointer_cast;
using ::epics::debug::dynamic_pointer_cast;
using ::epics::debug::const_pointer_cast;
using ::epics::debug::enable_shared_from_this;
using ::std::bad_weak_ptr;
}
}
#endif // DEBUG_SHARED_PTR
#elif defined(SHARED_TR1_FROM_STD)
# include <memory>
#elif defined(SHARED_FROM_TR1)
#if defined(SHARED_FROM_TR1)
# include <tr1/memory>
#elif defined(SHARED_FROM_BOOST)
@@ -142,15 +77,12 @@ namespace std {
# include <boost/tr1/memory.hpp>
#else
# error No shared_ptr selection
// eventually...
# include <memory>
#endif
// cleanup
#ifdef SHARED_FROM_STD
# undef SHARED_FROM_STD
#endif
#ifdef SHARED_FROM_TR1
# undef SHARED_FROM_TR1
#endif
@@ -159,78 +91,10 @@ namespace std {
# undef SHARED_FROM_BOOST
#endif
namespace detail {
template<typename T>
struct ref_shower {
const std::tr1::shared_ptr<T>& ptr;
bool self, weak;
ref_shower(const std::tr1::shared_ptr<T>& ptr, bool self, bool weak) :ptr(ptr),self(self),weak(weak) {}
};
}
/** Print a list (one per line) of shared_ptr which refer to the same object
*
* @param ptr Use the object pointed to by this shared_ptr
* @param self include or omit a line for this shared_ptr
* @param weak include a line for each weak_ptr (not implemented)
@code
shared_ptr<int> x;
std::cout << show_referrers(x);
@endcode
*/
template<typename T>
inline ::detail::ref_shower<T> show_referrers(const std::tr1::shared_ptr<T>& ptr, bool self=true, bool weak=false)
{
return ::detail::ref_shower<T>(ptr, self, weak);
}
namespace std{
template<typename T>
inline std::ostream& operator<<(std::ostream& strm, const ::detail::ref_shower<T>& refs)
{
#ifdef HAVE_SHOW_REFS
refs.ptr.show_refs(strm, refs.self, refs.weak);
#endif // HAVE_SHOW_REFS
return strm;
}
}//namespace std
#define POINTER_DEFINITIONS(clazz) \
typedef std::tr1::shared_ptr<clazz> shared_pointer; \
typedef std::tr1::shared_ptr<const clazz> const_shared_pointer; \
typedef std::tr1::weak_ptr<clazz> weak_pointer; \
typedef std::tr1::weak_ptr<const clazz> const_weak_pointer
namespace epics{
/** A semi-hack to help with migration from std::auto_ptr to std::unique_ptr,
* and avoid copious deprecation warning spam
* which may be hiding legitimate issues.
*
* Provides epics::auto_ptr<T> and epics::swap()
*
* epics::auto_ptr<T> is std::auto_ptr<T> for c++98
* and std::unique_ptr<T> for >= c++11.
*
* epics::swap() is the only supported operation.
* copy/assignment/return are not supported
* (use auto_ptr or unique_ptr explicitly).
*/
#if __cplusplus>=201103L
template<typename T>
using auto_ptr = std::unique_ptr<T>;
template<typename T>
static inline void swap(auto_ptr<T>& lhs, auto_ptr<T>& rhs) {
lhs.swap(rhs);
}
#else
using std::auto_ptr;
template<typename T>
static inline void swap(auto_ptr<T>& lhs, auto_ptr<T>& rhs) {
auto_ptr<T> temp(lhs);
lhs = rhs;
rhs = temp;
}
#endif
}
#endif // SHAREDPTR_H

View File

@@ -6,15 +6,15 @@
#ifndef SHAREDVECTOR_H
#define SHAREDVECTOR_H
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <ostream>
#include <algorithm>
#include <stdexcept>
#include <iterator>
#if __cplusplus>=201103L
# include <initializer_list>
#endif
#include <cassert>
#include "pv/sharedPtr.h"
@@ -70,24 +70,18 @@ namespace detail {
*/
public:
#if __cplusplus>=201103L
//! @brief Empty vector (not very interesting)
constexpr shared_vector_base() noexcept
:m_sdata(), m_offset(0), m_count(0), m_total(0)
{}
#else
//! @brief Empty vector (not very interesting)
shared_vector_base()
:m_sdata(), m_offset(0), m_count(0), m_total(0)
{}
#endif
protected:
// helper for constructors
// Ensure that offset and size are zero when we are constructed with NULL
void _null_input()
{
if(!m_sdata) {
if(!m_sdata.get()) {
m_offset = m_total = m_count = 0;
} else {
// ensure we won't have integer overflows later
@@ -96,12 +90,19 @@ namespace detail {
}
public:
#ifdef _WIN32
template<typename A>
shared_vector_base(A* v, size_t o, size_t c)
:m_sdata(v, detail::default_array_deleter<A*>())
,m_offset(o), m_count(c), m_total(c)
{_null_input();}
#else
template<typename A>
shared_vector_base(A v, size_t o, size_t c)
:m_sdata(v, detail::default_array_deleter<A>())
,m_offset(o), m_count(c), m_total(c)
{_null_input();}
#endif
shared_vector_base(const std::tr1::shared_ptr<E>& d, size_t o, size_t c)
:m_sdata(d), m_offset(o), m_count(c), m_total(c)
{_null_input();}
@@ -117,17 +118,6 @@ namespace detail {
,m_count(O.m_count), m_total(O.m_total)
{}
#if __cplusplus >= 201103L
shared_vector_base(shared_vector_base &&O)
:m_sdata(std::move(O.m_sdata))
,m_offset(O.m_offset)
,m_count(O.m_count)
,m_total(O.m_total)
{
O.clear();
}
#endif
protected:
typedef typename meta::strip_const<E>::type _E_non_const;
public:
@@ -142,11 +132,7 @@ namespace detail {
{
if(!O.unique())
throw std::runtime_error("Can't freeze non-unique vector");
#if __cplusplus >= 201103L
m_sdata = std::move(O.m_sdata);
#else
m_sdata = O.m_sdata;
#endif
O.clear();
}
@@ -160,11 +146,7 @@ namespace detail {
,m_total(O.m_total)
{
O.make_unique();
#if __cplusplus >= 201103L
m_sdata = std::move(std::tr1::const_pointer_cast<E>(O.m_sdata));
#else
m_sdata = std::tr1::const_pointer_cast<E>(O.m_sdata);
#endif
O.clear();
}
@@ -180,21 +162,6 @@ namespace detail {
return *this;
}
#if __cplusplus >= 201103L
//! @brief Move an existing vector
shared_vector_base& operator=(shared_vector_base&& o)
{
if(&o!=this) {
m_sdata=std::move(o.m_sdata);
m_offset=o.m_offset;
m_count=o.m_count;
m_total=o.m_total;
o.clear();
}
return *this;
}
#endif
//! @brief Swap the contents of this vector with another
void swap(shared_vector_base& o) {
if(&o!=this) {
@@ -213,12 +180,11 @@ namespace detail {
}
//! @brief Data is not shared?
bool unique() const {return !m_sdata || m_sdata.use_count()<=1;}
bool unique() const {return !m_sdata || m_sdata.unique();}
//! @brief Number of elements visible through this vector
size_t size() const{return m_count;}
//! @brief shorthand for size()==0
bool empty() const{return !m_count;}
@@ -252,9 +218,7 @@ namespace detail {
m_total -= offset;
if(length > max_count)
length = max_count;
m_count = length;
m_count = std::min(length, max_count);
}
// Access to members.
@@ -308,22 +272,9 @@ public:
// allow specialization for all E to be friends
template<typename E1, class Enable1> friend class shared_vector;
//! @brief Empty vector (not very interesting)
#if __cplusplus>=201103L
constexpr shared_vector() noexcept :base_t() {}
#else
shared_vector() :base_t() {}
#endif
#if __cplusplus>=201103L
template<typename A>
shared_vector(std::initializer_list<A> L)
:base_t(new _E_non_const[L.size()], 0, L.size())
{
_E_non_const *raw = const_cast<_E_non_const*>(data());
std::copy(L.begin(), L.end(), raw);
}
#endif
//! @brief Empty vector (not very interesting)
shared_vector() :base_t() {}
//! @brief Allocate (with new[]) a new vector of size c
explicit shared_vector(size_t c)
@@ -370,11 +321,6 @@ public:
//! @brief Copy an existing vector of same type
shared_vector(const shared_vector& o) :base_t(o) {}
#if __cplusplus>=201103L
//! @brief Move an existing vector of same type
shared_vector(shared_vector&& o) :base_t(std::move(o)) {}
#endif
//! @internal
//! Internal for static_shared_vector_cast
template<typename FROM>
@@ -396,20 +342,6 @@ public:
:base_t(O,t)
{}
inline shared_vector& operator=(const shared_vector& o)
{
this->base_t::operator=(o);
return *this;
}
#if __cplusplus>=201103L
inline shared_vector& operator=(shared_vector&& o)
{
this->base_t::operator=(std::move(o));
return *this;
}
#endif
size_t max_size() const{return ((size_t)-1)/sizeof(E);}
size_t capacity() const { return this->m_total; }
@@ -421,16 +353,11 @@ public:
* does not increase.
*
* For notes on copying see docs for make_unique().
*
* @throws std::bad_alloc if requested allocation can not be made
* @throws other exceptions from element copy ctor
*/
void reserve(size_t i) {
if(this->unique() && i<=this->m_total)
return;
size_t new_count = this->m_count;
if(new_count > i)
new_count = i;
size_t new_count = std::min(this->m_count, i);
_E_non_const* temp=new _E_non_const[i];
try{
std::copy(begin(), begin()+new_count, temp);
@@ -450,16 +377,13 @@ public:
* as if make_unique() were called. This holds even if the size does not change.
*
* For notes on copying see docs for make_unique().
*
* @throws std::bad_alloc if requested allocation can not be made
* @throws other exceptions from element copy ctor
*/
void resize(size_t i) {
if(i==this->m_count) {
make_unique();
return;
}
if(this->m_sdata && this->m_sdata.use_count()==1) {
if(this->m_sdata && this->m_sdata.unique()) {
// we have data and exclusive ownership of it
if(i<=this->m_total) {
// We have room to grow (or shrink)!
@@ -468,18 +392,13 @@ public:
}
}
// must re-allocate :(
size_t new_total = this->m_total;
if(new_total < i)
new_total = i;
size_t new_total = std::max(this->m_total, i);
_E_non_const* temp=new _E_non_const[new_total];
try{
size_t n = this->size();
if(n > i)
n = i;
// Copy as much as possible from old,
// remaining elements are uninitialized.
std::copy(begin(),
begin()+n,
begin()+std::min(i,this->size()),
temp);
this->m_sdata.reset(temp, detail::default_array_deleter<pointer>());
}catch(...){
@@ -519,14 +438,10 @@ public:
}
assert(original.unique());
@endcode
*
* @throws std::bad_alloc if requested allocation can not be made
* @throws other exceptions from element copy ctor
*/
void make_unique() {
if(this->unique())
return;
// at this point we know that !!m_sdata, so get()!=NULL
_E_non_const *d = new _E_non_const[this->m_total];
try {
std::copy(this->m_sdata.get()+this->m_offset,
@@ -540,27 +455,13 @@ public:
this->m_offset=0;
}
private:
/* Hack alert.
* For reasons of simplicity and efficiency, we want to use raw pointers for iteration.
* However, shared_ptr::get() isn't defined when !m_sdata, although practically it gives NULL.
* Unfortunately, many of the MSVC (<= VS 2013) STL methods assert() that iterators are never NULL.
* So we fudge here by abusing 'this' so that our iterators are always !NULL.
*/
inline E* base_ptr() const {
#if defined(_MSC_VER) && _MSC_VER<=1800
return this->m_count ? this->m_sdata.get() : (E*)(this-1);
#else
return this->m_sdata.get();
#endif
}
public:
// STL iterators
iterator begin() const{return this->base_ptr()+this->m_offset;}
iterator begin() const{return this->m_sdata.get()+this->m_offset;}
const_iterator cbegin() const{return begin();}
iterator end() const{return this->base_ptr()+this->m_offset+this->m_count;}
iterator end() const{return this->m_sdata.get()+this->m_offset+this->m_count;}
const_iterator cend() const{return end();}
reverse_iterator rbegin() const{return reverse_iterator(end());}
@@ -612,15 +513,10 @@ public:
// data access
//! @brief Return Base pointer
pointer data() const{return this->m_sdata.get()+this->m_offset;}
//! @brief Member access
//! Undefined if empty()==true.
reference operator[](size_t i) const {return this->m_sdata.get()[this->m_offset+i];}
//! @brief Member access
//! @throws std::out_of_range if i>=size().
reference at(size_t i) const
{
if(i>this->m_count)
@@ -667,11 +563,7 @@ public:
typedef std::tr1::shared_ptr<E> shared_pointer_type;
#if __cplusplus>=201103L
constexpr shared_vector() noexcept :base_t(), m_vtype((ScalarType)-1) {}
#else
shared_vector() :base_t(), m_vtype((ScalarType)-1) {}
#endif
shared_vector(pointer v, size_t o, size_t c)
:base_t(v,o,c), m_vtype((ScalarType)-1) {}
@@ -687,11 +579,6 @@ public:
shared_vector(const shared_vector& o)
:base_t(o), m_vtype(o.m_vtype) {}
#if __cplusplus>=201103L
shared_vector(shared_vector&& o)
:base_t(std::move(o)), m_vtype(o.m_vtype) {}
#endif
//! @internal
//! Internal for static_shared_vector_cast
template<typename FROM>
@@ -722,22 +609,6 @@ public:
return *this;
}
#if __cplusplus>=201103L
shared_vector& operator=(shared_vector&& o)
{
if(&o!=this) {
this->base_t::operator=(std::move(o));
m_vtype = o.m_vtype;
}
return *this;
}
#endif
void swap(shared_vector& o) {
base_t::swap(o);
std::swap(m_vtype, o.m_vtype);
}
size_t max_size() const{return (size_t)-1;}
pointer data() const{
@@ -752,21 +623,33 @@ namespace detail {
template<typename TO, typename FROM, class Enable = void>
struct static_shared_vector_caster { /* no default */ };
// from void to non-void with same const-ness
template<typename TO, typename FROM>
struct static_shared_vector_caster<TO, FROM,
typename meta::_and<meta::_and<meta::is_not_void<TO>, meta::is_void<FROM> >,
meta::same_const<TO,FROM> >::type> {
static inline shared_vector<TO> op(const shared_vector<FROM>& src) {
template<typename TO>
struct static_shared_vector_caster<TO, void,
typename meta::_and<meta::same_const<TO,void>, meta::is_not_void<TO> >::type> {
static inline shared_vector<TO> op(const shared_vector<void>& src) {
return shared_vector<TO>(src, detail::_shared_vector_cast_tag());
}
};
template<typename TO>
struct static_shared_vector_caster<TO, const void,
typename meta::_and<meta::same_const<TO,const void>, meta::is_not_void<TO> >::type> {
static inline shared_vector<TO> op(const shared_vector<const void>& src) {
return shared_vector<TO>(src, detail::_shared_vector_cast_tag());
}
};
// from non-void to void with same const-ness
template<typename TO, typename FROM>
struct static_shared_vector_caster<TO, FROM,
typename meta::_and<meta::_and<meta::is_void<TO>, meta::is_not_void<FROM> >,
meta::same_const<TO,FROM> >::type> {
static FORCE_INLINE shared_vector<TO> op(const shared_vector<FROM>& src) {
return shared_vector<TO>(src, detail::_shared_vector_cast_tag());
template<typename FROM>
struct static_shared_vector_caster<void, FROM,
typename meta::_and<meta::same_const<void,FROM>, meta::is_not_void<FROM> >::type> {
static FORCE_INLINE shared_vector<void> op(const shared_vector<FROM>& src) {
return shared_vector<void>(src, detail::_shared_vector_cast_tag());
}
};
template<typename FROM>
struct static_shared_vector_caster<const void, FROM,
typename meta::_and<meta::same_const<const void,FROM>, meta::is_not_void<FROM> >::type> {
static FORCE_INLINE shared_vector<const void> op(const shared_vector<FROM>& src) {
return shared_vector<const void>(src, detail::_shared_vector_cast_tag());
}
};
@@ -837,13 +720,9 @@ namespace detail {
typedef typename meta::strip_const<TO>::type to_t;
ScalarType stype = src.original_type(),
dtype = (ScalarType)ScalarTypeID<TO>::value;
if(src.empty()) {
return shared_vector<TO>();
} else if(stype==dtype) {
if(stype==dtype) {
// no convert needed
return shared_vector<TO>(src, detail::_shared_vector_cast_tag());
} else {
// alloc and convert
shared_vector<to_t> ret(src.size()/ScalarTypeFunc::elementSize(stype));
@@ -950,7 +829,7 @@ const_shared_vector_cast(shared_vector<FROM>& src)
namespace ScalarTypeFunc {
//! Allocate an untyped array based on ScalarType
epicsShareFunc shared_vector<void> allocArray(ScalarType id, size_t len);
shared_vector<void> allocArray(ScalarType id, size_t len);
//! Allocate an untyped array based on ScalarType
template<ScalarType ID>

View File

@@ -28,10 +28,10 @@ namespace epics { namespace pvData {
class epicsShareClass Status : public epics::pvData::Serializable {
public:
POINTER_DEFINITIONS(Status);
/**
* Status type enum.
*/
enum StatusType {
/**
* Status type enum.
*/
enum StatusType {
/** Operation completed successfully. */
STATUSTYPE_OK,
/** Operation completed successfully, but there is a warning message. */
@@ -40,24 +40,20 @@ namespace epics { namespace pvData {
STATUSTYPE_ERROR,
/** Operation failed due to an unexpected error. */
STATUSTYPE_FATAL
};
static const char* StatusTypeName[];
static Status Ok;
static inline Status warn(const std::string& m) { return Status(STATUSTYPE_WARNING, m); }
static inline Status error(const std::string& m) { return Status(STATUSTYPE_ERROR, m); }
static inline Status fatal(const std::string& m) { return Status(STATUSTYPE_FATAL, m); }
};
static const char* StatusTypeName[];
static Status Ok;
/**
* Creates OK status; STATUSTYPE_OK, empty message and stackDump.
*/
Status() :m_statusType(STATUSTYPE_OK) {}
/**
* Create non-OK status.
*/
Status();
/**
* Create non-OK status.
*/
Status(StatusType type, std::string const & message);
/**
@@ -65,25 +61,25 @@ namespace epics { namespace pvData {
*/
Status(StatusType type, std::string const & message, std::string const & stackDump);
virtual ~Status() {}
~Status();
/**
* Get status type.
* @return status type, non-<code>null</code>.
*/
inline StatusType getType() const { return m_statusType; }
StatusType getType() const;
/**
* Get error message describing an error. Required if error status.
* @return error message.
*/
inline const std::string& getMessage() const { return m_message; }
std::string getMessage() const;
/**
* Get stack dump where error (exception) happened. Optional.
* @return stack dump.
*/
inline const std::string& getStackDump() const { return m_stackDump; }
std::string getStackDump() const;
/**
* Convenient OK test. Same as <code>(getType() == StatusType.OK)</code>.
@@ -92,47 +88,13 @@ namespace epics { namespace pvData {
* @return OK status.
* @see #isSuccess()
*/
inline bool isOK() const {
return (m_statusType == STATUSTYPE_OK);
}
bool isOK() const;
/**
* Check if operation succeeded (OK or WARNING).
* Check if operation succeeded.
* @return operation success status.
*/
inline bool isSuccess() const {
return (m_statusType == STATUSTYPE_OK || m_statusType == STATUSTYPE_WARNING);
}
#if __cplusplus>=201103L
FORCE_INLINE explicit operator bool() const {
return isSuccess();
}
#else
private:
typedef bool (Status::*truth_type)() const;
public:
FORCE_INLINE operator truth_type() const {
return isSuccess() ? &Status::isSuccess : 0;
}
#endif
/** override this Status if the other has higher StatusType
@code
Status ret;
ret |= call1();
if(ret)
ret |= call2();
return ret;
@endcode
*/
void maximize(const Status& o);
//! short hand for "this->maximize(o)"
FORCE_INLINE Status& operator|=(const Status& o) {
maximize(o);
return *this;
}
bool isSuccess() const;
void serialize(ByteBuffer *buffer, SerializableControl *flusher) const;
void deserialize(ByteBuffer *buffer, DeserializableControl *flusher);
@@ -147,15 +109,8 @@ namespace epics { namespace pvData {
};
FORCE_INLINE std::ostream& operator<<(std::ostream& o, const Status& status) {
status.dump(o);
return o;
}
FORCE_INLINE std::ostream& operator<<(std::ostream& o, const Status::StatusType& statusType) {
o << Status::StatusTypeName[statusType];
return o;
}
epicsShareExtern std::ostream& operator<<(std::ostream& o, const Status& status);
epicsShareExtern std::ostream& operator<<(std::ostream& o, const Status::StatusType& statusType);
}}
#endif /* STATUS_H */

View File

@@ -10,9 +10,9 @@
// gently nudge the compiler to inline our wrappers
// Warning: Only use this when the template body is *small*.
// You have been warned!
#if defined(__MINGW32__)
#if defined(__MINGW32__)
# define FORCE_INLINE inline
#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 402)
#elif defined(__GNUC__) && __GNUC__>=3
# define FORCE_INLINE __attribute__((always_inline)) inline
#elif defined(_MSC_VER)
# define FORCE_INLINE __forceinline
@@ -116,31 +116,6 @@ struct _and {};
template<typename A, typename B, class R>
struct _and<A,B, typename A::type, typename B::type, R> { typedef R type; };
/** Mangle type to best pass as an argument.
*
* POD types passed by value.
* All others by const reference.
*/
template<typename T>
struct arg_type {typedef const T& type;};
#define SIMPLE_ARG_TYPE(TYPE) template<> struct arg_type<TYPE> { typedef TYPE type; };
SIMPLE_ARG_TYPE(bool)
SIMPLE_ARG_TYPE(char)
SIMPLE_ARG_TYPE(signed char)
SIMPLE_ARG_TYPE(unsigned char)
SIMPLE_ARG_TYPE(short)
SIMPLE_ARG_TYPE(unsigned short)
SIMPLE_ARG_TYPE(int)
SIMPLE_ARG_TYPE(unsigned int)
SIMPLE_ARG_TYPE(long)
SIMPLE_ARG_TYPE(unsigned long)
SIMPLE_ARG_TYPE(long long)
SIMPLE_ARG_TYPE(unsigned long long)
SIMPLE_ARG_TYPE(float)
SIMPLE_ARG_TYPE(double)
SIMPLE_ARG_TYPE(long double)
#undef SIMPLE_ARG_TYPE
}}}
#endif // TEMPLATEMETA_H

View File

@@ -11,7 +11,6 @@
#include <memory>
#include <sstream>
#include <stdexcept>
#if __cplusplus>=201103L
#include <functional>
@@ -42,7 +41,37 @@ typedef std::tr1::shared_ptr<epicsThread> EpicsThreadPtr;
typedef epicsThreadRunable Runnable;
//! Helper for those cases where a class should have more than one runnable
template<typename C>
class epicsShareClass RunnableMethod : public Runnable, private NoDefaultMethods
{
typedef void (C::*meth_t)();
C *inst;
meth_t meth;
virtual void run()
{
(inst->*meth)();
}
public:
RunnableMethod(C* inst, void (C::*meth)())
:inst(inst), meth(meth)
{}
};
namespace detail {
struct FuncRunner : public epicsThreadRunable
{
typedef void (*fn_t)(void*);
fn_t fn;
void *arg;
FuncRunner(fn_t f, void *a) :fn(f), arg(a) {}
virtual ~FuncRunner(){}
virtual void run()
{
(*fn)(arg);
}
};
template<typename C>
struct MethRunner : public epicsThreadRunable
{
@@ -56,14 +85,26 @@ struct MethRunner : public epicsThreadRunable
(inst->*fn)();
}
};
#if __cplusplus>=201103L
struct BindRunner : public epicsThreadRunable
{
typedef std::function<void()> fn_t;
fn_t fn;
BindRunner(const fn_t f) : fn(f) {}
virtual ~BindRunner() {}
virtual void run()
{
fn();
}
};
#endif
} // namespace detail
/**
* @brief C++ wrapper for epicsThread from EPICS base.
*
*/
class epicsShareClass Thread : public epicsThread {
EPICS_NOT_COPYABLE(Thread)
class epicsShareClass Thread : public epicsThread, private NoDefaultMethods {
public:
/** @brief Holds all the configuration necessary to launch a @class Thread
*
@@ -98,31 +139,56 @@ public:
std::ostringstream p_strm;
bool p_autostart;
Runnable *p_runner;
typedef epics::auto_ptr<Runnable> p_owned_runner_t;
#if __cplusplus>=201103L
typedef std::unique_ptr<Runnable> p_owned_runner_t;
#else
typedef std::auto_ptr<Runnable> p_owned_runner_t;
#endif
p_owned_runner_t p_owned_runner;
friend class Thread;
Runnable& x_getrunner();
void x_setdefault();
Runnable& x_getrunner()
{
if(!this->p_runner)
throw std::logic_error("Thread::Config missing run()");
return *this->p_runner;
}
void x_setdefault()
{
this->p_prio = epicsThreadPriorityLow;
this->p_autostart = true;
this->p_runner = NULL;
(*this).stack(epicsThreadStackSmall);
}
public:
Config();
Config(Runnable *r);
Config(void(*fn)(void*), void *ptr);
Config() {this->x_setdefault();}
Config(Runnable *r) {this->x_setdefault();this->run(r);}
Config(void(*fn)(void*), void *ptr) {this->x_setdefault();this->run(fn, ptr);}
template<typename C>
Config(C* inst, void(C::*meth)()) {this->x_setdefault();this->run(inst, meth);}
#if __cplusplus>=201103L
Config(std::function<void()>&& fn);
Config(const std::function<void()>& fn) {this->x_setdefault();this->run(fn);}
#endif
Config& name(const std::string& n);
Config& prio(unsigned int p);
Config& stack(epicsThreadStackSizeClass s);
Config& autostart(bool a);
inline Config& name(const std::string& n)
{ this->p_strm.str(n); return *this; }
inline Config& prio(unsigned int p)
{ this->p_prio = p; return *this; }
inline Config& stack(epicsThreadStackSizeClass s)
{ this->p_stack = epicsThreadGetStackSize(s); return *this; }
inline Config& autostart(bool a)
{ this->p_autostart = a; return *this; }
//! Thread will execute Runnable::run()
Config& run(Runnable* r);
Config& run(Runnable* r)
{ this->p_runner = r; return *this; }
//! Thread will execute (*fn)(ptr)
Config& run(void(*fn)(void*), void *ptr);
Config& run(void(*fn)(void*), void *ptr)
{
this->p_owned_runner.reset(new detail::FuncRunner(fn, ptr));
this->p_runner = this->p_owned_runner.get();
return *this;
}
//! Thread will execute (inst->*meth)()
template<typename C>
Config& run(C* inst, void(C::*meth)())
@@ -132,7 +198,12 @@ public:
return *this;
}
#if __cplusplus>=201103L
Config& run(std::function<void()>&& fn);
Config& run(const std::function<void()>& fn)
{
this->p_owned_runner.reset(new detail::BindRunner(fn));
this->p_runner = this->p_owned_runner.get();
return *this;
}
#endif
//! Append to thread name string. Argument must be understood by std::ostream::operator<<
@@ -158,7 +229,14 @@ public:
Thread(std::string name,
ThreadPriority priority,
Runnable *runnable,
epicsThreadStackSizeClass stkcls=epicsThreadStackBig);
epicsThreadStackSizeClass stkcls=epicsThreadStackSmall)
:epicsThread(*runnable,
name.c_str(),
epicsThreadGetStackSize(stkcls),
priority)
{
this->start();
}
/**
*
@@ -178,19 +256,38 @@ public:
Thread(Runnable &runnable,
std::string name,
unsigned int stksize,
unsigned int priority=lowestPriority);
unsigned int priority=lowestPriority)
:epicsThread(runnable,
name.c_str(),
stksize,
priority)
{
this->start();
}
//! @brief Create a new thread using the given @class Config
//! @throws std::logic_error for improper @class Config (ie. missing runner)
Thread(Config& c);
Thread(Config& c)
:epicsThread(c.x_getrunner(), c.p_strm.str().c_str(),
c.p_stack, c.p_prio)
{
#if __cplusplus>=201103L
p_owned = std::move(c.p_owned_runner);
#else
p_owned = c.p_owned_runner;
#endif
if(c.p_autostart)
this->start();
}
/**
* Destructor
*/
~Thread();
~Thread()
{
this->exitWait();
}
static size_t num_instances;
private:
Config::p_owned_runner_t p_owned;
};

View File

@@ -0,0 +1,70 @@
/* timeFunction.h */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mrk
*/
#ifndef TIMEFUNCTION_H
#define TIMEFUNCTION_H
#include <pv/sharedPtr.h>
#include <shareLib.h>
namespace epics { namespace pvData {
class TimeFunctionRequester;
class TimeFunction;
typedef std::tr1::shared_ptr<TimeFunctionRequester> TimeFunctionRequesterPtr;
typedef std::tr1::shared_ptr<TimeFunction> TimeFunctionPtr;
/**
* @brief Class that must be implemented by timeFunction requester.
*
*/
class epicsShareClass TimeFunctionRequester {
public:
POINTER_DEFINITIONS(TimeFunctionRequester);
/**
* Destructor
*/
virtual ~TimeFunctionRequester(){}
/**
* function to be timed.
* It will get called multiple times.
*/
virtual void function() = 0;
};
/**
* @brief Class for measuring time it takes to execute a function.
*
*/
class epicsShareClass TimeFunction {
public:
POINTER_DEFINITIONS(TimeFunction);
/**
* Constructor
* @param requester The class that has a function method.
*/
TimeFunction(TimeFunctionRequesterPtr const & requester);
/**
* Destructor
*/
~TimeFunction();
/**
* Time the function.
* @return the time in seconds to execute the function.
* Note that the function may be called many times.
*/
double timeCall();
private:
TimeFunctionRequesterPtr requester;
};
}}
#endif /* TIMEFUNCTION_H */

View File

@@ -9,8 +9,6 @@
#ifndef TIMER_H
#define TIMER_H
#include <memory>
#include <list>
#include <stddef.h>
#include <stdlib.h>
#include <stddef.h>
@@ -57,28 +55,34 @@ public:
*/
virtual void timerStopped() = 0;
private:
epicsTime timeToRun;
TimerCallbackPtr next;
TimeStamp timeToRun;
double period;
bool onList;
friend class Timer;
struct IncreasingTime;
};
/**
* @brief Support for delayed or periodic callback execution.
*
*/
class epicsShareClass Timer : private Runnable {
class epicsShareClass Timer : public Runnable {
public:
POINTER_DEFINITIONS(Timer);
/** Create a new timer queue
/**
* Constructor
* @param threadName name for the timer thread.
* @param priority thread priority
*/
Timer(std::string threadName, ThreadPriority priority);
/**
* Destructor
*/
virtual ~Timer();
//! Prevent new callbacks from being scheduled, and cancel pending callbacks
void close();
/**
* The thread run method. This is called automatically.
*/
virtual void run();
/**
* schedule a callback after a delay.
* @param timerCallback the timerCallback instance.
@@ -100,38 +104,31 @@ public:
/**
* cancel a callback.
* @param timerCallback the timerCallback to cancel.
* @returns true if the timer was queued, and now is cancelled
*/
bool cancel(TimerCallbackPtr const &timerCallback);
void cancel(TimerCallbackPtr const &timerCallback);
/**
* Is the callback scheduled to be called?
* @param timerCallback the timerCallback.
* @return (false,true) if (not, is) scheduled.
*/
bool isScheduled(TimerCallbackPtr const &timerCallback) const;
bool isScheduled(TimerCallbackPtr const &timerCallback);
/**
* show the elements in the timer queue.
* @param o The output stream for the output
*/
void dump(std::ostream& o) const;
void dump(std::ostream& o);
private:
virtual void run();
// call with mutex held
void addElement(TimerCallbackPtr const &timerCallback);
typedef std::list<TimerCallbackPtr> queue_t;
mutable Mutex mutex;
queue_t queue;
TimerCallbackPtr head;
Mutex mutex;
Event waitForWork;
bool waiting;
Event waitForDone;
bool alive;
Thread thread;
};
epicsShareExtern std::ostream& operator<<(std::ostream& o, const Timer& timer);
epicsShareExtern std::ostream& operator<<(std::ostream& o, Timer& timer);
}}
#endif /* TIMER_H */

View File

@@ -22,29 +22,17 @@ namespace epics { namespace pvData {
namespace detail {
// parseToPOD wraps the epicsParse*() functions in one name
// and throws exceptions
epicsShareExtern void parseToPOD(const char*, boolean *out);
epicsShareExtern void parseToPOD(const char*, int8 *out);
epicsShareExtern void parseToPOD(const char*, uint8 *out);
epicsShareExtern void parseToPOD(const char*, int16_t *out);
epicsShareExtern void parseToPOD(const char*, uint16_t *out);
epicsShareExtern void parseToPOD(const char*, int32_t *out);
epicsShareExtern void parseToPOD(const char*, uint32_t *out);
epicsShareExtern void parseToPOD(const char*, int64_t *out);
epicsShareExtern void parseToPOD(const char*, uint64_t *out);
epicsShareExtern void parseToPOD(const char*, float *out);
epicsShareExtern void parseToPOD(const char*, double *out);
static inline void parseToPOD(const std::string& str, boolean *out) { return parseToPOD(str.c_str(), out); }
static inline void parseToPOD(const std::string& str, int8 *out) { return parseToPOD(str.c_str(), out); }
static inline void parseToPOD(const std::string& str, uint8 *out) { return parseToPOD(str.c_str(), out); }
static inline void parseToPOD(const std::string& str, int16_t *out) { return parseToPOD(str.c_str(), out); }
static inline void parseToPOD(const std::string& str, uint16_t *out) { return parseToPOD(str.c_str(), out); }
static inline void parseToPOD(const std::string& str, int32_t *out) { return parseToPOD(str.c_str(), out); }
static inline void parseToPOD(const std::string& str, uint32_t *out) { return parseToPOD(str.c_str(), out); }
static inline void parseToPOD(const std::string& str, int64_t *out) { return parseToPOD(str.c_str(), out); }
static inline void parseToPOD(const std::string& str, uint64_t *out) { return parseToPOD(str.c_str(), out); }
static inline void parseToPOD(const std::string& str, float *out) { return parseToPOD(str.c_str(), out); }
static inline void parseToPOD(const std::string& str, double *out) { return parseToPOD(str.c_str(), out); }
epicsShareExtern void parseToPOD(const std::string&, boolean *out);
epicsShareExtern void parseToPOD(const std::string&, int8 *out);
epicsShareExtern void parseToPOD(const std::string&, uint8 *out);
epicsShareExtern void parseToPOD(const std::string&, int16_t *out);
epicsShareExtern void parseToPOD(const std::string&, uint16_t *out);
epicsShareExtern void parseToPOD(const std::string&, int32_t *out);
epicsShareExtern void parseToPOD(const std::string&, uint32_t *out);
epicsShareExtern void parseToPOD(const std::string&, int64_t *out);
epicsShareExtern void parseToPOD(const std::string&, uint64_t *out);
epicsShareExtern void parseToPOD(const std::string&, float *out);
epicsShareExtern void parseToPOD(const std::string&, double *out);
/* want to pass POD types by value,
* and std::string by const reference
@@ -120,28 +108,13 @@ namespace detail {
}
};
// parse POD from C string
// TO!=const char*
template<typename TO>
struct cast_helper<TO, const char*,
typename meta::_and<
typename meta::not_same_type<TO,const char*>,
typename meta::not_same_type<TO,std::string>
>::type> {
static FORCE_INLINE TO op(const char* from) {
TO ret;
parseToPOD(from, &ret);
return ret;
}
};
} // end detail
/** @brief Casting/converting between supported scalar types.
*
* Supported types: uint8_t, int8_t, uint16_t, int16_t,
* uint32_t, int32_t, uint64_t, int64_t,
* float, double, std::string, const char* (only FROM)
* float, double, std::string
*
* As defined in pvType.h
*
@@ -208,43 +181,6 @@ static FORCE_INLINE
typename detail::print_convolute<T>::return_t
print_cast(const T& v) { return detail::print_convolute<T>::op(v); }
struct escape;
epicsShareFunc
std::ostream& operator<<(std::ostream& strm, const escape& Q);
//! Helper to print a string with escaping
//! @code strm<<'"'<<escape(astring)<<'"' @endcode
struct epicsShareClass escape
{
enum style_t {
C, // default
CSV, // a la RFC4180
};
private:
const std::string& orig;
style_t S;
public:
escape(const std::string& orig) :orig(orig), S(C) {}
~escape();
//! Change escaping style
inline escape& style(style_t s) { S = s; return *this; }
//! print to string and return. (alloc and copy)
std::string str() const;
friend
epicsShareFunc
std::ostream& operator<<(std::ostream& strm, const escape& Q);
};
struct maybeQuote {
const std::string& s;
maybeQuote(const std::string& s) :s(s) {}
};
epicsShareExtern
std::ostream& operator<<(std::ostream& strm, const maybeQuote& q);
}} // end namespace
#endif // PVTYPECAST_H

View File

@@ -1,53 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <string>
#include <vector>
#include <epicsUnitTest.h>
#define epicsExportSharedSymbols
#include <pv/pvUnitTest.h>
namespace detail {
testPassx::~testPassx() {
if(!alive) return;
std::string msg(strm.str());
size_t nl = msg.find_first_of('\n');
if(nl==msg.npos) {
// single-line output
if(dotest)
testOk(pass, "%s", msg.c_str());
else
testDiag("%s", msg.c_str());
} else {
// multi-line output
std::istringstream lines(msg);
std::string line;
bool first = true;
while(std::getline(lines ,line)) {
if(dotest && first) {
first = false;
testOk(pass, "%s", line.c_str());
} else {
testDiag("%s", line.c_str());
}
}
}
}
testPassx::testPassx(testPassx& o)
:strm(o.strm.str())
,dotest(o.dotest)
,pass(o.pass)
,alive(o.alive)
{
strm.seekp(0, std::ios_base::end);
o.alive = false;
}
} // namespace detail

View File

@@ -1,282 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <memory>
#include <stdlib.h>
#include <string.h>
#include <epicsString.h>
#include <epicsGuard.h>
#include <epicsMutex.h>
#include <epicsEvent.h>
#include <epicsThread.h>
#include <epicsTime.h>
#define epicsExportSharedSymbols
#include <pv/pvdVersion.h>
#include <pv/sharedPtr.h>
#include "pv/reftrack.h"
namespace {
typedef epicsGuard<epicsMutex> Guard;
typedef epicsGuardRelease<epicsMutex> UnGuard;
struct refgbl_t {
epicsMutex lock;
typedef std::map<std::string, const size_t*> counters_t;
counters_t counters;
} *refgbl;
void refgbl_init(void *)
{
try {
refgbl = new refgbl_t;
} catch(std::exception& e) {
std::cerr<<"Failed to initialize global ref. counter registry :"<<e.what()<<"\n";
}
}
epicsThreadOnceId refgbl_once = EPICS_THREAD_ONCE_INIT;
void refgbl_setup()
{
epicsThreadOnce(&refgbl_once, &refgbl_init, 0);
if(!refgbl)
throw std::runtime_error("Failed to initialize global ref. counter registry");
}
} // namespace
namespace epics {
void registerRefCounter(const char *name, const size_t* counter)
{
refgbl_setup();
Guard G(refgbl->lock);
refgbl->counters[name] = counter;
}
void unregisterRefCounter(const char *name, const size_t* counter)
{
refgbl_setup();
Guard G(refgbl->lock);
refgbl_t::counters_t::iterator it(refgbl->counters.find(name));
if(it!=refgbl->counters.end() && it->second==counter)
refgbl->counters.erase(it);
}
size_t readRefCounter(const char *name)
{
refgbl_setup();
Guard G(refgbl->lock);
refgbl_t::counters_t::iterator it(refgbl->counters.find(name));
if(it==refgbl->counters.end())
return 0;
return atomic::get(*it->second);
}
const RefSnapshot::Count&
RefSnapshot::operator[](const std::string& name) const
{
static const Count zero;
cnt_map_t::const_iterator it(counts.find(name));
return it==counts.end() ? zero : it->second;
}
void RefSnapshot::update()
{
refgbl_t::counters_t counters;
{
refgbl_setup();
Guard G(refgbl->lock);
counters = refgbl->counters; // copy
}
counts.clear();
for(refgbl_t::counters_t::const_iterator it=counters.begin(),
end=counters.end();
it!=end; ++it)
{
size_t cnt = atomic::get(*it->second);
counts[it->first] = Count(cnt, 0);
}
}
RefSnapshot RefSnapshot::operator-(const RefSnapshot& rhs) const
{
RefSnapshot ret;
RefSnapshot::cnt_map_t::const_iterator lit = counts.begin(),
lend= counts.end(),
rit = rhs.counts.begin(),
rend= rhs.counts.end();
while(lit!=lend || rit!=rend)
{
if(lit==lend || (rit!=rend && lit->first > rit->first)) {
ret.counts[rit->first] = RefSnapshot::Count(0, -long(rit->second.current));
++rit;
} else if(rit==rend || lit->first < rit->first) {
ret.counts[lit->first] = RefSnapshot::Count(lit->second.current, long(lit->second.current));
++lit;
} else { // !end and lit->first == rit->first
ret.counts[lit->first] = RefSnapshot::Count(lit->second.current,
long(lit->second.current) - long(rit->second.current));
++lit;
++rit;
}
}
return ret;
}
std::ostream& operator<<(std::ostream& strm, const RefSnapshot& snap)
{
for(RefSnapshot::const_iterator it = snap.begin(), end = snap.end(); it!=end; ++it)
{
if(it->second.delta==0) continue;
strm<<it->first<<":\t"<<it->second.current<<" (delta "<<it->second.delta<<")\n";
}
return strm;
}
struct RefMonitor::Impl : public epicsThreadRunable
{
RefMonitor& owner;
epics::auto_ptr<epicsThread> worker;
epicsMutex lock;
epicsEvent wakeup;
RefSnapshot prev;
bool done;
double period;
Impl(RefMonitor* owner) :owner(*owner), done(false), period(10.0) {}
virtual ~Impl() {}
virtual void run()
{
Guard G(lock);
while(!done) {
RefSnapshot current, P;
P = prev; // copy
{
UnGuard U(G);
current.update();
owner.show(current-P);
}
prev.swap(current);
{
UnGuard U(G);
wakeup.wait(period);
}
}
}
};
RefMonitor::RefMonitor()
:impl(new Impl(this))
{}
RefMonitor::~RefMonitor()
{
stop();
delete impl;
}
void RefMonitor::start(double period)
{
Guard G(impl->lock);
if(impl->worker.get()) return;
impl->done = false;
impl->period = period;
impl->worker.reset(new epicsThread(*impl,
"RefMonitor",
epicsThreadGetStackSize(epicsThreadStackBig),
epicsThreadPriorityMin));
impl->worker->start();
}
void RefMonitor::stop()
{
epics::auto_ptr<epicsThread> W;
{
Guard G(impl->lock);
if(!impl->worker.get()) return;
epics::swap(W, impl->worker);
impl->done = true;
}
impl->wakeup.signal();
W->exitWait();
W.reset();
}
bool RefMonitor::running() const
{
Guard G(impl->lock);
return !!impl->worker.get();
}
void RefMonitor::current()
{
RefSnapshot current, P;
current.update();
{
Guard G(impl->lock);
P = impl->prev; // copy
}
show(current-P, true);
}
void RefMonitor::show(const RefSnapshot &snap, bool complete)
{
char buf[80];
epicsTime::getCurrent().strftime(buf, sizeof(buf), "%a %b %d %Y %H:%M:%S.%f");
buf[sizeof(buf)-1] = '\0';
std::cerr<<buf<<" : References\n";
for(RefSnapshot::const_iterator it = snap.begin(), end = snap.end(); it!=end; ++it)
{
// print if delta!=0 or (complete && current!=0)
if(it->second.delta==0 && (!complete || it->second.current==0)) continue;
std::cerr<<it->first<<":\t"<<it->second.current<<" (delta "<<it->second.delta<<")\n";
}
}
} // namespace epics
char* epicsRefSnapshotCurrent()
{
try {
epics::RefSnapshot snap;
snap.update();
std::ostringstream strm;
strm<<snap;
const char *str = strm.str().c_str();
char *ret = (char*)malloc(strlen(str)+1);
if(ret)
strcpy(ret, str);
return ret;
}catch(std::exception& e){
return epicsStrDup(e.what());
}
}

44
src/misc/requester.cpp Normal file
View File

@@ -0,0 +1,44 @@
/* requester.cpp */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mrk
*/
#include <string>
#include <cstdio>
#include <iostream>
#include <epicsMutex.h>
#define epicsExportSharedSymbols
#include <pv/lock.h>
#include <pv/requester.h>
using std::string;
namespace epics { namespace pvData {
static StringArray messageTypeName(MESSAGE_TYPE_COUNT);
string getMessageTypeName(MessageType messageType)
{
// TODO not thread-safe
static Mutex mutex;
Lock xx(mutex);
if(messageTypeName[0].size()==0) {
messageTypeName[0] = "info";
messageTypeName[1] = "warning";
messageTypeName[2] = "error";
messageTypeName[3] = "fatalError";
}
return messageTypeName[messageType];
}
void Requester::message(std::string const & message,MessageType messageType)
{
std::cerr << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")\n";
}
}}

View File

@@ -32,7 +32,7 @@ namespace epics {
}
void SerializeHelper::writeSize(std::size_t s, ByteBuffer* buffer) {
if(s==(std::size_t)-1) // null not a size, but used in PVUnion::serialize()
if(s==(std::size_t)-1) // null // TODO remove
buffer->putByte(-1);
else if(s<254)
buffer->putByte(static_cast<int8>(s));
@@ -98,18 +98,20 @@ namespace epics {
break;
}
}
static string emptyStringtring;
string SerializeHelper::deserializeString(ByteBuffer* buffer,
DeserializableControl* control) {
std::size_t size = SerializeHelper::readSize(buffer, control);
if(size!=(size_t)-1) // TODO null strings check, to be removed in the future
if(size!=(size_t)-1) // TODO null strings check, to be removed in the future
{
if (buffer->getRemaining()>=size)
{
// entire string is in buffer, simply create a string out of it (copy)
std::size_t pos = buffer->getPosition();
string str(buffer->getBuffer()+pos, size);
string str(buffer->getArray()+pos, size);
buffer->setPosition(pos+size);
return str;
}
@@ -122,7 +124,7 @@ namespace epics {
while(true) {
std::size_t toRead = min(size-i, buffer->getRemaining());
std::size_t pos = buffer->getPosition();
str.append(buffer->getBuffer()+pos, toRead);
str.append(buffer->getArray()+pos, toRead);
buffer->setPosition(pos+toRead);
i += toRead;
if(i<size)
@@ -137,7 +139,7 @@ namespace epics {
}
}
else
return std::string();
return emptyStringtring;
}
}
}
@@ -174,6 +176,14 @@ struct ToString : public epics::pvData::SerializableControl
assert(bufwrap.getRemaining()>0);
}
virtual void alignBuffer(std::size_t alignment)
{
if(bufwrap.getRemaining()<alignment)
flushSerializeBuffer();
assert(bufwrap.getRemaining()>=alignment);
bufwrap.align(alignment);
}
virtual bool directSerialize(
ByteBuffer *existingBuffer,
const char* toSerialize,
@@ -224,6 +234,16 @@ struct FromString : public epics::pvData::DeserializableControl
throw std::logic_error("Incomplete buffer");
}
virtual void alignData(std::size_t alignment)
{
size_t pos = buf.getPosition(), k = alignment-1;
if(pos&k) {
std::size_t npad = alignment-(pos&k);
ensureData(npad);
buf.align(alignment);
}
}
virtual bool directDeserialize(
ByteBuffer *existingBuffer,
char* deserializeTo,

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