Compare commits
314 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2b42d5f8c | ||
|
|
90b1f2f6da | ||
|
|
2e775ef2fb | ||
|
|
f17d2bbca1 | ||
|
|
79b02254c4 | ||
|
|
19245ce805 | ||
|
|
0fa927afa7 | ||
|
|
d9b3f98e35 | ||
|
|
81e7968230 | ||
|
|
06f3b96992 | ||
| 1038182a16 | |||
| 60091bfe56 | |||
| f4de6dd9b1 | |||
|
|
3d93a80cce | ||
|
|
94eff54fa9 | ||
|
|
e9dde4d2f8 | ||
|
|
0a5e22e5b0 | ||
|
|
3a5cfba4e1 | ||
|
|
07b75e4543 | ||
|
|
e4f150b34f | ||
|
|
ca2ae0d0e7 | ||
|
|
828506720c | ||
|
|
ad712b63f9 | ||
|
|
4c3d5a788d | ||
|
|
df89135455 | ||
|
|
95d452870c | ||
|
|
8a82ff9fe4 | ||
|
|
dd24b2ad75 | ||
|
|
17fa7a7724 | ||
|
|
d01ba94ed7 | ||
|
|
c7c7585950 | ||
|
|
f869936cfd | ||
|
|
0ad253c6f0 | ||
|
|
52d04d5044 | ||
|
|
bff2dc9cd2 | ||
|
|
b903df5d0d | ||
|
|
416d910577 | ||
|
|
37b7a0708f | ||
|
|
6ceaa6adb0 | ||
|
|
0447826e7c | ||
|
|
3ef60a61a2 | ||
|
|
31802a8bde | ||
|
|
c2bc77a649 | ||
|
|
4dd7a18301 | ||
|
|
a29894ee2b | ||
|
|
2f8ac7f673 | ||
|
|
b050fbbcbe | ||
|
|
caa11605fc | ||
|
|
cfcdd1a3f9 | ||
|
|
95ff606ba1 | ||
|
|
4cc9b650c5 | ||
|
|
cd2436342d | ||
|
|
3ae2d09fe3 | ||
|
|
35b3403de6 | ||
|
|
f780ebdf76 | ||
|
|
93f0518b4b | ||
|
|
6da871fa64 | ||
|
|
d53cb0cbc9 | ||
|
|
8f0111e482 | ||
|
|
5525119778 | ||
|
|
6410600205 | ||
|
|
a5d44745d1 | ||
|
|
8c275cbc1c | ||
|
|
b79f69231c | ||
|
|
ff165595c4 | ||
|
|
12d851dc6f | ||
|
|
643f289c23 | ||
|
|
68e74ed1d2 | ||
|
|
f0ef0965c4 | ||
|
|
61ce532fdf | ||
|
|
d746e1bfb3 | ||
|
|
deccc41b9a | ||
|
|
4ffddfa2f6 | ||
|
|
2814c779bd | ||
|
|
4c73607799 | ||
|
|
d776f6eaf0 | ||
|
|
706ef01782 | ||
|
|
00c62cbd67 | ||
|
|
da8f3d6cc7 | ||
|
|
0bc95e51c2 | ||
|
|
727153e965 | ||
|
|
d00f54228d | ||
|
|
77c67802a3 | ||
|
|
7d68d177d7 | ||
|
|
9b20505dcd | ||
|
|
5f93e292b2 | ||
|
|
edd3e20f3c | ||
|
|
90cffa60d6 | ||
|
|
6171cd6867 | ||
|
|
fa731bf6c3 | ||
|
|
818fce324c | ||
|
|
1bc867e48d | ||
|
|
434b9f7a9f | ||
|
|
f54602dead | ||
|
|
fb546b41c1 | ||
|
|
e400d9f5fd | ||
|
|
f0fa8a2481 | ||
|
|
c3b0b49e3f | ||
|
|
45265b4f9b | ||
|
|
aa87a2a23d | ||
|
|
5a59b1da75 | ||
|
|
32aa0dd72f | ||
|
|
c1188b16a1 | ||
|
|
a02a60c658 | ||
| c5f9f5a2dc | |||
|
|
342b1bc8ef | ||
|
|
64158376f5 | ||
|
|
850d4ff056 | ||
|
|
f0cfe1c85a | ||
|
|
c8b615b3ee | ||
|
|
c67fdafb43 | ||
|
|
f66d277918 | ||
|
|
4ef7db20f8 | ||
|
|
e1216dfa76 | ||
|
|
340fa8a7cb | ||
|
|
a029455466 | ||
|
|
27f78c430b | ||
|
|
3d707e5e95 | ||
|
|
0406a2f614 | ||
|
|
a1c0e432ee | ||
|
|
57e57d9e43 | ||
|
|
e4e4188eaf | ||
|
|
f1553cc90e | ||
|
|
997e68c99a | ||
|
|
25663d9a7b | ||
|
|
1e55266396 | ||
|
|
810ae15991 | ||
|
|
271fec7f5e | ||
|
|
c43486791e | ||
|
|
499c03265f | ||
|
|
7eaa613d4d | ||
|
|
19db72031c | ||
|
|
671f9cca4b | ||
|
|
9ecdb80534 | ||
|
|
e973422ee1 | ||
|
|
8093c25b72 | ||
|
|
a7c9c620dd | ||
|
|
1e1d94ed73 | ||
|
|
7b8ef390ce | ||
|
|
87ade13234 | ||
|
|
32abde7f19 | ||
|
|
fe413af177 | ||
|
|
b4cd026fe5 | ||
|
|
a51b308cc8 | ||
|
|
1c09b42951 | ||
|
|
e42bb46563 | ||
|
|
a7788f9847 | ||
|
|
b597364419 | ||
|
|
a9a951d970 | ||
|
|
6ac879ec6a | ||
|
|
2422ef50b6 | ||
|
|
06dbf96b65 | ||
|
|
172046e78f | ||
|
|
7e8c49f0a0 | ||
|
|
f2ad6292f5 | ||
|
|
337e13b72e | ||
|
|
2ab2fc62dc | ||
|
|
786575c3de | ||
|
|
4cca194000 | ||
|
|
cd3ead0028 | ||
|
|
a239b95ca1 | ||
|
|
09574c0e82 | ||
|
|
0b6b01ef83 | ||
|
|
34145e459b | ||
|
|
207efca15c | ||
|
|
3e25c2ea46 | ||
|
|
2046678caa | ||
|
|
cb7e4e858b | ||
|
|
43ee4b9cb6 | ||
|
|
207c24a4fd | ||
|
|
6465ab3b6d | ||
|
|
cf624bc679 | ||
|
|
df55a776c7 | ||
|
|
07afe3887b | ||
|
|
25434ba84f | ||
|
|
9787dbd14f | ||
|
|
fd9081c80e | ||
|
|
e79c49019d | ||
|
|
5bc081a3af | ||
|
|
5976eb5186 | ||
|
|
b194bc05b1 | ||
|
|
18207fd79e | ||
|
|
7c1e0a51eb | ||
|
|
7196658166 | ||
|
|
187fe67ffa | ||
|
|
7136098c3c | ||
|
|
7979238029 | ||
|
|
fc38dff3b0 | ||
|
|
c590204cf9 | ||
|
|
284e49c807 | ||
|
|
43fcd3d1e2 | ||
|
|
a9f2d7df40 | ||
|
|
21a03d2b85 | ||
|
|
f123b8654a | ||
|
|
51cbe538e8 | ||
|
|
e247a2c4eb | ||
|
|
490b6684ac | ||
|
|
6fdeadf171 | ||
|
|
a88d491012 | ||
|
|
fd34d68933 | ||
|
|
215e3aab7b | ||
|
|
cd4feb3bab | ||
|
|
6b0af421dd | ||
|
|
9aeb4f2a96 | ||
|
|
9b1e789e62 | ||
|
|
406b163bcc | ||
|
|
6f2cae95e1 | ||
|
|
594a29b2db | ||
|
|
0d12464e30 | ||
|
|
ccd9ab70ee | ||
|
|
27f2f87e29 | ||
|
|
635eb9d36d | ||
|
|
f3e7f9bb8f | ||
|
|
2f69665056 | ||
|
|
559f7bc1b7 | ||
|
|
693f00caf5 | ||
|
|
822173979c | ||
|
|
9bce66f307 | ||
|
|
7a71e758b1 | ||
|
|
72fe0ca3e7 | ||
|
|
fd0570f0c9 | ||
|
|
e0037a0c8b | ||
|
|
c7c83282ee | ||
|
|
8f98d9792b | ||
|
|
111f7bd15e | ||
|
|
787af8de18 | ||
|
|
db6ebfe71b | ||
|
|
1cb490039f | ||
|
|
a152a64f1c | ||
|
|
08f50e56ac | ||
|
|
9ae221ca0c | ||
|
|
aca8da5891 | ||
|
|
34896560ea | ||
|
|
3597fbe382 | ||
|
|
0b262baf97 | ||
|
|
78b51ebe59 | ||
|
|
08a92468fe | ||
|
|
b84ed964f9 | ||
|
|
8bfe7b6b9d | ||
|
|
dbae173399 | ||
|
|
4d4dbcda4d | ||
|
|
fd4584a49d | ||
|
|
ceb9f795cb | ||
|
|
443c254d46 | ||
|
|
87fa150ced | ||
|
|
4cfdb8233f | ||
|
|
5e42cf76eb | ||
|
|
dccf6193da | ||
|
|
ee4fdf3f39 | ||
|
|
918b7f96db | ||
|
|
ef55345665 | ||
|
|
0a41dbb443 | ||
|
|
934ad32e52 | ||
|
|
b582f0f880 | ||
|
|
face3de44a | ||
|
|
8a7b9d776f | ||
|
|
a8c5d1095d | ||
|
|
9fa5028f6c | ||
|
|
0de3c089d2 | ||
|
|
919bc0138a | ||
|
|
a0210af5c6 | ||
|
|
888291db9a | ||
|
|
4fce663795 | ||
|
|
fc3384fb95 | ||
|
|
f9a30ca08e | ||
|
|
cbbe691f70 | ||
|
|
afbf0809c3 | ||
|
|
db8d4b4347 | ||
|
|
904ee7dfec | ||
|
|
bef616632c | ||
|
|
1d2e5d182e | ||
|
|
568ee1fa85 | ||
|
|
66633a7728 | ||
|
|
18283b44b2 | ||
|
|
278696b28e | ||
|
|
26efd09d45 | ||
|
|
ec9aba79ae | ||
|
|
07e42d81f4 | ||
|
|
d272afc128 | ||
|
|
6cf9fa2208 | ||
|
|
a72451cdbe | ||
|
|
e82489b158 | ||
|
|
e4c7fa6a1c | ||
|
|
48ed24dabf | ||
|
|
9ef060a2f2 | ||
|
|
255c41607f | ||
|
|
22da026888 | ||
|
|
def0a63008 | ||
|
|
6e62e123e8 | ||
|
|
262df56024 | ||
|
|
81cdf071a8 | ||
|
|
c5ce75888e | ||
|
|
fb232896a8 | ||
|
|
5efa462f19 | ||
|
|
225f3ab125 | ||
|
|
664fbfeb6e | ||
|
|
66f8ca0501 | ||
|
|
e977d63f08 | ||
|
|
08fc3cab38 | ||
|
|
a01885536c | ||
|
|
f7343674ee | ||
|
|
fc2adf98ba | ||
|
|
a3c57a5077 | ||
|
|
28b5dd0163 | ||
|
|
c0b69e4e6f | ||
|
|
a9111d78d3 | ||
|
|
cc91e22038 | ||
|
|
4555f69733 | ||
|
|
da8ba56dd1 | ||
|
|
4d6d5620b0 | ||
|
|
f7cad98f3e | ||
|
|
d978c4c3bf | ||
|
|
1c4b7810b1 | ||
|
|
a6ec43f81c |
94
.appveyor.yml
Normal file
94
.appveyor.yml
Normal file
@@ -0,0 +1,94 @@
|
||||
# .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
1
.ci
Submodule
Submodule .ci added at 12d769835e
6
.ci-local/defaults.set
Normal file
6
.ci-local/defaults.set
Normal file
@@ -0,0 +1,6 @@
|
||||
# EPICS Base
|
||||
BASE_DIRNAME=base
|
||||
BASE_REPONAME=epics-base
|
||||
BASE_REPOOWNER=epics-base
|
||||
BASE_VARNAME=EPICS_BASE
|
||||
BASE_RECURSIVE=NO
|
||||
30
.gitignore
vendored
30
.gitignore
vendored
@@ -1,15 +1,17 @@
|
||||
bin/
|
||||
lib/
|
||||
doc/
|
||||
include/
|
||||
db/
|
||||
dbd/
|
||||
documentation/html
|
||||
documentation/*.tag
|
||||
/cfg/
|
||||
/bin/
|
||||
/lib/
|
||||
/db/
|
||||
/dbd/
|
||||
/html/
|
||||
/include/
|
||||
/templates/
|
||||
/configure/*.local
|
||||
/configure/RELEASE.*
|
||||
/configure/CONFIG_SITE.*
|
||||
O.*/
|
||||
/QtC-*
|
||||
envPaths
|
||||
configure/*.local
|
||||
configure/RELEASE.*
|
||||
configure/CONFIG_SITE.*
|
||||
!configure/ExampleRELEASE.local
|
||||
**/O.*
|
||||
QtC-*
|
||||
*.orig
|
||||
*.log
|
||||
.*.swp
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule ".ci"]
|
||||
path = .ci
|
||||
url = https://github.com/epics-base/ci-scripts
|
||||
17
.readthedocs.yml
Normal file
17
.readthedocs.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
# .readthedocs.yml
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Build documentation in the documentation/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: documentation/conf.py
|
||||
|
||||
# Build documentation with MkDocs
|
||||
#mkdocs:
|
||||
# configuration: mkdocs.yml
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
formats: all
|
||||
94
.travis.yml
Normal file
94
.travis.yml
Normal file
@@ -0,0 +1,94 @@
|
||||
# .travis.yml for use with EPICS Base ci-scripts
|
||||
# (see: https://github.com/epics-base/ci-scripts)
|
||||
|
||||
language: cpp
|
||||
compiler: gcc
|
||||
dist: bionic
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache
|
||||
|
||||
env:
|
||||
global:
|
||||
- SETUP_PATH=.ci-local
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
# for all EPICS builds
|
||||
- libreadline6-dev
|
||||
- libncurses5-dev
|
||||
- perl
|
||||
# for clang compiler
|
||||
- clang
|
||||
# for mingw builds (32bit and 64bit)
|
||||
- g++-mingw-w64-i686
|
||||
- g++-mingw-w64-x86-64
|
||||
# for RTEMS cross builds
|
||||
- qemu-system-x86
|
||||
|
||||
install:
|
||||
- python .ci/cue.py prepare
|
||||
|
||||
script:
|
||||
- python .ci/cue.py build
|
||||
- python .ci/cue.py test-results
|
||||
|
||||
jobs:
|
||||
include:
|
||||
|
||||
# Windows builds
|
||||
|
||||
- env: BASE=7.0
|
||||
os: windows
|
||||
compiler: vs2017
|
||||
|
||||
- env: BASE=7.0
|
||||
os: windows
|
||||
|
||||
# Different configurations of default gcc and clang
|
||||
|
||||
- env: BASE=7.0
|
||||
|
||||
- env: BASE=7.0
|
||||
compiler: clang
|
||||
|
||||
- env: BASE=7.0 EXTRA="CMD_CXXFLAGS=-std=c++11"
|
||||
|
||||
- env: BASE=7.0 EXTRA="CMD_CXXFLAGS=-std=c++11"
|
||||
compiler: clang
|
||||
|
||||
# Trusty: compiler versions very close to RHEL 7
|
||||
|
||||
- env: BASE=7.0
|
||||
dist: trusty
|
||||
|
||||
- env: BASE=7.0 EXTRA="CMD_CXXFLAGS=-std=c++11"
|
||||
dist: trusty
|
||||
|
||||
- env: BASE=3.15 EXTRA="CMD_CXXFLAGS=-std=c++11"
|
||||
dist: trusty
|
||||
|
||||
# Cross-compilations to Windows using MinGW and WINE
|
||||
|
||||
- env: BASE=7.0 WINE=32 TEST=NO BCFG=static
|
||||
|
||||
- env: BASE=7.0 WINE=64 TEST=NO
|
||||
|
||||
# Other gcc versions (added as an extra package)
|
||||
|
||||
- env: BASE=7.0
|
||||
compiler: gcc-6
|
||||
addons: { apt: { packages: ["g++-6"], sources: ["ubuntu-toolchain-r-test"] } }
|
||||
|
||||
- env: BASE=7.0
|
||||
compiler: gcc-7
|
||||
addons: { apt: { packages: ["g++-7"], sources: ["ubuntu-toolchain-r-test"] } }
|
||||
|
||||
# MacOS build
|
||||
|
||||
- env: BASE=7.0
|
||||
os: osx
|
||||
compiler: clang
|
||||
addons: { homebrew: { packages: ["re2c"], update: true } }
|
||||
316
Doxyfile
316
Doxyfile
@@ -1,4 +1,4 @@
|
||||
# Doxyfile 1.8.6
|
||||
# Doxyfile 1.8.8
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project.
|
||||
@@ -32,33 +32,33 @@ DOXYFILE_ENCODING = UTF-8
|
||||
# title of most generated pages and in a few other places.
|
||||
# The default value is: My Project.
|
||||
|
||||
PROJECT_NAME = pvDataCPP
|
||||
PROJECT_NAME = "PVData C++"
|
||||
|
||||
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER =
|
||||
PROJECT_NUMBER = 8.0.3
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
# quick idea about the purpose of the project. Keep the description short.
|
||||
|
||||
PROJECT_BRIEF =
|
||||
PROJECT_BRIEF =
|
||||
|
||||
# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
|
||||
# the documentation. The maximum height of the logo should not exceed 55 pixels
|
||||
# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
|
||||
# to the output directory.
|
||||
|
||||
PROJECT_LOGO =
|
||||
PROJECT_LOGO =
|
||||
|
||||
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
|
||||
# into which the generated documentation will be written. If a relative path is
|
||||
# entered, it will be relative to the location where doxygen was started. If
|
||||
# left blank the current directory will be used.
|
||||
|
||||
OUTPUT_DIRECTORY =
|
||||
OUTPUT_DIRECTORY = .
|
||||
|
||||
# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
|
||||
# directories (in 2 levels) under the output directory of each output format and
|
||||
@@ -70,6 +70,14 @@ OUTPUT_DIRECTORY =
|
||||
|
||||
CREATE_SUBDIRS = NO
|
||||
|
||||
# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
|
||||
# characters to appear in the names of generated files. If set to NO, non-ASCII
|
||||
# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
|
||||
# U+3044.
|
||||
# The default value is: NO.
|
||||
|
||||
ALLOW_UNICODE_NAMES = NO
|
||||
|
||||
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
|
||||
# documentation generated by doxygen is written. Doxygen will use this
|
||||
# information to generate all constant output in the proper language.
|
||||
@@ -110,7 +118,17 @@ REPEAT_BRIEF = YES
|
||||
# the entity):The $name class, The $name widget, The $name file, is, provides,
|
||||
# specifies, contains, represents, a, an and the.
|
||||
|
||||
ABBREVIATE_BRIEF =
|
||||
ABBREVIATE_BRIEF = "The $name class" \
|
||||
"The $name widget" \
|
||||
"The $name file" \
|
||||
is \
|
||||
provides \
|
||||
specifies \
|
||||
contains \
|
||||
represents \
|
||||
a \
|
||||
an \
|
||||
the
|
||||
|
||||
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
|
||||
# doxygen will generate a detailed section even if there is only a brief
|
||||
@@ -125,7 +143,7 @@ ALWAYS_DETAILED_SEC = NO
|
||||
# operators of the base classes will not be shown.
|
||||
# The default value is: NO.
|
||||
|
||||
INLINE_INHERITED_MEMB = YES
|
||||
INLINE_INHERITED_MEMB = NO
|
||||
|
||||
# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
|
||||
# before files name in the file list and in the header files. If set to NO the
|
||||
@@ -144,7 +162,7 @@ FULL_PATH_NAMES = YES
|
||||
# will be relative from the directory where doxygen is started.
|
||||
# This tag requires that the tag FULL_PATH_NAMES is set to YES.
|
||||
|
||||
STRIP_FROM_PATH =
|
||||
STRIP_FROM_PATH = src
|
||||
|
||||
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
|
||||
# path mentioned in the documentation of a class, which tells the reader which
|
||||
@@ -153,7 +171,7 @@ STRIP_FROM_PATH =
|
||||
# specify the list of include paths that are normally passed to the compiler
|
||||
# using the -I flag.
|
||||
|
||||
STRIP_FROM_INC_PATH =
|
||||
STRIP_FROM_INC_PATH = src
|
||||
|
||||
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
|
||||
# less readable) file names. This can be useful is your file systems doesn't
|
||||
@@ -220,13 +238,13 @@ TAB_SIZE = 4
|
||||
# "Side Effects:". You can put \n's in the value part of an alias to insert
|
||||
# newlines.
|
||||
|
||||
ALIASES =
|
||||
ALIASES =
|
||||
|
||||
# This tag can be used to specify a number of word-keyword mappings (TCL only).
|
||||
# A mapping has the form "name=value". For example adding "class=itcl::class"
|
||||
# will allow you to use the command class in the itcl::class meaning.
|
||||
|
||||
TCL_SUBST =
|
||||
TCL_SUBST =
|
||||
|
||||
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
|
||||
# only. Doxygen will then generate output that is more tailored for C. For
|
||||
@@ -261,16 +279,19 @@ OPTIMIZE_OUTPUT_VHDL = NO
|
||||
# extension. Doxygen has a built-in mapping, but you can override or extend it
|
||||
# using this tag. The format is ext=language, where ext is a file extension, and
|
||||
# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
|
||||
# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
|
||||
# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
|
||||
# (default is Fortran), use: inc=Fortran f=C.
|
||||
# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
|
||||
# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
|
||||
# Fortran. In the later case the parser tries to guess whether the code is fixed
|
||||
# or free formatted code, this is the default for Fortran type files), VHDL. For
|
||||
# instance to make doxygen treat .inc files as Fortran files (default is PHP),
|
||||
# and .f files as C (default is Fortran), use: inc=Fortran f=C.
|
||||
#
|
||||
# Note For files without extension you can use no_extension as a placeholder.
|
||||
#
|
||||
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
|
||||
# the files are not read by doxygen.
|
||||
|
||||
EXTENSION_MAPPING =
|
||||
EXTENSION_MAPPING =
|
||||
|
||||
# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
|
||||
# according to the Markdown format, which allows for more readable
|
||||
@@ -298,7 +319,7 @@ AUTOLINK_SUPPORT = YES
|
||||
# diagrams that involve STL classes more complete and accurate.
|
||||
# The default value is: NO.
|
||||
|
||||
BUILTIN_STL_SUPPORT = YES
|
||||
BUILTIN_STL_SUPPORT = NO
|
||||
|
||||
# If you use Microsoft's C++/CLI language, you should set this option to YES to
|
||||
# enable parsing support.
|
||||
@@ -398,7 +419,7 @@ LOOKUP_CACHE_SIZE = 0
|
||||
# normally produced when WARNINGS is set to YES.
|
||||
# The default value is: NO.
|
||||
|
||||
EXTRACT_ALL = YES
|
||||
EXTRACT_ALL = NO
|
||||
|
||||
# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
|
||||
# be included in the documentation.
|
||||
@@ -487,7 +508,7 @@ INTERNAL_DOCS = NO
|
||||
# and Mac users are advised to set this option to NO.
|
||||
# The default value is: system dependent.
|
||||
|
||||
CASE_SENSE_NAMES = YES
|
||||
CASE_SENSE_NAMES = NO
|
||||
|
||||
# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
|
||||
# their full class and namespace scopes in the documentation. If set to YES the
|
||||
@@ -606,7 +627,7 @@ GENERATE_DEPRECATEDLIST= YES
|
||||
# sections, marked by \if <section_label> ... \endif and \cond <section_label>
|
||||
# ... \endcond blocks.
|
||||
|
||||
ENABLED_SECTIONS =
|
||||
ENABLED_SECTIONS =
|
||||
|
||||
# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
|
||||
# initial value of a variable or macro / define can have for it to appear in the
|
||||
@@ -648,7 +669,7 @@ SHOW_NAMESPACES = YES
|
||||
# by doxygen. Whatever the program writes to standard output is used as the file
|
||||
# version. For an example see the documentation.
|
||||
|
||||
FILE_VERSION_FILTER =
|
||||
FILE_VERSION_FILTER =
|
||||
|
||||
# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
|
||||
# by doxygen. The layout file controls the global structure of the generated
|
||||
@@ -661,7 +682,7 @@ FILE_VERSION_FILTER =
|
||||
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
|
||||
# tag is left empty.
|
||||
|
||||
LAYOUT_FILE =
|
||||
LAYOUT_FILE =
|
||||
|
||||
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
|
||||
# the reference definitions. This must be a list of .bib files. The .bib
|
||||
@@ -669,10 +690,9 @@ LAYOUT_FILE =
|
||||
# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
|
||||
# For LaTeX the style of the bibliography can be controlled using
|
||||
# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
|
||||
# search path. Do not use file names with spaces, bibtex cannot handle them. See
|
||||
# also \cite for info how to create references.
|
||||
# search path. See also \cite for info how to create references.
|
||||
|
||||
CITE_BIB_FILES =
|
||||
CITE_BIB_FILES =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to warning and progress messages
|
||||
@@ -699,7 +719,7 @@ WARNINGS = YES
|
||||
# will automatically be disabled.
|
||||
# The default value is: YES.
|
||||
|
||||
WARN_IF_UNDOCUMENTED = YES
|
||||
WARN_IF_UNDOCUMENTED = NO
|
||||
|
||||
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
|
||||
# potential errors in the documentation, such as not documenting some parameters
|
||||
@@ -731,7 +751,7 @@ WARN_FORMAT = "$file:$line: $text"
|
||||
# messages should be written. If left blank the output is written to standard
|
||||
# error (stderr).
|
||||
|
||||
WARN_LOGFILE =
|
||||
WARN_LOGFILE =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the input files
|
||||
@@ -743,7 +763,12 @@ WARN_LOGFILE =
|
||||
# spaces.
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = include
|
||||
INPUT = src/pv \
|
||||
documentation/mainpage.dox \
|
||||
src/copy/pv \
|
||||
src/misc/pv \
|
||||
src/json/pv \
|
||||
documentation/release_notes.dox
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
@@ -763,13 +788,13 @@ INPUT_ENCODING = UTF-8
|
||||
# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
|
||||
# *.qsf, *.as and *.js.
|
||||
|
||||
FILE_PATTERNS =
|
||||
FILE_PATTERNS =
|
||||
|
||||
# The RECURSIVE tag can be used to specify whether or not subdirectories should
|
||||
# be searched for input files as well.
|
||||
# The default value is: NO.
|
||||
|
||||
RECURSIVE = YES
|
||||
RECURSIVE = NO
|
||||
|
||||
# The EXCLUDE tag can be used to specify files and/or directories that should be
|
||||
# excluded from the INPUT source files. This way you can easily exclude a
|
||||
@@ -778,7 +803,7 @@ RECURSIVE = YES
|
||||
# Note that relative paths are relative to the directory from which doxygen is
|
||||
# run.
|
||||
|
||||
EXCLUDE =
|
||||
EXCLUDE =
|
||||
|
||||
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
|
||||
# directories that are symbolic links (a Unix file system feature) are excluded
|
||||
@@ -794,7 +819,7 @@ EXCLUDE_SYMLINKS = NO
|
||||
# Note that the wildcards are matched against the file with absolute path, so to
|
||||
# exclude all test directories for example use the pattern */test/*
|
||||
|
||||
EXCLUDE_PATTERNS =
|
||||
EXCLUDE_PATTERNS =
|
||||
|
||||
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
|
||||
# (namespaces, classes, functions, etc.) that should be excluded from the
|
||||
@@ -805,20 +830,20 @@ EXCLUDE_PATTERNS =
|
||||
# Note that the wildcards are matched against the file with absolute path, so to
|
||||
# exclude all test directories use the pattern */test/*
|
||||
|
||||
EXCLUDE_SYMBOLS =
|
||||
EXCLUDE_SYMBOLS =
|
||||
|
||||
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
|
||||
# that contain example code fragments that are included (see the \include
|
||||
# command).
|
||||
|
||||
EXAMPLE_PATH =
|
||||
EXAMPLE_PATH = examples
|
||||
|
||||
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
|
||||
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
|
||||
# *.h) to filter out the source-files in the directories. If left blank all
|
||||
# files are included.
|
||||
|
||||
EXAMPLE_PATTERNS =
|
||||
EXAMPLE_PATTERNS = *.cpp
|
||||
|
||||
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
|
||||
# searched for input files to be used with the \include or \dontinclude commands
|
||||
@@ -831,7 +856,7 @@ EXAMPLE_RECURSIVE = NO
|
||||
# that contain images that are to be included in the documentation (see the
|
||||
# \image command).
|
||||
|
||||
IMAGE_PATH =
|
||||
IMAGE_PATH =
|
||||
|
||||
# The INPUT_FILTER tag can be used to specify a program that doxygen should
|
||||
# invoke to filter for each input file. Doxygen will invoke the filter program
|
||||
@@ -848,7 +873,7 @@ IMAGE_PATH =
|
||||
# code is scanned, but not when the output code is generated. If lines are added
|
||||
# or removed, the anchors will not be placed correctly.
|
||||
|
||||
INPUT_FILTER =
|
||||
INPUT_FILTER =
|
||||
|
||||
# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
|
||||
# basis. Doxygen will compare the file name with each pattern and apply the
|
||||
@@ -857,7 +882,7 @@ INPUT_FILTER =
|
||||
# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
|
||||
# patterns match the file name, INPUT_FILTER is applied.
|
||||
|
||||
FILTER_PATTERNS =
|
||||
FILTER_PATTERNS =
|
||||
|
||||
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
|
||||
# INPUT_FILTER ) will also be used to filter the input files that are used for
|
||||
@@ -872,14 +897,14 @@ FILTER_SOURCE_FILES = NO
|
||||
# *.ext= (so without naming a filter).
|
||||
# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
|
||||
|
||||
FILTER_SOURCE_PATTERNS =
|
||||
FILTER_SOURCE_PATTERNS =
|
||||
|
||||
# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
|
||||
# is part of the input, its contents will be placed on the main page
|
||||
# (index.html). This can be useful if you have a project on for instance GitHub
|
||||
# and want to reuse the introduction page also for the doxygen output.
|
||||
|
||||
USE_MDFILE_AS_MAINPAGE =
|
||||
USE_MDFILE_AS_MAINPAGE =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to source browsing
|
||||
@@ -911,13 +936,13 @@ STRIP_CODE_COMMENTS = YES
|
||||
# function all documented functions referencing it will be listed.
|
||||
# The default value is: NO.
|
||||
|
||||
REFERENCED_BY_RELATION = YES
|
||||
REFERENCED_BY_RELATION = NO
|
||||
|
||||
# If the REFERENCES_RELATION tag is set to YES then for each documented function
|
||||
# all documented entities called/used by that function will be listed.
|
||||
# The default value is: NO.
|
||||
|
||||
REFERENCES_RELATION = YES
|
||||
REFERENCES_RELATION = NO
|
||||
|
||||
# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
|
||||
# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
|
||||
@@ -967,6 +992,25 @@ USE_HTAGS = NO
|
||||
|
||||
VERBATIM_HEADERS = YES
|
||||
|
||||
# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the
|
||||
# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
|
||||
# cost of reduced performance. This can be particularly helpful with template
|
||||
# rich C++ code for which doxygen's built-in parser lacks the necessary type
|
||||
# information.
|
||||
# Note: The availability of this option depends on whether or not doxygen was
|
||||
# compiled with the --with-libclang option.
|
||||
# The default value is: NO.
|
||||
|
||||
CLANG_ASSISTED_PARSING = YES
|
||||
|
||||
# If clang assisted parsing is enabled you can provide the compiler with command
|
||||
# line options that you would normally use when invoking the compiler. Note that
|
||||
# the include paths will already be set by doxygen for the files and directories
|
||||
# specified with INPUT and INCLUDE_PATH.
|
||||
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
|
||||
|
||||
CLANG_OPTIONS =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the alphabetical class index
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -976,7 +1020,7 @@ VERBATIM_HEADERS = YES
|
||||
# classes, structs, unions or interfaces.
|
||||
# The default value is: YES.
|
||||
|
||||
ALPHABETICAL_INDEX = NO
|
||||
ALPHABETICAL_INDEX = YES
|
||||
|
||||
# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
|
||||
# which the alphabetical index list will be split.
|
||||
@@ -991,7 +1035,7 @@ COLS_IN_ALPHA_INDEX = 5
|
||||
# while generating the index headers.
|
||||
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
|
||||
|
||||
IGNORE_PREFIX =
|
||||
IGNORE_PREFIX =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the HTML output
|
||||
@@ -1008,7 +1052,7 @@ GENERATE_HTML = YES
|
||||
# The default directory is: html.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_OUTPUT = documentation/html
|
||||
HTML_OUTPUT = html/doxygen
|
||||
|
||||
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
|
||||
# generated HTML page (for example: .htm, .php, .asp).
|
||||
@@ -1035,7 +1079,7 @@ HTML_FILE_EXTENSION = .html
|
||||
# of the possible markers and block names see the documentation.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_HEADER =
|
||||
HTML_HEADER =
|
||||
|
||||
# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
|
||||
# generated HTML page. If the tag is left blank doxygen will generate a standard
|
||||
@@ -1045,7 +1089,7 @@ HTML_HEADER =
|
||||
# that doxygen normally uses.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_FOOTER =
|
||||
HTML_FOOTER =
|
||||
|
||||
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
|
||||
# sheet that is used by each HTML page. It can be used to fine-tune the look of
|
||||
@@ -1057,18 +1101,20 @@ HTML_FOOTER =
|
||||
# obsolete.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_STYLESHEET =
|
||||
HTML_STYLESHEET =
|
||||
|
||||
# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
|
||||
# defined cascading style sheet that is included after the standard style sheets
|
||||
# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
|
||||
# cascading style sheets that are included after the standard style sheets
|
||||
# created by doxygen. Using this option one can overrule certain style aspects.
|
||||
# This is preferred over using HTML_STYLESHEET since it does not replace the
|
||||
# standard style sheet and is therefor more robust against future updates.
|
||||
# Doxygen will copy the style sheet file to the output directory. For an example
|
||||
# see the documentation.
|
||||
# Doxygen will copy the style sheet files to the output directory.
|
||||
# Note: The order of the extra stylesheet files is of importance (e.g. the last
|
||||
# stylesheet in the list overrules the setting of the previous ones in the
|
||||
# list). For an example see the documentation.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_EXTRA_STYLESHEET =
|
||||
HTML_EXTRA_STYLESHEET =
|
||||
|
||||
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
|
||||
# other source files which should be copied to the HTML output directory. Note
|
||||
@@ -1078,7 +1124,7 @@ HTML_EXTRA_STYLESHEET =
|
||||
# files will be copied as-is; there are no commands or markers available.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_EXTRA_FILES = documentation/pvDataCPP.html
|
||||
HTML_EXTRA_FILES =
|
||||
|
||||
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
|
||||
# will adjust the colors in the stylesheet and background images according to
|
||||
@@ -1206,7 +1252,7 @@ GENERATE_HTMLHELP = NO
|
||||
# written to the html output directory.
|
||||
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
|
||||
|
||||
CHM_FILE =
|
||||
CHM_FILE =
|
||||
|
||||
# The HHC_LOCATION tag can be used to specify the location (absolute path
|
||||
# including file name) of the HTML help compiler ( hhc.exe). If non-empty
|
||||
@@ -1214,7 +1260,7 @@ CHM_FILE =
|
||||
# The file has to be specified with full path.
|
||||
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
|
||||
|
||||
HHC_LOCATION =
|
||||
HHC_LOCATION =
|
||||
|
||||
# The GENERATE_CHI flag controls if a separate .chi index file is generated (
|
||||
# YES) or that it should be included in the master .chm file ( NO).
|
||||
@@ -1227,10 +1273,11 @@ GENERATE_CHI = NO
|
||||
# and project file content.
|
||||
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
|
||||
|
||||
CHM_INDEX_ENCODING =
|
||||
CHM_INDEX_ENCODING =
|
||||
|
||||
# The BINARY_TOC flag controls whether a binary table of contents is generated (
|
||||
# YES) or a normal table of contents ( NO) in the .chm file.
|
||||
# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it
|
||||
# enables the Previous and Next buttons.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
|
||||
|
||||
@@ -1257,7 +1304,7 @@ GENERATE_QHP = NO
|
||||
# the HTML output folder.
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QCH_FILE =
|
||||
QCH_FILE =
|
||||
|
||||
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
|
||||
# Project output. For more information please see Qt Help Project / Namespace
|
||||
@@ -1282,7 +1329,7 @@ QHP_VIRTUAL_FOLDER = doc
|
||||
# filters).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_CUST_FILTER_NAME =
|
||||
QHP_CUST_FILTER_NAME =
|
||||
|
||||
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
|
||||
# custom filter to add. For more information please see Qt Help Project / Custom
|
||||
@@ -1290,21 +1337,21 @@ QHP_CUST_FILTER_NAME =
|
||||
# filters).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_CUST_FILTER_ATTRS =
|
||||
QHP_CUST_FILTER_ATTRS =
|
||||
|
||||
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
|
||||
# project's filter section matches. Qt Help Project / Filter Attributes (see:
|
||||
# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_SECT_FILTER_ATTRS =
|
||||
QHP_SECT_FILTER_ATTRS =
|
||||
|
||||
# The QHG_LOCATION tag can be used to specify the location of Qt's
|
||||
# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
|
||||
# generated .qhp file.
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHG_LOCATION =
|
||||
QHG_LOCATION =
|
||||
|
||||
# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
|
||||
# generated, together with the HTML files, they form an Eclipse help plugin. To
|
||||
@@ -1437,7 +1484,7 @@ MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
|
||||
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
MATHJAX_EXTENSIONS =
|
||||
MATHJAX_EXTENSIONS =
|
||||
|
||||
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
|
||||
# of code that will be used on startup of the MathJax code. See the MathJax site
|
||||
@@ -1445,7 +1492,7 @@ MATHJAX_EXTENSIONS =
|
||||
# example see the documentation.
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
MATHJAX_CODEFILE =
|
||||
MATHJAX_CODEFILE =
|
||||
|
||||
# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
|
||||
# the HTML output. The underlying search engine uses javascript and DHTML and
|
||||
@@ -1466,15 +1513,15 @@ MATHJAX_CODEFILE =
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
SEARCHENGINE = NO
|
||||
SEARCHENGINE = YES
|
||||
|
||||
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
|
||||
# implemented using a web server instead of a web client using Javascript. There
|
||||
# are two flavours of web server based searching depending on the
|
||||
# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
|
||||
# searching and an index file used by the script. When EXTERNAL_SEARCH is
|
||||
# enabled the indexing and searching needs to be provided by external tools. See
|
||||
# the section "External Indexing and Searching" for details.
|
||||
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
|
||||
# setting. When disabled, doxygen will generate a PHP script for searching and
|
||||
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
|
||||
# and searching needs to be provided by external tools. See the section
|
||||
# "External Indexing and Searching" for details.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag SEARCHENGINE is set to YES.
|
||||
|
||||
@@ -1505,7 +1552,7 @@ EXTERNAL_SEARCH = NO
|
||||
# Searching" for details.
|
||||
# This tag requires that the tag SEARCHENGINE is set to YES.
|
||||
|
||||
SEARCHENGINE_URL =
|
||||
SEARCHENGINE_URL =
|
||||
|
||||
# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
|
||||
# search data is written to a file for indexing by an external tool. With the
|
||||
@@ -1521,7 +1568,7 @@ SEARCHDATA_FILE = searchdata.xml
|
||||
# projects and redirect the results back to the right project.
|
||||
# This tag requires that the tag SEARCHENGINE is set to YES.
|
||||
|
||||
EXTERNAL_SEARCH_ID =
|
||||
EXTERNAL_SEARCH_ID =
|
||||
|
||||
# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
|
||||
# projects other than the one defined by this configuration file, but that are
|
||||
@@ -1531,7 +1578,7 @@ EXTERNAL_SEARCH_ID =
|
||||
# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
|
||||
# This tag requires that the tag SEARCHENGINE is set to YES.
|
||||
|
||||
EXTRA_SEARCH_MAPPINGS =
|
||||
EXTRA_SEARCH_MAPPINGS =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the LaTeX output
|
||||
@@ -1583,7 +1630,7 @@ COMPACT_LATEX = NO
|
||||
# The default value is: a4.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
PAPER_TYPE = a4wide
|
||||
PAPER_TYPE = a4
|
||||
|
||||
# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
|
||||
# that should be included in the LaTeX output. To get the times font for
|
||||
@@ -1592,7 +1639,7 @@ PAPER_TYPE = a4wide
|
||||
# If left blank no extra packages will be included.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
EXTRA_PACKAGES =
|
||||
EXTRA_PACKAGES =
|
||||
|
||||
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
|
||||
# generated LaTeX document. The header should contain everything until the first
|
||||
@@ -1602,22 +1649,24 @@ EXTRA_PACKAGES =
|
||||
#
|
||||
# Note: Only use a user-defined header if you know what you are doing! The
|
||||
# following commands have a special meaning inside the header: $title,
|
||||
# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
|
||||
# replace them by respectively the title of the page, the current date and time,
|
||||
# only the current date, the version number of doxygen, the project name (see
|
||||
# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
|
||||
# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
|
||||
# $projectbrief, $projectlogo. Doxygen will replace $title with the empy string,
|
||||
# for the replacement values of the other commands the user is refered to
|
||||
# HTML_HEADER.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_HEADER =
|
||||
LATEX_HEADER =
|
||||
|
||||
# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
|
||||
# generated LaTeX document. The footer should contain everything after the last
|
||||
# chapter. If it is left blank doxygen will generate a standard footer.
|
||||
# chapter. If it is left blank doxygen will generate a standard footer. See
|
||||
# LATEX_HEADER for more information on how to generate a default footer and what
|
||||
# special commands can be used inside the footer.
|
||||
#
|
||||
# Note: Only use a user-defined footer if you know what you are doing!
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_FOOTER =
|
||||
LATEX_FOOTER =
|
||||
|
||||
# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
|
||||
# other source files which should be copied to the LATEX_OUTPUT output
|
||||
@@ -1625,7 +1674,7 @@ LATEX_FOOTER =
|
||||
# markers available.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_EXTRA_FILES =
|
||||
LATEX_EXTRA_FILES =
|
||||
|
||||
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
|
||||
# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
|
||||
@@ -1634,15 +1683,15 @@ LATEX_EXTRA_FILES =
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
PDF_HYPERLINKS = NO
|
||||
PDF_HYPERLINKS = YES
|
||||
|
||||
# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
|
||||
# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
|
||||
# the PDF file directly from the LaTeX files. Set this option to YES to get a
|
||||
# higher quality PDF documentation.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
USE_PDFLATEX = NO
|
||||
USE_PDFLATEX = YES
|
||||
|
||||
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
|
||||
# command to the generated LaTeX files. This will instruct LaTeX to keep running
|
||||
@@ -1725,14 +1774,14 @@ RTF_HYPERLINKS = NO
|
||||
# default style sheet that doxygen normally uses.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
RTF_STYLESHEET_FILE =
|
||||
RTF_STYLESHEET_FILE =
|
||||
|
||||
# Set optional variables used in the generation of an RTF document. Syntax is
|
||||
# similar to doxygen's config file. A template extensions file can be generated
|
||||
# using doxygen -e rtf extensionFile.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
RTF_EXTENSIONS_FILE =
|
||||
RTF_EXTENSIONS_FILE =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the man page output
|
||||
@@ -1762,6 +1811,13 @@ MAN_OUTPUT = man
|
||||
|
||||
MAN_EXTENSION = .3
|
||||
|
||||
# The MAN_SUBDIR tag determines the name of the directory created within
|
||||
# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
|
||||
# MAN_EXTENSION with the initial . removed.
|
||||
# This tag requires that the tag GENERATE_MAN is set to YES.
|
||||
|
||||
MAN_SUBDIR =
|
||||
|
||||
# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
|
||||
# will generate one additional man file for each entity documented in the real
|
||||
# man page(s). These additional files only source the real man page, but without
|
||||
@@ -1789,18 +1845,6 @@ GENERATE_XML = NO
|
||||
|
||||
XML_OUTPUT = xml
|
||||
|
||||
# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a
|
||||
# validating XML parser to check the syntax of the XML files.
|
||||
# This tag requires that the tag GENERATE_XML is set to YES.
|
||||
|
||||
XML_SCHEMA =
|
||||
|
||||
# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
|
||||
# validating XML parser to check the syntax of the XML files.
|
||||
# This tag requires that the tag GENERATE_XML is set to YES.
|
||||
|
||||
XML_DTD =
|
||||
|
||||
# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
|
||||
# listings (including syntax highlighting and cross-referencing information) to
|
||||
# the XML output. Note that enabling this will significantly increase the size
|
||||
@@ -1828,6 +1872,15 @@ GENERATE_DOCBOOK = NO
|
||||
|
||||
DOCBOOK_OUTPUT = docbook
|
||||
|
||||
# If the DOCBOOK_PROGRAMLISTING tag is set to YES doxygen will include the
|
||||
# program listings (including syntax highlighting and cross-referencing
|
||||
# information) to the DOCBOOK output. Note that enabling this will significantly
|
||||
# increase the size of the DOCBOOK output.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
|
||||
|
||||
DOCBOOK_PROGRAMLISTING = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options for the AutoGen Definitions output
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -1876,7 +1929,7 @@ PERLMOD_PRETTY = YES
|
||||
# overwrite each other's variables.
|
||||
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
|
||||
|
||||
PERLMOD_MAKEVAR_PREFIX =
|
||||
PERLMOD_MAKEVAR_PREFIX =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the preprocessor
|
||||
@@ -1895,7 +1948,7 @@ ENABLE_PREPROCESSING = YES
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
MACRO_EXPANSION = NO
|
||||
MACRO_EXPANSION = YES
|
||||
|
||||
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
|
||||
# the macro expansion is limited to the macros specified with the PREDEFINED and
|
||||
@@ -1917,7 +1970,7 @@ SEARCH_INCLUDES = YES
|
||||
# preprocessor.
|
||||
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
|
||||
|
||||
INCLUDE_PATH =
|
||||
INCLUDE_PATH = src/misc src ../../include/
|
||||
|
||||
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
|
||||
# patterns (like *.h and *.hpp) to filter out the header-files in the
|
||||
@@ -1925,7 +1978,7 @@ INCLUDE_PATH =
|
||||
# used.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
INCLUDE_FILE_PATTERNS =
|
||||
INCLUDE_FILE_PATTERNS =
|
||||
|
||||
# The PREDEFINED tag can be used to specify one or more macro names that are
|
||||
# defined before the preprocessor is started (similar to the -D option of e.g.
|
||||
@@ -1935,7 +1988,7 @@ INCLUDE_FILE_PATTERNS =
|
||||
# recursively expanded use the := operator instead of the = operator.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
PREDEFINED =
|
||||
PREDEFINED =
|
||||
|
||||
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
|
||||
# tag can be used to specify a list of macro names that should be expanded. The
|
||||
@@ -1947,14 +2000,14 @@ PREDEFINED =
|
||||
EXPAND_AS_DEFINED =
|
||||
|
||||
# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
|
||||
# remove all refrences to function-like macros that are alone on a line, have an
|
||||
# all uppercase name, and do not end with a semicolon. Such function macros are
|
||||
# typically used for boiler-plate code, and will confuse the parser if not
|
||||
# remove all references to function-like macros that are alone on a line, have
|
||||
# an all uppercase name, and do not end with a semicolon. Such function macros
|
||||
# are typically used for boiler-plate code, and will confuse the parser if not
|
||||
# removed.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
SKIP_FUNCTION_MACROS = YES
|
||||
SKIP_FUNCTION_MACROS = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to external references
|
||||
@@ -1969,17 +2022,17 @@ SKIP_FUNCTION_MACROS = YES
|
||||
# where loc1 and loc2 can be relative or absolute paths or URLs. See the
|
||||
# section "Linking to external documentation" for more information about the use
|
||||
# of tag files.
|
||||
# Note: Each tag file must have an unique name (where the name does NOT include
|
||||
# Note: Each tag file must have a unique name (where the name does NOT include
|
||||
# the path). If a tag file is not located in the directory in which doxygen is
|
||||
# run, you must also specify the path to the tagfile here.
|
||||
|
||||
TAGFILES =
|
||||
TAGFILES = "libstdc++.tag = http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen"
|
||||
|
||||
# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
|
||||
# tag file that is based on the input files it reads. See section "Linking to
|
||||
# external documentation" for more information about the usage of tag files.
|
||||
|
||||
GENERATE_TAGFILE =
|
||||
GENERATE_TAGFILE = pvdata.tag
|
||||
|
||||
# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
|
||||
# class index. If set to NO only the inherited external classes will be listed.
|
||||
@@ -1992,14 +2045,14 @@ ALLEXTERNALS = NO
|
||||
# listed.
|
||||
# The default value is: YES.
|
||||
|
||||
EXTERNAL_GROUPS = YES
|
||||
EXTERNAL_GROUPS = NO
|
||||
|
||||
# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
|
||||
# the related pages index. If set to NO, only the current project's pages will
|
||||
# be listed.
|
||||
# The default value is: YES.
|
||||
|
||||
EXTERNAL_PAGES = YES
|
||||
EXTERNAL_PAGES = NO
|
||||
|
||||
# The PERL_PATH should be the absolute path and name of the perl script
|
||||
# interpreter (i.e. the result of 'which perl').
|
||||
@@ -2027,14 +2080,14 @@ CLASS_DIAGRAMS = YES
|
||||
# the mscgen tool resides. If left empty the tool is assumed to be found in the
|
||||
# default search path.
|
||||
|
||||
MSCGEN_PATH =
|
||||
MSCGEN_PATH =
|
||||
|
||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||
# then run dia to produce the diagram and insert it in the documentation. The
|
||||
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||
# If left empty dia is assumed to be found in the default search path.
|
||||
|
||||
DIA_PATH =
|
||||
DIA_PATH =
|
||||
|
||||
# If set to YES, the inheritance and collaboration graphs will hide inheritance
|
||||
# and usage relations if the target is undocumented or is not a class.
|
||||
@@ -2047,9 +2100,9 @@ HIDE_UNDOC_RELATIONS = YES
|
||||
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
|
||||
# Bell Labs. The other options in this section have no effect if this option is
|
||||
# set to NO
|
||||
# The default value is: NO.
|
||||
# The default value is: YES.
|
||||
|
||||
HAVE_DOT = NO
|
||||
HAVE_DOT = YES
|
||||
|
||||
# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
|
||||
# to run in parallel. When set to 0 doxygen will base this on the number of
|
||||
@@ -2061,7 +2114,7 @@ HAVE_DOT = NO
|
||||
|
||||
DOT_NUM_THREADS = 0
|
||||
|
||||
# When you want a differently looking font n the dot files that doxygen
|
||||
# When you want a differently looking font in the dot files that doxygen
|
||||
# generates you can specify the font name using DOT_FONTNAME. You need to make
|
||||
# sure dot is able to find the font, which can be done by putting it in a
|
||||
# standard location or by setting the DOTFONTPATH environment variable or by
|
||||
@@ -2083,7 +2136,7 @@ DOT_FONTSIZE = 10
|
||||
# the path where dot can find it using this tag.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_FONTPATH =
|
||||
DOT_FONTPATH =
|
||||
|
||||
# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
|
||||
# each documented class showing the direct and indirect inheritance relations.
|
||||
@@ -2199,7 +2252,9 @@ DIRECTORY_GRAPH = YES
|
||||
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
|
||||
# to make the SVG files visible in IE 9+ (other browsers do not have this
|
||||
# requirement).
|
||||
# Possible values are: png, jpg, gif and svg.
|
||||
# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
|
||||
# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
|
||||
# gif:cairo:gd, gif:gd, gif:gd:gd and svg.
|
||||
# The default value is: png.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
@@ -2221,26 +2276,35 @@ INTERACTIVE_SVG = NO
|
||||
# found. If left blank, it is assumed the dot tool can be found in the path.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_PATH =
|
||||
DOT_PATH =
|
||||
|
||||
# The DOTFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain dot files that are included in the documentation (see the \dotfile
|
||||
# command).
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOTFILE_DIRS =
|
||||
DOTFILE_DIRS =
|
||||
|
||||
# The MSCFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain msc files that are included in the documentation (see the \mscfile
|
||||
# command).
|
||||
|
||||
MSCFILE_DIRS =
|
||||
MSCFILE_DIRS =
|
||||
|
||||
# The DIAFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain dia files that are included in the documentation (see the \diafile
|
||||
# command).
|
||||
|
||||
DIAFILE_DIRS =
|
||||
DIAFILE_DIRS =
|
||||
|
||||
# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
|
||||
# path where java can find the plantuml.jar file. If left blank, it is assumed
|
||||
# PlantUML is not used or called during a preprocessing step. Doxygen will
|
||||
# generate a warning when it encounters a \startuml command in this case and
|
||||
# will not generate output for the diagram.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
PLANTUML_JAR_PATH =
|
||||
|
||||
# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
|
||||
# that will be shown in the graph. If the number of nodes in a graph becomes
|
||||
|
||||
3
Makefile
3
Makefile
@@ -11,4 +11,7 @@ src_DEPEND_DIRS = configure
|
||||
DIRS += testApp
|
||||
testApp_DEPEND_DIRS = src
|
||||
|
||||
DIRS += examples
|
||||
examples_DEPEND_DIRS = src
|
||||
|
||||
include $(TOP)/configure/RULES_TOP
|
||||
|
||||
61
README.md
61
README.md
@@ -1,57 +1,16 @@
|
||||
pvaDataCPP
|
||||
==========
|
||||
# pvaDataCPP
|
||||
|
||||
pvDataCPP is a set of data types and utilities that form part of the EPICS V4 project.
|
||||
The EPICS **pvData** API provides a set of classes and utilities that form the core of the EPICS PVA implementation.
|
||||
|
||||
The pvDataCPP module is a part of the EPICS software toolkit that implements pvData structures as C++ class objects.
|
||||
|
||||
Further Info
|
||||
------------
|
||||
## Links
|
||||
|
||||
Consult the documents in the documentation directory, in particular
|
||||
|
||||
* pvDataCPP.html
|
||||
* RELEASE_NOTES.md
|
||||
|
||||
Also see the [EPICS Version 4 website](http://epics-pvdata.sourceforge.net)
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
The pvDataCPP requires recent versions of the following software:
|
||||
|
||||
1. EPICS Base (v3.14.12.3 or later)
|
||||
2. EPICS4 pvCommonCPP (4.1.0 or later)
|
||||
|
||||
(pvCommonCPP may not be needed depending on host/compiler.)
|
||||
|
||||
|
||||
Building
|
||||
--------
|
||||
|
||||
Building uses the make utility and the EPICS base build system.
|
||||
|
||||
The build system needs the location of the prerequisites, e.g. by placing the
|
||||
lines of the form
|
||||
|
||||
PVCOMMON = /home/install/epicsV4/pvCommonCPP
|
||||
EPICS_BASE = /home/install/epics/base
|
||||
|
||||
pointing to the locations in a file called RELEASE.local
|
||||
in the configure directory or the parent directory of pvDataCPP.
|
||||
|
||||
With this in place, to build type make
|
||||
|
||||
make
|
||||
|
||||
To perform a clean build type
|
||||
|
||||
make clean uninstall
|
||||
|
||||
To run the unit tests type
|
||||
|
||||
make runtests
|
||||
|
||||
For more information on the EPICS build system consult the
|
||||
[Application Development guide](http://www.aps.anl.gov/epics/base/R3-14/12-docs/AppDevGuide.pdf).
|
||||
- General information about EPICS can be found at the
|
||||
[EPICS Controls website](https://epics-controls.org).
|
||||
- API documentation for this module can be found on its
|
||||
[Github Pages website](https://epics-base.github.io/pvDataCPP/).
|
||||
|
||||
## Building
|
||||
|
||||
This module is included as a submodule of a full EPICS 7 release and will be compiled during builds of that software.
|
||||
|
||||
12
configure/CONFIG_PVDATA_VERSION
Normal file
12
configure/CONFIG_PVDATA_VERSION
Normal file
@@ -0,0 +1,12 @@
|
||||
# Version number for the PV Data API and shared library
|
||||
|
||||
EPICS_PVD_MAJOR_VERSION = 8
|
||||
EPICS_PVD_MINOR_VERSION = 0
|
||||
EPICS_PVD_MAINTENANCE_VERSION = 3
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
EPICS_PVD_DEVELOPMENT_FLAG = 0
|
||||
|
||||
# Immediately after a release the MAINTENANCE_VERSION
|
||||
# will be incremented and the DEVELOPMENT_FLAG set to 1
|
||||
@@ -20,9 +20,14 @@ CHECK_RELEASE = YES
|
||||
# INSTALL_LOCATION here.
|
||||
#INSTALL_LOCATION=</path/name/to/install/top>
|
||||
|
||||
USR_CPPFLAGS += -DPVD_INTERNAL
|
||||
|
||||
-include $(TOP)/../CONFIG_SITE.local
|
||||
-include $(TOP)/configure/CONFIG_SITE.local
|
||||
|
||||
# MSVC - skip defining min()/max() macros
|
||||
USR_CPPFLAGS_WIN32 += -DNOMINMAX
|
||||
|
||||
ifdef WITH_COVERAGE
|
||||
USR_CPPFLAGS += --coverage
|
||||
USR_LDFLAGS += --coverage
|
||||
|
||||
@@ -2,6 +2,8 @@ TOP=..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
CFG += CONFIG_PVDATA_VERSION
|
||||
|
||||
TARGETS = $(CONFIG_TARGETS)
|
||||
CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS)))
|
||||
|
||||
|
||||
@@ -1,25 +1,38 @@
|
||||
# pvDataCPP RELEASE - Location of external support modules
|
||||
# RELEASE - Location of external support modules
|
||||
#
|
||||
# IF YOU CHANGE this file or any file it includes you must
|
||||
# subsequently do a "gnumake rebuild" in the application's
|
||||
# top level directory.
|
||||
# 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.
|
||||
#
|
||||
# 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.
|
||||
# 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.
|
||||
#
|
||||
# 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!
|
||||
#
|
||||
# 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
|
||||
# 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.
|
||||
|
||||
# Variables and paths to dependent modules:
|
||||
#MODULES = /path/to/modules
|
||||
#MYMODULE = $(MODULES)/my-module
|
||||
|
||||
# If building the EPICS modules individually, set these:
|
||||
#EPICS_BASE = /path/to/base
|
||||
|
||||
# Set RULES here if you want to use build rules from elsewhere:
|
||||
#RULES = $(MODULES)/build-rules
|
||||
|
||||
# These allow developers to override the RELEASE variable settings
|
||||
# without having to modify the configure/RELEASE file itself.
|
||||
-include $(TOP)/../RELEASE.local
|
||||
-include $(TOP)/../RELEASE.$(EPICS_HOST_ARCH).local
|
||||
-include $(TOP)/configure/RELEASE.local
|
||||
|
||||
3
documentation/.gitignore
vendored
Normal file
3
documentation/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*.tag
|
||||
*.db
|
||||
html/
|
||||
File diff suppressed because it is too large
Load Diff
16
documentation/Makefile
Normal file
16
documentation/Makefile
Normal file
@@ -0,0 +1,16 @@
|
||||
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
|
||||
@@ -1,315 +0,0 @@
|
||||
<h1>Release 6.0.0</h1>
|
||||
|
||||
<p>The main changes since release 5.0.4 are:</p>
|
||||
|
||||
<ul>
|
||||
<li>Linux shared library version added</li>
|
||||
<li>Headers have been moved into pv directories</li>
|
||||
<li>Bitset functions declared const where possible</li>
|
||||
<li>Bitset::swap added</li>
|
||||
<li>Requester::message has default implementation</li>
|
||||
<li>Serialization/deserialization helpers added</li>
|
||||
<li>Non-template getSubField char* overload added</li>
|
||||
<li>MonitorPlugin deprecated</li>
|
||||
<li>Field name validation performed</li>
|
||||
<li>Now builds for Cygwin and MinGW targets</li>
|
||||
<li>Fix for debug build issue.</li>
|
||||
<li>New license file replaces LICENSE and COPYRIGHT</li>
|
||||
</ul>
|
||||
|
||||
<h2>Shared library version added</h2>
|
||||
|
||||
<p>Linux shared library version numbers have been added by setting SHRLIB_VERSION
|
||||
(to 6.0 in this case). So shared object will be libpvData.so.6.0 instead of
|
||||
libpvData.so.</p>
|
||||
|
||||
<h2>Headers have been moved into pv directories</h2>
|
||||
|
||||
<p>E.g. src/property/alarm.h -> src/property/pv/alarm.h</p>
|
||||
|
||||
<p>This facilitates using some IDEs such as Qt Creator.</p>
|
||||
|
||||
<h2>Requester::message has default implementation</h2>
|
||||
|
||||
<p>Requester::message is no longer pure virtual. Default implementation sends
|
||||
string to std::cerr.</p>
|
||||
|
||||
<h2>Serialization/deserialization helpers added</h2>
|
||||
|
||||
<p>A helper function, serializeToVector, has been added which serializes a
|
||||
Serializable object into a standard vector of UInt8s.</p>
|
||||
|
||||
<p>Similarly a function deserializeFromVector deserializes a standard vector into
|
||||
a Deserializable object.</p>
|
||||
|
||||
<p>A function deserializeFromBuffer deserializes a ByteBuffer into a
|
||||
Deserializable object.</p>
|
||||
|
||||
<h2>Field name validation performed</h2>
|
||||
|
||||
<p>On creating a Structure or Union the field names are now validated.</p>
|
||||
|
||||
<p>Valid characters for a field name are upper or lowercase letters, numbers and
|
||||
underscores and intial numbers are invalid, i.e. names must be of the form
|
||||
[A-Za-z<em>][A-Za-z0-9</em>]*.</p>
|
||||
|
||||
<h2>Now builds for Cygwin and MinGW targets</h2>
|
||||
|
||||
<p>Includes cross-compiling MinGW on Linux.</p>
|
||||
|
||||
<h1>Release 5.0.4</h1>
|
||||
|
||||
<p>The changes since release 5.0.3 are:</p>
|
||||
|
||||
<ul>
|
||||
<li>Fixed bitset serialization (issue #24)</li>
|
||||
<li>Fixed truncation in BitSet::or_and (issue #27)</li>
|
||||
</ul>
|
||||
|
||||
<h2>Fixed bitset serialization (issue #24)</h2>
|
||||
|
||||
<p>C++ bitset serialization was not consistent with the C++ deserialization and
|
||||
Java code in some instances (depending on the endianness of the serializer and
|
||||
deserializer) when the number of bits was 56-63 modulo 64. C++ serialization
|
||||
has been fixed.</p>
|
||||
|
||||
<p>Fix exposed issue in deserialization on 32-bit platforms which
|
||||
has also been corrected. </p>
|
||||
|
||||
<h2>Fixed truncation in BitSet::or_and (issue #27)</h2>
|
||||
|
||||
<p>If n, n1 and n2 words are used to store the values of the bitsets bitset,
|
||||
bitset1 and bitset2 respectively then max(n, min(n1,n2)) words are needed
|
||||
to store bitset.or_(bitset1, bitset2).</p>
|
||||
|
||||
<p>Previously min(n1,n2) words were used and the result would be truncated in
|
||||
some instances. This has been fixed.</p>
|
||||
|
||||
<h1>Release 5.0.3</h1>
|
||||
|
||||
<p>The only change since release 5.0.2 is:</p>
|
||||
|
||||
<h2>Fixed buffer overflow in PVUnion::serialize() (issue #20)</h2>
|
||||
|
||||
<p>A PVUnion whose stored value was null was serialized without checking
|
||||
whether the buffer had sufficient capacity. This has been fixed by calling
|
||||
ensureBuffer().</p>
|
||||
|
||||
<h1>Release 5.0.2</h1>
|
||||
|
||||
<p>The main changes since release 4.0.3 are:</p>
|
||||
|
||||
<ul>
|
||||
<li>Deprecated getXXXField() methods have been removed from PVStructure</li>
|
||||
<li>Convert copy methods and equals operators (re)moved</li>
|
||||
<li>Convert::copyUnion now always copies between subfields.</li>
|
||||
<li>New method getSubFieldT, like getSubField except it throws an exception</li>
|
||||
<li>findSubField method removed from PVStructure</li>
|
||||
<li>New stream operators for Field and PVField are provided</li>
|
||||
<li>New template versions of Structure::getField</li>
|
||||
<li>Fixes for static initialisation order issues</li>
|
||||
<li>CreateRequest prevents a possible SEGFAULT</li>
|
||||
</ul>
|
||||
|
||||
<h2>Deprecated getXXXField methods have been removed from PVStructure</h2>
|
||||
|
||||
<p>The following methods have been removed from PVStructure</p>
|
||||
|
||||
<ul>
|
||||
<li>getBooleanField</li>
|
||||
<li>getByteField, getShortField, getIntField, getLongField</li>
|
||||
<li>getUByteField, getUShortField, getUIntField, getULongField</li>
|
||||
<li>getStringField</li>
|
||||
<li>getStructureField, getUnionField</li>
|
||||
<li>getScalarArrayField, getStructureArrayField, getUnionArrayField</li>
|
||||
</ul>
|
||||
|
||||
<p>Use template getSubField instead, e.g. use</p>
|
||||
|
||||
<pre><code>getSubField< PVInt >(fieldName)
|
||||
</code></pre>
|
||||
|
||||
<p>in place of</p>
|
||||
|
||||
<pre><code>getIntField(fieldName)
|
||||
</code></pre>
|
||||
|
||||
<h2>Convert copy methods and equals operators</h2>
|
||||
|
||||
<p>Convert copy methods where moved and replaced with methods
|
||||
on PVField classes, i.e.</p>
|
||||
|
||||
<pre><code>PVField::copy(const PVField& from)
|
||||
</code></pre>
|
||||
|
||||
<p>Methods</p>
|
||||
|
||||
<pre><code>PVField::copyUnchecked(const PVField& from)
|
||||
</code></pre>
|
||||
|
||||
<p>were added to allow unchecked copies, to gain performance
|
||||
where checked are not needed (anymore).</p>
|
||||
|
||||
<p>In addition:
|
||||
- isCompatibleXXX methods were removed in favour of Field::operator==.
|
||||
- equals methods were remove in favour of PVField::operator==.
|
||||
- operator== methods where moved to pvIntrospect.h and pvData.h</p>
|
||||
|
||||
<h2>Convert::copyUnion</h2>
|
||||
|
||||
<p>Before this method, depending on types for to and from,
|
||||
sometimes did a shallow copy, i.e. just made to shared_ptr for to
|
||||
share the same data as from.
|
||||
Now it always copies between the subfield of to and from.</p>
|
||||
|
||||
<h2>New method getSubFieldT, like getSubField except it throws an exception</h2>
|
||||
|
||||
<p>PVStructure has a new template member</p>
|
||||
|
||||
<pre><code>getSubFieldT(std::string const &fieldName)
|
||||
</code></pre>
|
||||
|
||||
<p>that is like <b>getSubField</b> except that it throws a runtime_error
|
||||
instead of returning null.</p>
|
||||
|
||||
<h2>findSubField method removed from PVStructure</h2>
|
||||
|
||||
<p>This was mainly used in the implementation of getSubField. With a change to
|
||||
the latter, findSubField was removed.</p>
|
||||
|
||||
<h2>New stream operators</h2>
|
||||
|
||||
<p>New steam operators are available for Field and PVField.
|
||||
Before to print a Field (or any extension) or a PVField (or any extension)
|
||||
it was necessary to have code like:</p>
|
||||
|
||||
<pre><code> void print(StructureConstPtr struc, PVStructurePtr pv)
|
||||
{
|
||||
if(struc) {
|
||||
cout << *struc << endl;
|
||||
} else {
|
||||
cout << "nullptr\n"
|
||||
}
|
||||
if(pv) {
|
||||
cout << *.struc << endl;
|
||||
} else {
|
||||
cout << "nullptr\n"
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
<p>Now it can be done as follows:</p>
|
||||
|
||||
<pre><code> void print(StructureConstPtr struc, PVStructurePtr pv)
|
||||
{
|
||||
cout << struc << endl;
|
||||
cout << pv << endl;
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
<h2>New template version of Structure::getField</h2>
|
||||
|
||||
<p>A new template getField method has been added to Structure</p>
|
||||
|
||||
<p>template<typename FT >
|
||||
std::tr1::shared_ptr< const FT > getField(std::string const &fieldName) const </p>
|
||||
|
||||
<p>Can be used, for example, as follows:</p>
|
||||
|
||||
<pre><code>StructurePtr tsStruc = struc->getField<Structure>("timeStamp");
|
||||
</code></pre>
|
||||
|
||||
<h2>Fixes for static initialisation order issues</h2>
|
||||
|
||||
<p>Certain static builds (in particular Windows builds) of applications using
|
||||
pvData had issues due to PVStructure::DEFAULT_ID being used before being initialised. This has been fixed.</p>
|
||||
|
||||
<h2>CreateRequest change</h2>
|
||||
|
||||
<p>createRequest could cause a SEGFAULT if passed a bad argument.
|
||||
This has been changed so the it returns a null pvStructure
|
||||
and provides an error.</p>
|
||||
|
||||
<h1>Release 4.0.3</h1>
|
||||
|
||||
<p>The main changes since release 3.0.2 are:</p>
|
||||
|
||||
<ul>
|
||||
<li>array semantics now enforce Copy On Write.</li>
|
||||
<li>String no longer defined.</li>
|
||||
<li>timeStamp and valueAlarm name changes</li>
|
||||
<li>toString replaced by stream I/O </li>
|
||||
<li>union is new type.</li>
|
||||
<li>copy is new.</li>
|
||||
<li>monitorPlugin is new.</li>
|
||||
</ul>
|
||||
|
||||
<h2>New Semantics for Arrays</h2>
|
||||
|
||||
<p>PVScalarArray, PVStructureArray, and PVUnionArray all enforce COW (Copy On Write) Semantics.
|
||||
In order to limit memory usage the storage for raw data is managed via a new shared<em>vector facility.
|
||||
This allows multiple instances of array data to use the shared raw data.
|
||||
COW is implemented via shared</em>vectors of const data, i. e. data that can not be modified.</p>
|
||||
|
||||
<h2>String no longer defined</h2>
|
||||
|
||||
<p>This is replaced by std::string.</p>
|
||||
|
||||
<h2>timeStamp and valueAlarm name changes</h2>
|
||||
|
||||
<p>In timeStamp nanoSeconds is changed to nanoseconds.</p>
|
||||
|
||||
<p>In valueAlarm hysteresis is changed to hysteresis</p>
|
||||
|
||||
<h2>toString replaced by stream I/O</h2>
|
||||
|
||||
<p>pvData.h and pvIntrospect no longer defines toString
|
||||
Instead they have stream support.
|
||||
pvIntrospect uses method dump and pvData uses dumpValue.
|
||||
For example:</p>
|
||||
|
||||
<pre><code> PVDoublePtr pvValue;
|
||||
String buffer;
|
||||
pvValue->toString(&buffer);
|
||||
cout << buffer << endl;
|
||||
buffer.clear();
|
||||
pvValue->getField()->toString(&buffer);
|
||||
cout << buffer << evdl;
|
||||
</code></pre>
|
||||
|
||||
<p>is replaced by</p>
|
||||
|
||||
<pre><code> PVDoublePtr pvValue;
|
||||
cout << *pvValue << endl
|
||||
cout << *pvValue->getField() << endl;
|
||||
</code></pre>
|
||||
|
||||
<h2>union is a new basic type.</h2>
|
||||
|
||||
<p>There are two new basic types: union_t and unionArray.</p>
|
||||
|
||||
<p>A union is like a structure that has a single subfield.
|
||||
There are two flavors:</p>
|
||||
|
||||
<ul>
|
||||
<li><b>variant union</b> The field can have any type.</li>
|
||||
<li><b>union</b> The field can any of specified set of types.</li>
|
||||
</ul>
|
||||
|
||||
<p>The field type can be dynamically changed.</p>
|
||||
|
||||
<h2>copy </h2>
|
||||
|
||||
<p>This consists of createRequest and pvCopy.
|
||||
createRequest was moved from pvAccess to here.
|
||||
pvCopy is moved from pvDatabaseCPP and now depends
|
||||
only on pvData, i.e. it no longer has any knowledge of PVRecord.</p>
|
||||
|
||||
<h2>monitorPlugin</h2>
|
||||
|
||||
<p>This is for is for use by code that implements pvAccess monitors.
|
||||
This is prototype and is subject to debate.</p>
|
||||
|
||||
<h1>Release 3.0.2</h1>
|
||||
|
||||
<p>This was the starting point for RELEASE_NOTES</p>
|
||||
@@ -12,9 +12,3 @@ 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.
|
||||
|
||||
|
||||
7
documentation/_static/css/custom.css
Normal file
7
documentation/_static/css/custom.css
Normal file
@@ -0,0 +1,7 @@
|
||||
.wy-side-nav-search {
|
||||
background-color: #18334B;
|
||||
}
|
||||
|
||||
.wy-side-nav-search input[type="text"] {
|
||||
border-color: #18334b;
|
||||
}
|
||||
45
documentation/commit-gh.sh
Executable file
45
documentation/commit-gh.sh
Executable file
@@ -0,0 +1,45 @@
|
||||
#!/bin/sh
|
||||
set -e -x
|
||||
# Usage: commit-gh <sub-directory-prefix> <files...>
|
||||
#
|
||||
# Creates a commit containing only the files in the sub-directory provided as an argument
|
||||
#
|
||||
# Does not disturb the working copy or index
|
||||
|
||||
prefix="$1"
|
||||
shift
|
||||
|
||||
# Commit to this branch
|
||||
BRANCH=refs/heads/gh-pages
|
||||
|
||||
# Use the main branch description as the gh-pages commit message
|
||||
MSG=`git describe --tags --always`
|
||||
|
||||
# Scratch space
|
||||
TDIR=`mktemp -d -p $PWD`
|
||||
|
||||
# Automatic cleanup of scratch space
|
||||
trap 'rm -rf $TDIR' INT TERM QUIT EXIT
|
||||
|
||||
export GIT_INDEX_FILE="$TDIR/index"
|
||||
|
||||
# Add listed files to a new (empty) index
|
||||
git update-index --add "$@"
|
||||
|
||||
# Write the index into the repo, get tree hash
|
||||
TREE=`git write-tree --prefix="$prefix"`
|
||||
|
||||
echo "TREE $TREE"
|
||||
git cat-file -p $TREE
|
||||
|
||||
# Create a commit with our new tree
|
||||
# Reference current branch head as parent (if any)
|
||||
CMT=`git commit-tree -m "$MSG" $TREE`
|
||||
|
||||
echo "COMMIT $CMT"
|
||||
git cat-file -p $CMT
|
||||
|
||||
# Update the branch with the new commit tree hash
|
||||
git update-ref $BRANCH $CMT
|
||||
|
||||
echo "Done"
|
||||
78
documentation/conf.py
Normal file
78
documentation/conf.py
Normal file
@@ -0,0 +1,78 @@
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file only contains a selection of the most common options. For a full
|
||||
# list see the documentation:
|
||||
# http://www.sphinx-doc.org/en/master/config
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'EPICS Documentation'
|
||||
copyright = '2019, EPICS Controls.'
|
||||
author = 'EPICS'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.intersphinx',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
# Intersphinx links to subprojects
|
||||
intersphinx_mapping = {
|
||||
'how-tos': ('https://docs.epics-controls.org/projects/how-tos/en/latest', None),
|
||||
}
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
html_css_files = [
|
||||
'css/custom.css',
|
||||
]
|
||||
|
||||
master_doc = 'index'
|
||||
|
||||
html_theme_options = {
|
||||
'logo_only': True,
|
||||
}
|
||||
html_logo = "images/EPICS_white_logo_v02.png"
|
||||
|
||||
html_extra_path = ['../html']
|
||||
|
||||
|
||||
# -- Run Doxygen ------------------------------------------------------------
|
||||
|
||||
import subprocess
|
||||
subprocess.call('cd ..; mkdir -p html/doxygen; doxygen', shell=True)
|
||||
@@ -1,679 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
|
||||
<title>EPICS pvDataCPP: copy and monitor</title>
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="http://epics-pvdata.sourceforge.net/base.css" />
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="http://epics-pvdata.sourceforge.net/epicsv4.css" />
|
||||
<style type="text/css">
|
||||
/*<![CDATA[*/
|
||||
.about { margin-left: 3em; margin-right: 3em; font-size: .83em}
|
||||
table { margin-left: auto; margin-right: auto }
|
||||
.diagram { text-align: center; margin: 2.5em 0 }
|
||||
span.opt { color: grey }
|
||||
span.nterm { font-style:italic }
|
||||
span.term { font-family:courier }
|
||||
span.user { font-family:courier }
|
||||
span.user:before { content:"<" }
|
||||
span.user:after { content:">" }
|
||||
.nonnorm { font-style:italic }
|
||||
p.ed { color: #AA0000 }
|
||||
span.ed { color: #AA0000 }
|
||||
p.ed.priv { display: inline; }
|
||||
span.ed.priv { display: inline; }
|
||||
/*]]>*/</style>
|
||||
<!-- Script that generates the Table of Contents -->
|
||||
<script type="text/javascript"
|
||||
src="http://epics-pvdata.sourceforge.net/script/tocgen.js">
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<div id="toc">
|
||||
<h2 class="nocount" style="page-break-before: always">Table of Contents</h2>
|
||||
</div>
|
||||
<div id="contents" class="contents">
|
||||
|
||||
<h2>support for copy and monitor</h2>
|
||||
<p><b>copy</b> and <b>monitor</b> are not used in this project.
|
||||
They are intended for use by pvAccess and by pvAccess servers.
|
||||
They are provided with this project because the code depends only on
|
||||
pvData itself.
|
||||
</p>
|
||||
<p>This document describes C++ specific code.
|
||||
<a href="http://epics-pvdata.sourceforge.net/informative/pvRequest.html">
|
||||
pvRequest.html</a>
|
||||
provides a language independent overview of <b>copy</b> and <b>monitor</b>.
|
||||
</p>
|
||||
<p>
|
||||
<b>NOTE:pvRequest.html</b> must be updated since it is based on an earlier version of pvCopy that
|
||||
had knowledge of PVRecord. The C++ version was implemented in pvDatabaseCPP
|
||||
and the Java version on pvIOCJava.
|
||||
At present only the C++ version of the new API for pvCopy is implemented.
|
||||
</p>
|
||||
<p>Copy provides:
|
||||
<dl>
|
||||
<dt>createRequest</dt>
|
||||
<dd>
|
||||
The Channel create methods in pvAccess all have an argument
|
||||
<b>PVStructure pvRequest</b>.<br />
|
||||
Given an ascii string createRequest creates a PVStructure that provides
|
||||
a pvData representation of the information from the ascii string.
|
||||
It is this structure that can be passed to the channel create methods.<br />
|
||||
The information in a pvRequest selects an arbitrary subset of the
|
||||
fields in a top level structure that resides in the server.
|
||||
In addition options can be specified. Both global and field specific
|
||||
options can be specified.
|
||||
</dd>
|
||||
<dt>pvCopy</dt>
|
||||
<dd>This is a facility used by channel providers.
|
||||
It provides client specific code that manages a copy of an arbitrary
|
||||
subset of the fields in a top level structure that resides in the
|
||||
provider. It also allows provider access to options specified
|
||||
by the client.
|
||||
</dd>
|
||||
</dl>
|
||||
Monitor provides:
|
||||
<dl>
|
||||
<dt>monitor</dt>
|
||||
<dd>This is support code for channel providers that implement channel
|
||||
monitor. It, together with the queue facility, provides support for
|
||||
monitor queues.
|
||||
</dd>
|
||||
<dt>monitorPlugin</dt>
|
||||
<dd>This is support for implementing monitor plugins.
|
||||
A monitor plugin can be developed that has no knowledge
|
||||
of pvAccess but only pvData.
|
||||
</dd>
|
||||
</dl>
|
||||
</p>
|
||||
|
||||
<h2>support for copy</h2>
|
||||
<p><b>copy</b> provides the ability to create a structure that has
|
||||
a copy of an arbitrary subset of the fields in an existing top level
|
||||
structure. In addition it allows global options and field specific options.
|
||||
It has two main components: <b>createRequest</b> and <b>pvCopy</b>.
|
||||
Given a string createRequest creates a pvRequest, which is a PVStructure
|
||||
that has the format expected by <b>pvCopy</b>.
|
||||
</p>
|
||||
|
||||
<h3>createRequest</h3>
|
||||
<p>This is mainly used by pvAccess clients. Given a request string it creates
|
||||
a pvRequest structure that can be passed to the pvAccess create methods.
|
||||
In turn pvAccess passes the pvRequest to a local channel provider which
|
||||
then passes it to pvCopy.
|
||||
</p>
|
||||
<p>The definition of the public members is:</p>
|
||||
<pre>
|
||||
class CreateRequest {
|
||||
...
|
||||
static CreateRequestPtr create();
|
||||
virtual PVStructurePtr createRequest(std::string const &request);
|
||||
std::string getMessage();
|
||||
};
|
||||
</pre>
|
||||
<p>An example of how it is used is:</p>
|
||||
<pre>
|
||||
CreateRequestPtr createRequest = CreateRequest::create();
|
||||
PVStructurePtr pvRequest = createRequest->createRequest(request);
|
||||
if(pvRequest==NULL) {
|
||||
std::string error = createRequest->getMessage();
|
||||
// take some action
|
||||
} else {
|
||||
//success do something
|
||||
}
|
||||
</pre>
|
||||
<h3>pvCopy</h3>
|
||||
<p>The definition of the public members is:</p>
|
||||
<pre>
|
||||
class epicsShareClass PVCopyTraverseMasterCallback
|
||||
{
|
||||
...
|
||||
virtual void nextMasterPVField(PVFieldPtr const &pvField);
|
||||
};
|
||||
|
||||
class class epicsShareClass PVCopy
|
||||
{
|
||||
...
|
||||
static PVCopyPtr create(
|
||||
PVStructurePtr const &pvMaster,
|
||||
PVStructurePtr const &pvRequest,
|
||||
std::string const & structureName);
|
||||
PVStructurePtr getPVMaster();
|
||||
void traverseMaster(PVCopyTraverseMasterCallbackPtr const & callback);
|
||||
StructureConstPtr getStructure();
|
||||
PVStructurePtr createPVStructure();
|
||||
size_t getCopyOffset(PVFieldPtr const &masterPVField);
|
||||
size_t getCopyOffset(
|
||||
PVStructurePtr const &masterPVStructure,
|
||||
PVFieldPtr const &masterPVField);
|
||||
PVFieldPtr getMasterPVField(std::size_t structureOffset);
|
||||
void initCopy(
|
||||
PVStructurePtr const &copyPVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
void updateCopySetBitSet(
|
||||
PVStructurePtr const &copyPVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
void updateCopyFromBitSet(
|
||||
PVStructurePtr const &copyPVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
void updateMaster(
|
||||
PVStructurePtr const &copyPVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
PVStructurePtr getOptions(std::size_t fieldOffset);
|
||||
...
|
||||
};
|
||||
</pre>
|
||||
where
|
||||
<dl>
|
||||
<dt>PVCopyTraverseMasterCallback::nextMasterPVField</dt>
|
||||
<dd>
|
||||
<b>PVCopyTraverseMasterCallback</b> is a callback which must
|
||||
be implemented by the code that uses pvCopy, normally
|
||||
the channel provider. It has the single method <b>nextMasterPVField</b>
|
||||
<br />
|
||||
<b>nextMasterPVField</b> is called for each field in the master
|
||||
as a result of a call to <b>traverseMaster</b>.
|
||||
</dd>
|
||||
<dt>create</dt>
|
||||
<dd>
|
||||
This is the method for creating a PVCopy instance.<br/>
|
||||
<dl>
|
||||
<dt>pvMaster</dt>
|
||||
<dd>the top level structure managed by the server.</dd>
|
||||
<dt>pvRequest</dt>
|
||||
<dd>selects the set of subfields desired
|
||||
and options for each field.</dd>
|
||||
<dt>structureName</dt>
|
||||
<dd>the name for the top level of any PVStructure created.
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt>getPVMaster</dt>
|
||||
<dd>
|
||||
Gets the top level structure from pvMaster.
|
||||
</dd>
|
||||
<dt>traverseMaster</dt>
|
||||
<dd>
|
||||
Traverse all fields of the top level structure of pvMaster.
|
||||
For each field the callback is called.
|
||||
</dd>
|
||||
<dt>getStructure</dt>
|
||||
<dd>
|
||||
Get the introspection interface for a PVStructure for e copy.
|
||||
</dd>
|
||||
<dt>createPVStructure</dt>
|
||||
<dd>Create a copy instance.
|
||||
Monitors keep a queue of monitor elements.
|
||||
Since each element needs a PVStructure, multiple top level structures
|
||||
will be created.
|
||||
</dd>
|
||||
<dt>getCopyOffset</dt>
|
||||
<dd>Given a field in pvMaster.
|
||||
return the offset in copy for the same field.
|
||||
A value of std::string::npos means that the copy does not have this field.
|
||||
Two overloaded methods are provided. The first is called if
|
||||
the field of master is not a structure. The second is for
|
||||
subfields of a structure.
|
||||
</dd>
|
||||
<dt>getMasterPVField</dt>
|
||||
<dd>
|
||||
Given a offset in the copy get the corresponding field in pvMaster.
|
||||
</dd>
|
||||
<dt>initCopy</dt>
|
||||
<dd>
|
||||
Initialize the fields in copyPVStructure
|
||||
by giving each field the value from the corresponding field in pvMaster.
|
||||
bitSet will be set to show that all fields are changed.
|
||||
This means that bit set will have the value <b>{0}</b>.
|
||||
</dd>
|
||||
<dt>updateCopySetBitSet</dt>
|
||||
<dd>
|
||||
Set all fields in copyPVStructure to the value of the corresponding field
|
||||
in pvMaster. Each field that is changed has it's corresponding
|
||||
bit set in bitSet.
|
||||
</dd>
|
||||
<dt>updateCopyFromBitSet</dt>
|
||||
<dd>
|
||||
For each set bit in bitSet set the field in copyPVStructure to the value
|
||||
of the corresponding field in pvMaster.
|
||||
</dd>
|
||||
<dt>updateMaster</dt>
|
||||
<dd>
|
||||
For each set bit in bitSet set the field in pvMaster to the value
|
||||
of the corresponding field in copyPVStructure.
|
||||
|
||||
</dd>
|
||||
<dt>getOptions</dt>
|
||||
<dd>
|
||||
Get the options for the field at the specified offset.
|
||||
A NULL is returned if no options were specified for the field.
|
||||
If options were specified,PVStructurePtr is
|
||||
a structure with a set of PVString subfields that specify name,value
|
||||
pairs. name is the subField name and value is the subField value.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
<h2>support for monitor</h2>
|
||||
<p>This consists of two components:
|
||||
<dl>
|
||||
<dt>monitor</dt>
|
||||
<dd>Used by code that implements pvAccess monitors.</dd>
|
||||
<dt>monitorPlugin</dt>
|
||||
<dd>Code that provides special semantics for monitors.</dd>
|
||||
</dl>
|
||||
</p>
|
||||
<h3>monitor</h3>
|
||||
<pre>
|
||||
class MonitorElement {
|
||||
MonitorElement(PVStructurePtr const & pvStructurePtr);
|
||||
PVStructurePtr pvStructurePtr;
|
||||
BitSetPtr changedBitSet;
|
||||
BitSetPtr overrunBitSet;
|
||||
};
|
||||
|
||||
class Monitor {
|
||||
virtual Status start() = 0;
|
||||
virtual Status stop() = 0;
|
||||
virtual MonitorElementPtr poll() = 0;
|
||||
virtual void release(MonitorElementPtr const & monitorElement) = 0;
|
||||
};
|
||||
|
||||
class MonitorRequester : public virtual Requester {
|
||||
virtual void monitorConnect(Status const & status,
|
||||
MonitorPtr const & monitor, StructureConstPtr const & structure) = 0;
|
||||
virtual void monitorEvent(MonitorPtr const & monitor) = 0;
|
||||
virtual void unlisten(MonitorPtr const & monitor) = 0;
|
||||
};
|
||||
</pre>
|
||||
<h4>monitorElement</h4>
|
||||
<p><b>MonitorElement</b> holds the data for one element of a monitor queue.
|
||||
It has the fields:
|
||||
<dl>
|
||||
<dt>pvStructurePtr</dt>
|
||||
<dd>A top level structure with data values at the time the monitors occurs.</dd>
|
||||
<dt>changedBitSet</dt>
|
||||
<dd>Shows which fields have changed since the previous monitor.</dd>
|
||||
<dt>overrunBitSet</dt>
|
||||
<dd>Shows which fields have changed more han once since the previous monitor.</dd>
|
||||
</dl>
|
||||
</p>
|
||||
<h4>monitorElement queue</h4>
|
||||
<p>
|
||||
A queue of monitor elements must be implemented by any channel provider that implements
|
||||
<b>Channel::createMonitor</b>.
|
||||
For an example implementation look at pvDatabaseCPP.
|
||||
It has the following:
|
||||
<pre>
|
||||
typedef Queue<MonitorElement> MonitorElementQueue;
|
||||
typedef std::tr1::shared_ptr<MonitorElementQueue> MonitorElementQueuePtr;
|
||||
|
||||
class MultipleElementQueue :
|
||||
public ElementQueue
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(MultipleElementQueue);
|
||||
virtual ~MultipleElementQueue(){}
|
||||
MultipleElementQueue(
|
||||
MonitorLocalPtr const &monitorLocal,
|
||||
MonitorElementQueuePtr const &queue,
|
||||
size_t nfields);
|
||||
virtual void destroy(){}
|
||||
virtual Status start();
|
||||
virtual Status stop();
|
||||
virtual bool dataChanged();
|
||||
virtual MonitorElementPtr poll();
|
||||
virtual void release(MonitorElementPtr const &monitorElement);
|
||||
...
|
||||
};
|
||||
</pre>
|
||||
<h4>Monitor</h4>
|
||||
<p><b>Monitor</b> must be implemented by any channel provider that implements
|
||||
<b>Channel::createMonitor</b>.
|
||||
Remote PVAccess also implements Monitor on the client side.
|
||||
Note that each client has it's own queue that is not shared with other client.
|
||||
</p>
|
||||
<p>Monitor has the following methods:</p>
|
||||
<dl>
|
||||
<dt>start</dt>
|
||||
<dd>
|
||||
Start monitoring.
|
||||
This will result in a an initial monitor that has the current value
|
||||
of all fields.
|
||||
</dd>
|
||||
<dt>stop</dt>
|
||||
<dd>
|
||||
Stop monitoring.
|
||||
</dd>
|
||||
<dt>poll</dt>
|
||||
<dd>
|
||||
Called to get a monitor element.
|
||||
If no new elements are available then a null pointer is returned.
|
||||
</dd>
|
||||
<dt>release</dt>
|
||||
<dd>
|
||||
Release the monitor element.
|
||||
The caller owns the monitor element between the calls to poll and release.
|
||||
</dd>
|
||||
<dl>
|
||||
</dl>
|
||||
<h4>MonitorRequester</h4>
|
||||
<p>This must be implemented by a pvAccess client.
|
||||
It has the methods:</p>
|
||||
<dl>
|
||||
<dt>monitorConnect</dt>
|
||||
<dd>
|
||||
A monitor has either connected of disconnected.
|
||||
</dd>
|
||||
<dt>monitorEvent</dt>
|
||||
<dd>
|
||||
A new monitor element is available.
|
||||
</dd>
|
||||
<dt>unlisten</dt>
|
||||
<dd>
|
||||
The channel is going away. The client cam no longer access the monitor.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<h3>monitorPlugin</h3>
|
||||
<pre>
|
||||
class MonitorPlugin
|
||||
{
|
||||
virtual std::string const & getName() = 0;
|
||||
virtual bool causeMonitor(
|
||||
PVFieldPtr const &pvField,
|
||||
PVStructurePtr const &pvTop,
|
||||
MonitorElementPtr const &monitorElement) = 0;
|
||||
virtual void monitorDone(
|
||||
MonitorElementPtr const &monitorElement);
|
||||
virtual void startMonitoring();
|
||||
virtual void stopMonitoring();
|
||||
virtual void beginGroupPut();
|
||||
virtual void endGroupPut();
|
||||
};
|
||||
|
||||
class MonitorPluginCreator
|
||||
{
|
||||
virtual MonitorPluginPtr create(
|
||||
FieldConstPtr const &field,
|
||||
StructureConstPtr const &top,
|
||||
PVStructurePtr const &pvFieldOptions) = 0;
|
||||
virtual std::string const & getName() = 0;
|
||||
}
|
||||
|
||||
class MonitorPluginManager
|
||||
{
|
||||
static MonitorPluginManagerPtr get();
|
||||
bool addPlugin(
|
||||
std::string const &pluginName,
|
||||
MonitorPluginCreatorPtr const &creator);
|
||||
MonitorPluginCreatorPtr findPlugin(std::string const &pluginName);
|
||||
void showNames();
|
||||
};
|
||||
|
||||
</pre>
|
||||
<h4>MonitorPlugin</h4>
|
||||
<p><b>MonitorPlugin</b> must be implemented by the plugin implementation.
|
||||
It has methods:</p>
|
||||
<dl>
|
||||
<dt>getName</dt>
|
||||
<dd>Get the name of the plugin.</dd>
|
||||
<dt>causeMonitor</dt>
|
||||
<dd>
|
||||
Should the value of pvField cause a monitor to be raised.
|
||||
pvField and pvTop are fields in the top level structure
|
||||
being monitored. monitorElement has the top level structure
|
||||
for the copy</b>.
|
||||
The implementation should <b>not</b> modify the fields in the structure
|
||||
being monitored.
|
||||
Called with pvTop locked.
|
||||
</dd>
|
||||
<dt>monitorDone</dt>
|
||||
<dd>
|
||||
Called just before monitorElement will be given to client.
|
||||
The plugin can change the data values and bitSets in monitorElement.
|
||||
Called with pvTop unlocked.
|
||||
</dd>
|
||||
<dt>startMonitoring</dt>
|
||||
<dd>
|
||||
Monitoring is starting.
|
||||
</dd>
|
||||
<dt>stopMonitoring</dt>
|
||||
<dd>
|
||||
Monitoring is being stopped.
|
||||
</dd>
|
||||
<dt>beginGroupPut</dt>
|
||||
<dd>
|
||||
A set of puts is starting.
|
||||
Called with pvTop locked.
|
||||
</dd>
|
||||
<dt>endGroupPut</dt>
|
||||
<dd>
|
||||
The set of puts is complete.
|
||||
Called with pvTop locked.
|
||||
</dd>
|
||||
</dl>
|
||||
<h4>MonitorPluginCreator</h4>
|
||||
<p><b>MonitorPluginCreator</b> must also be implemented by the plugin implementation.
|
||||
It is called for each field instance that has options of the from
|
||||
<b>[plugin=name...]</b> where <b>name</b> is the name of the plugin.
|
||||
Note that a plugin instance will belong to a single client.
|
||||
It has methods:</p>
|
||||
<dl>
|
||||
<dt>getName</dt>
|
||||
<dd>Get the name of the plugin.</dd>
|
||||
<dt>create</dt>
|
||||
<dd>
|
||||
Create a new plugin instance.
|
||||
If the arguments are not compatible with the plugin a NULL shared pointer is
|
||||
returned.<br/>
|
||||
pvFieldOptions is
|
||||
a structure with a set of PVString subfields that specify <b>name,value</b>
|
||||
pairs. name is the subField name and value is the subField value.<br/>
|
||||
Note that a plugin will below to a single client.
|
||||
</dd>
|
||||
<dl>
|
||||
<h4>MonitorPluginManager</h4>
|
||||
<p><b>MonitorPluginManager</b> has the methods:</p>
|
||||
<dl>
|
||||
<dt>get</dt>
|
||||
<dd>
|
||||
MonitorPluginManager is a singleton.
|
||||
The first call to get will create the single instance.
|
||||
Further calls will return the single instance.
|
||||
</dd>
|
||||
<dt>addPlugin</dt>
|
||||
<dd>
|
||||
Add a new plugin.
|
||||
</dd>
|
||||
<dt>findPlugin</dt>
|
||||
<dd>
|
||||
Find a plugin. A NULL shared pointer is returned if it has not been added.
|
||||
</dd>
|
||||
<dt>showNames</dt>
|
||||
<dd>
|
||||
Show the names of all plugins that have been added.
|
||||
</dd>
|
||||
</dl>
|
||||
<p><b>NOTE:</b>
|
||||
Should the method <b>causeMonitor</b>
|
||||
have arguments <b>pvField</b> and <b>pvTop</b>
|
||||
be defined so that they can not be modified.
|
||||
This would be possible if the following was defined:
|
||||
<pre>
|
||||
typedef std::tr1::shared_ptr<const PVField> PVFieldConstPtr;
|
||||
typedef std::tr1::shared_ptr<const PVStructure> PVStructureConstPtr;
|
||||
</pre>
|
||||
then the definition for causeMonitor could be:
|
||||
<pre>
|
||||
virtual bool causeMonitor(
|
||||
PVFieldConstPtr const &pvField,
|
||||
PVStructureConstPtr const &pvTop,
|
||||
MonitorElementPtr const &monitorElement) = 0;
|
||||
</pre>
|
||||
But just adding these definitions is not sufficient.
|
||||
In addition all methods defined in pvDataCPP must be checked.
|
||||
In particular many of the methods in <b>Convert</b> must have
|
||||
their arguments modified.
|
||||
Big job.
|
||||
</p>
|
||||
<h2>monitorPlugin example</h2>
|
||||
<h3>Example Plugin Overview</h3>
|
||||
<p>This section describes an example plugin that:</p>
|
||||
<ul>
|
||||
<li>Only raises monitors when a field changes value.<br />
|
||||
If no plugin is provided
|
||||
the default is to raise a monitor when a put is issued to a field.</li>
|
||||
<li>Optionally a change will not raise a monitor.<br />
|
||||
The change will, however,
|
||||
appear if a put to another field raise a monitor.</li>
|
||||
</ul>
|
||||
<p>As an example assume that a channel provided by pvAccess has a top level structure
|
||||
that represents a power supply.</p>
|
||||
<pre>
|
||||
structure powerSupply
|
||||
structure alarm
|
||||
structure timeStamp
|
||||
structure power
|
||||
double value
|
||||
structure alarm
|
||||
structure display
|
||||
structure voltage
|
||||
double value
|
||||
structure alarm
|
||||
structure display
|
||||
structure current
|
||||
double value
|
||||
structure alarm
|
||||
structure display
|
||||
</pre>
|
||||
<p>A pvAccess client wants to create a monitor on the powerSupply as follows:
|
||||
The client wants a top level structure that looks like:
|
||||
<pre>
|
||||
structure powerSupply
|
||||
structure alarm
|
||||
structure timeStamp
|
||||
structure power
|
||||
double value
|
||||
structure voltage
|
||||
double value
|
||||
structure current
|
||||
double value
|
||||
</pre>
|
||||
In addition the client wants monitors to occur only when one of the monitored
|
||||
fields changes value but not just because a put occured.
|
||||
Also if only the timeStamp changes value then that should not cause a monitor.
|
||||
</p>
|
||||
<p>The example monitor plugin implements the semantics the
|
||||
client wants. It can be attached to any field via the following options:
|
||||
<pre>
|
||||
[plugin=onChange,raiseMonitor=value]
|
||||
</pre>
|
||||
This plugin will trigger a monitor for the field only if the field changes
|
||||
value. In addition <b>value</b> equals <b>false</b> means do not raise a monitor
|
||||
for changes to this field.
|
||||
But if a change to another field does cause a monitor the change to this field
|
||||
will be passed to the client.
|
||||
</p>
|
||||
<p>
|
||||
Assume that the client has already connected to the channel.
|
||||
The client can then issue the commands:</p>
|
||||
<pre>
|
||||
std::string request("field(alarm[plugin=onChange]");
|
||||
request += ",timeStamp[plugin=onChange,raiseMonitor=false]";
|
||||
request += ",power.value[plugin=onChange";
|
||||
request += ",voltage.value[plugin=onChange";
|
||||
request += ",current.value[plugin=onChange";
|
||||
|
||||
PVStructurePtr pvRequest = createRequest->createRequest(request);
|
||||
|
||||
MonitorPtr monitor = channel->createMonitor(monitorRequester,pvRequest);
|
||||
</pre>
|
||||
<h3>Example Plugin Code</h3>
|
||||
<p>The header file to create the example has the definition:</p>
|
||||
<pre>
|
||||
class ExampleMonitorPlugin{
|
||||
public:
|
||||
static void create();
|
||||
};
|
||||
</pre>
|
||||
<p>The implementation is:</p>
|
||||
<pre>
|
||||
class OnChangePlugin : public MonitorPlugin
|
||||
{
|
||||
public:
|
||||
virtual ~OnChangePlugin(){}
|
||||
OnChangePlugin() {}
|
||||
bool init(
|
||||
FieldConstPtr const &field,
|
||||
StructureConstPtr const &top,
|
||||
PVStructurePtr const &pvFieldOptions)
|
||||
{
|
||||
pvField = getPVDataCreate()->createPVField(field);
|
||||
raiseMonitor = true;
|
||||
if(pvFieldOptions!=NULL) {
|
||||
PVStringPtr pvString =
|
||||
pvFieldOptions->getSubField<PVString>("raiseMonitor");
|
||||
if(pvString!=NULL) {
|
||||
std::string value = pvString->get();
|
||||
if(value.compare("false")==0) raiseMonitor = false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
virtual std::string &getName(){return pluginName;}
|
||||
virtual bool causeMonitor(
|
||||
PVFieldPtr const &pvNew,
|
||||
PVStructurePtr const &pvTop,
|
||||
MonitorElementPtr const &monitorElement)
|
||||
{
|
||||
bool isSame = convert->equals(pvNew,pvField);
|
||||
if(isSame) return false;
|
||||
convert->copy(pvNew,pvField);
|
||||
return raiseMonitor;
|
||||
}
|
||||
private:
|
||||
PVFieldPtr pvField;
|
||||
bool raiseMonitor;
|
||||
};
|
||||
class OnChangePluginCreator : public MonitorPluginCreator
|
||||
{
|
||||
public:
|
||||
virtual std::string &getName(){return pluginName;}
|
||||
virtual MonitorPluginPtr create(
|
||||
FieldConstPtr const &field,
|
||||
StructureConstPtr const &top,
|
||||
PVStructurePtr const &pvFieldOptions)
|
||||
{
|
||||
OnChangePluginPtr plugin(new OnChangePlugin());
|
||||
bool result = plugin->init(field,top,pvFieldOptions);
|
||||
if(!result) return MonitorPluginPtr();
|
||||
return plugin;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void ExampleMonitorPlugin::create()
|
||||
{
|
||||
static OnChangePluginCreatorPtr plugin;
|
||||
static Mutex mutex;
|
||||
Lock xx(mutex);
|
||||
if(plugin==NULL) {
|
||||
plugin = OnChangePluginCreatorPtr(new OnChangePluginCreator());
|
||||
MonitorPluginManager::get()->addPlugin(pluginName,plugin);
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
BIN
documentation/images/EPICS_white_logo_v02.png
Normal file
BIN
documentation/images/EPICS_white_logo_v02.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
16
documentation/index.rst
Normal file
16
documentation/index.rst
Normal file
@@ -0,0 +1,16 @@
|
||||
pvData (C++) Library
|
||||
====================
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
EPICS Website <https://epics-controls.org>
|
||||
EPICS Documentation Home <https://docs.epics-controls.org>
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: pvDataCPP
|
||||
|
||||
Reference Manual and API Documentation <https://docs.epics-controls.org/projects/pvdata-cpp/en/latest/doxygen>
|
||||
Source Code Repository on GitHub <https://github.com/epics-base/pvDataCPP>
|
||||
58
documentation/mainpage.dox
Normal file
58
documentation/mainpage.dox
Normal file
@@ -0,0 +1,58 @@
|
||||
#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 */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,179 +0,0 @@
|
||||
pvDataCPP cookbook
|
||||
------------------
|
||||
|
||||
|
||||
Creating introspection data interfaces
|
||||
|
||||
// create a scalar
|
||||
getFieldCreate()->createScalar(pvDouble);
|
||||
|
||||
// create a scalar array
|
||||
getFieldCreate()->createScalarArray(pvDouble);
|
||||
|
||||
// create a structure
|
||||
getFieldCreate()->createFieldBuilder()->
|
||||
setId("enum_t")->
|
||||
add("index", pvDouble)->
|
||||
addArray("choices", pvString)->
|
||||
createStructure();
|
||||
|
||||
// create a structure (cntd.)
|
||||
StructureConstPtr enum_t =
|
||||
getFieldCreate()->createFieldBuilder()->
|
||||
setId("enum_t")->
|
||||
add("index", pvInt)->
|
||||
addArray("choices", pvString)->
|
||||
createStructure();
|
||||
|
||||
// create a structure (cntd.)
|
||||
StructureConstPtr ntEnum =
|
||||
getFieldCreate()->createFieldBuilder()->
|
||||
setId("epics:nt/NTEnum:1.0")->
|
||||
add("value", enum_t)->
|
||||
addNestedStructure("timeStamp")->
|
||||
setId("time_t")->
|
||||
add("secondsPastEpoch", pvLong)->
|
||||
add("nanoseconds", pvInt)->
|
||||
add("userTag", pvInt)->
|
||||
endNested()->
|
||||
createStructure();
|
||||
|
||||
// create an union == same as structure
|
||||
|
||||
---
|
||||
|
||||
Creating data containers
|
||||
|
||||
// create a scalar
|
||||
PVDouble::shared_pointer doubleValue = getPVDataCreate()->createPVScalar<PVDouble>();
|
||||
|
||||
// create a scalar array
|
||||
PVDoubleArray::shared_pointer doubleArrayValue = getPVDataCreate()->createPVScalarArray<PVDouble>();
|
||||
|
||||
// create a structure
|
||||
PVStructure::shared_pointer struct = getPVDataCreate()->createPVStructure(ntEnum);
|
||||
|
||||
// create an union
|
||||
PVUnion::shared_pointer pvUnion = getPVDataCreate()->createPVUnion(unionIF);
|
||||
|
||||
// create a structure array
|
||||
PVStructureArray::shared_pointer structArray = getPVDataCreate()->createPVStructureArray(ntEnum);
|
||||
|
||||
|
||||
// scalar usage
|
||||
PVInt::shared_pointer index = struct->getSubField<PVInt>("value.index");
|
||||
int32 ix = index->get();
|
||||
index->put(3);
|
||||
std::cout << *index << std::endl;
|
||||
|
||||
|
||||
// using <<=, >>= operators to get/set
|
||||
*doubleValue <<= 12.3;
|
||||
|
||||
double val;
|
||||
*doubleValue >>= val;
|
||||
|
||||
|
||||
|
||||
// array usage
|
||||
PVStringArray::shared_pointer choices = struct->getSubField<PVStringArray>("value.choices");
|
||||
|
||||
// use view() to access read-only data
|
||||
PVStringArray::const_svector data(choices->view());
|
||||
for (std::size_t i = 0; i < data.size(); i++)
|
||||
std::cout << data[i] << std::endl;
|
||||
|
||||
// use replace() to put new data
|
||||
PVStringArray::svector newdata;
|
||||
newdata.push_back("zero");
|
||||
newdata.push_back("one");
|
||||
newdata.push_back("two");
|
||||
choices->replace(freeze(newdata));
|
||||
|
||||
// (add more use-cases) here
|
||||
|
||||
// print entire array
|
||||
std::cout << *choices << std::endl;
|
||||
|
||||
// print elmenet at index == 1
|
||||
std::cout << format::array_at(1) << *choices << std::endl;
|
||||
|
||||
----
|
||||
|
||||
Union handling
|
||||
|
||||
|
||||
Union::const_shared_pointer punion =
|
||||
getFieldCreate()->createFieldBuilder()->
|
||||
add("doubleValue", pvDouble)->
|
||||
add("intValue", pvInt)->
|
||||
createUnion();
|
||||
|
||||
PVUnion::shared_pointer u = getPVDataCreate()->createPVUnion(punion);
|
||||
|
||||
// select and put
|
||||
// this create a new instance of PVDouble (everytime select() is called)
|
||||
PVDouble::shared_pointer doubleValue = u->select<PVDouble>("doubleValue");
|
||||
doubeValue->put(12);
|
||||
// select using index (and direct put)
|
||||
u->select<PVDouble>(0)->put(12);
|
||||
// select using existing PVField (PVUnion stores by-reference)
|
||||
u->set("doubleValue", doubleValue);
|
||||
|
||||
// get selected field name or index
|
||||
std::string selectedFN = u->getSelectedFieldName();
|
||||
int32 selectedIndex = u->getSelectedIndex();
|
||||
|
||||
// get currently selected (knowing it's PVDouble)
|
||||
PVDouble value = u->get<PVDouble>();
|
||||
|
||||
|
||||
|
||||
Variant Union handling
|
||||
|
||||
|
||||
PVUnion::shared_pointer any = getPVDataCreate()->createPVVariantUnion();
|
||||
|
||||
PVDouble::shared_pointer doubleValue = getPVDataCreate()->createPVScalar<PVDouble>();
|
||||
doubleValue->put(12.8);
|
||||
any->set(doubleValue);
|
||||
|
||||
PVDouble::shared_pointer doubleValue2 = any->get<PVDouble>();
|
||||
|
||||
// variant union work by-reference (pointers match also)
|
||||
// doubleValue.get() == doubleValue2.get()
|
||||
|
||||
|
||||
|
||||
------
|
||||
|
||||
Convert
|
||||
|
||||
|
||||
// convert to int
|
||||
int32 i = doubleValue->getAs<int32>();
|
||||
|
||||
// from int
|
||||
doubleValue->putFrom<int32>(i);
|
||||
|
||||
// from string
|
||||
doubleValue->putFrom<std::string>("12.3");
|
||||
|
||||
// from scalar field
|
||||
doubleValue->assign(pvScalar);
|
||||
|
||||
|
||||
|
||||
// convert to int array
|
||||
PVIntArray::const_svector intData;
|
||||
doubleArrayValue->getAs<int32>(intData);
|
||||
|
||||
// from string array
|
||||
PVStringArray::svector labels;
|
||||
labels.push_back("zero");
|
||||
labels.push_back("one");
|
||||
labels.push_back("two");
|
||||
doubleArrayValue->putFrom<std::string>(labels);
|
||||
|
||||
// from scalar array
|
||||
doubleArrayValue->assign(pvScalarArray);
|
||||
@@ -1,6 +1,125 @@
|
||||
Release 6.0.0
|
||||
/**
|
||||
|
||||
@page release_notes Release Notes
|
||||
|
||||
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 testApp/misc/testTypeCast.cpp print (u)int8 values as integers
|
||||
* Minor documentation updates
|
||||
|
||||
|
||||
Release 6.0.0 (Aug. 2016)
|
||||
=======================
|
||||
|
||||
The main changes since release 5.0.4 are:
|
||||
|
||||
* Linux shared library version added
|
||||
@@ -39,13 +158,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
|
||||
@@ -80,7 +199,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)
|
||||
----------------------------------------------
|
||||
@@ -101,38 +220,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
|
||||
=============
|
||||
Release 5.0.2 (Sep. 2015)
|
||||
=========================
|
||||
|
||||
The main changes since release 4.0.3 are:
|
||||
The main changes since release 4.0 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
|
||||
|
||||
@@ -180,7 +299,7 @@ PVStructure has a new template member
|
||||
|
||||
getSubFieldT(std::string const &fieldName)
|
||||
|
||||
that is like <b>getSubField</b> except that it throws a runtime_error
|
||||
that is like `getSubField()` except that it throws a runtime_error
|
||||
instead of returning null.
|
||||
|
||||
|
||||
@@ -226,8 +345,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:
|
||||
|
||||
@@ -254,13 +373,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
|
||||
--------
|
||||
@@ -316,8 +435,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:
|
||||
|
||||
* <b>variant union</b> The field can have any type.
|
||||
* <b>union</b> The field can any of specified set of types.
|
||||
- *variant union* The field can have any type.
|
||||
- *union* The field can any of specified set of types.
|
||||
|
||||
The field type can be dynamically changed.
|
||||
|
||||
@@ -338,3 +457,5 @@ This is prototype and is subject to debate.
|
||||
Release 3.0.2
|
||||
==========
|
||||
This was the starting point for RELEASE_NOTES
|
||||
|
||||
*/
|
||||
14
examples/Makefile
Normal file
14
examples/Makefile
Normal file
@@ -0,0 +1,14 @@
|
||||
# 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
|
||||
|
||||
|
||||
30
examples/unittest.cpp
Normal file
30
examples/unittest.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/* c++ unittest skeleton */
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <testMain.h>
|
||||
|
||||
#include <pv/pvUnitTest.h>
|
||||
#include <pv/epicsException.h>
|
||||
|
||||
namespace {
|
||||
void testCase1() {
|
||||
testEqual(1+1, 2);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
MAIN(testUnitTest)
|
||||
{
|
||||
testPlan(1);
|
||||
try {
|
||||
testCase1();
|
||||
}catch(std::exception& e){
|
||||
PRINT_EXCEPTION(e); // print stack trace if thrown with THROW_EXCEPTION()
|
||||
testAbort("Unhandled exception: %s", e.what());
|
||||
}
|
||||
return testDone();
|
||||
}
|
||||
@@ -21,23 +21,15 @@ installE4 () {
|
||||
local module=$1
|
||||
local branch=$2
|
||||
|
||||
# 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
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE}/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
|
||||
tar -xzf ${module}.CB-dist.tar.gz
|
||||
}
|
||||
|
||||
###########################################
|
||||
# Defaults for EPICS Base and MB
|
||||
# Defaults for EPICS Base
|
||||
|
||||
DEFAULT_BASE=3.15.4
|
||||
BASE=${BASE:-${DEFAULT_BASE}}
|
||||
MB=${MB:-"NO_MICROBENCH"}
|
||||
|
||||
###########################################
|
||||
# Fetch and unpack dependencies
|
||||
@@ -73,6 +65,6 @@ make distclean all
|
||||
make runtests
|
||||
|
||||
###########################################
|
||||
# Create distribution
|
||||
# Create cache
|
||||
|
||||
tar czf pvData.CB-dist.tar.gz lib include LICENSE
|
||||
tar czf pvData.CB-dist.tar.gz lib include cfg LICENSE
|
||||
|
||||
@@ -21,14 +21,7 @@ installE4 () {
|
||||
local module=$1
|
||||
local branch=$2
|
||||
|
||||
# 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
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE}/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
|
||||
tar -xzf ${module}.CB-dist.tar.gz
|
||||
}
|
||||
|
||||
@@ -38,7 +31,6 @@ installE4 () {
|
||||
BASE=3.15.4
|
||||
PUBLISH=${PUBLISH:-NO}
|
||||
BRANCH=${BRANCH:-master}
|
||||
MB=NO_MICROBENCH
|
||||
|
||||
###########################################
|
||||
# Fetch and unpack dependencies
|
||||
|
||||
16
src/Makefile
16
src/Makefile
@@ -11,14 +11,26 @@ include $(PVDATA_SRC)/factory/Makefile
|
||||
include $(PVDATA_SRC)/property/Makefile
|
||||
include $(PVDATA_SRC)/copy/Makefile
|
||||
include $(PVDATA_SRC)/pvMisc/Makefile
|
||||
include $(PVDATA_SRC)/monitor/Makefile
|
||||
include $(PVDATA_SRC)/json/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 ?= 6.0
|
||||
SHRLIB_VERSION = $(EPICS_PVD_MAJOR_VERSION).$(EPICS_PVD_MINOR_VERSION).$(EPICS_PVD_MAINTENANCE_VERSION)
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
# Can't use EXPAND as generated headers must appear
|
||||
# in O.Common, but EXPAND emits rules for O.$(T_A)
|
||||
../O.Common/pv/pvdVersionNum.h: ../pv/pvdVersionNum.h@
|
||||
$(MKDIR) $(COMMON_DIR)/pv
|
||||
$(EXPAND_TOOL) $(EXPANDFLAGS) $($@_EXPANDFLAGS) $< $@
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
SRC_DIRS += $(PVDATA_SRC)/copy
|
||||
|
||||
INC += pv/createRequest.h
|
||||
INC += pv/pvCopy.h
|
||||
|
||||
LIBSRCS += createRequest.cpp
|
||||
LIBSRCS += pvCopy.cpp
|
||||
LIBSRCS += requestmapper.cpp
|
||||
|
||||
@@ -22,15 +22,15 @@ using std::endl;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
namespace {
|
||||
using namespace epics::pvData;
|
||||
|
||||
static PVDataCreatePtr pvDataCreate = getPVDataCreate();
|
||||
static FieldCreatePtr fieldCreate = getFieldCreate();
|
||||
|
||||
class CreateRequestImpl : public CreateRequest {
|
||||
private:
|
||||
struct CreateRequestImpl {
|
||||
|
||||
struct Node
|
||||
struct Node
|
||||
{
|
||||
string name;
|
||||
vector<Node> nodes;
|
||||
@@ -54,13 +54,8 @@ private:
|
||||
string fullFieldName;
|
||||
|
||||
|
||||
public:
|
||||
CreateRequestImpl()
|
||||
{
|
||||
fullFieldName = "";
|
||||
}
|
||||
private:
|
||||
|
||||
CreateRequestImpl() {}
|
||||
|
||||
|
||||
void removeBlanks(string& str)
|
||||
{
|
||||
@@ -75,8 +70,7 @@ private:
|
||||
size_t openBrace = request.find('{', index+1);
|
||||
size_t closeBrace = request.find('}', index+1);
|
||||
if(openBrace == string::npos && closeBrace == string::npos){
|
||||
message = request + " mismatched {}";
|
||||
throw std::logic_error("message");
|
||||
throw std::runtime_error(request + " mismatched {}");
|
||||
}
|
||||
if (openBrace != string::npos && openBrace!=0) {
|
||||
if(openBrace<closeBrace) return findMatchingBrace(request,openBrace,numOpen+1);
|
||||
@@ -91,14 +85,12 @@ private:
|
||||
for(size_t i=index+1; i< request.size(); ++i) {
|
||||
if(request[i] == ']') {
|
||||
if(i==index+1) {
|
||||
message = request + " mismatched []";
|
||||
throw std::logic_error("message");
|
||||
throw std::runtime_error(request + " mismatched []");
|
||||
}
|
||||
return i;
|
||||
}
|
||||
}
|
||||
message = request + " missing ]";
|
||||
throw std::logic_error("message");
|
||||
throw std::runtime_error(request + " missing ]");
|
||||
}
|
||||
|
||||
size_t findEndField(string& request) {
|
||||
@@ -137,7 +129,7 @@ private:
|
||||
if(pos==string::npos) break;
|
||||
numValues++;
|
||||
index = pos +1;
|
||||
}
|
||||
}
|
||||
vector<string> valueList(numValues,"");
|
||||
index=0;
|
||||
for(size_t i=0; i<numValues; i++) {
|
||||
@@ -153,7 +145,7 @@ private:
|
||||
string const & request)
|
||||
{
|
||||
if(request.length()<=1) {
|
||||
throw std::logic_error("logic error empty options");
|
||||
throw std::runtime_error("logic error empty options");
|
||||
}
|
||||
vector<Node> top;
|
||||
vector<string> items = split(request);
|
||||
@@ -163,8 +155,7 @@ private:
|
||||
string item = items[j];
|
||||
size_t equals = item.find('=');
|
||||
if(equals==string::npos || equals==0) {
|
||||
message = item + " illegal option " + request;
|
||||
throw std::logic_error("message");
|
||||
throw std::runtime_error(item + " illegal option " + request);
|
||||
}
|
||||
top.push_back(Node(item.substr(0,equals)));
|
||||
string name = fullFieldName + "._options." + item.substr(0,equals);
|
||||
@@ -213,7 +204,7 @@ private:
|
||||
if(end==0) end = request.size();
|
||||
string name = request.substr(0,end);
|
||||
if(name.size()<1) {
|
||||
throw std::logic_error("null field name " + request);
|
||||
throw std::runtime_error("null field name " + request);
|
||||
}
|
||||
string saveFullName = fullFieldName;
|
||||
fullFieldName += "." + name;
|
||||
@@ -236,7 +227,7 @@ private:
|
||||
if(chr=='.') {
|
||||
request = request.substr(end+1);
|
||||
if(request.size()==string::npos || request.size()<1) {
|
||||
throw std::logic_error("null field name " + request);
|
||||
throw std::runtime_error("null field name " + request);
|
||||
}
|
||||
Node subNode(name);
|
||||
if(optionNode.name.size()>0) subNode.nodes.push_back(optionNode);
|
||||
@@ -257,11 +248,11 @@ private:
|
||||
if(chr=='{') {
|
||||
size_t endBrace = findEndField(request);
|
||||
if((end+1)>=(endBrace-1)) {
|
||||
throw std::logic_error("illegal syntax " + request);
|
||||
throw std::runtime_error("illegal syntax " + request);
|
||||
}
|
||||
string subRequest = request.substr(end+1,endBrace-1 -end -1);
|
||||
if(subRequest.size()<1) {
|
||||
throw std::logic_error("empty {} " + request);
|
||||
throw std::runtime_error("empty {} " + request);
|
||||
}
|
||||
Node subNode(name);
|
||||
if(optionNode.name.size()>0) subNode.nodes.push_back(optionNode);
|
||||
@@ -277,7 +268,7 @@ private:
|
||||
createSubNode(node,request);
|
||||
return;
|
||||
}
|
||||
throw std::logic_error("logic error");
|
||||
throw std::runtime_error("logic error");
|
||||
}
|
||||
|
||||
FieldConstPtr createSubStructure(vector<Node> & nodes)
|
||||
@@ -318,17 +309,16 @@ private:
|
||||
return structure;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
virtual PVStructurePtr createRequest(
|
||||
PVStructurePtr createRequest(
|
||||
string const & crequest)
|
||||
{
|
||||
try {
|
||||
{
|
||||
string request = crequest;
|
||||
if (!request.empty()) removeBlanks(request);
|
||||
if (request.empty())
|
||||
{
|
||||
return pvDataCreate->createPVStructure(fieldCreate->createStructure());
|
||||
return fieldCreate->createStructure()->build();
|
||||
}
|
||||
size_t offsetRecord = request.find("record[");
|
||||
size_t offsetField = request.find("field(");
|
||||
@@ -357,20 +347,17 @@ public:
|
||||
if(numParan!=0) {
|
||||
ostringstream oss;
|
||||
oss << "mismatched () " << numParan;
|
||||
message = oss.str();
|
||||
return PVStructurePtr();
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
if(numBrace!=0) {
|
||||
ostringstream oss;
|
||||
oss << "mismatched {} " << numBrace;
|
||||
message = oss.str();
|
||||
return PVStructurePtr();
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
if(numBracket!=0) {
|
||||
ostringstream oss;
|
||||
oss << "mismatched [] " << numBracket;
|
||||
message = oss.str();
|
||||
return PVStructurePtr();
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
vector<Node> top;
|
||||
try {
|
||||
@@ -379,9 +366,8 @@ public:
|
||||
size_t openBracket = request.find('[', offsetRecord);
|
||||
size_t closeBracket = request.find(']', openBracket);
|
||||
if(closeBracket==string::npos) {
|
||||
message = request.substr(offsetRecord) +
|
||||
"record[ does not have matching ]";
|
||||
return PVStructurePtr();
|
||||
throw std::runtime_error(request.substr(offsetRecord) +
|
||||
"record[ does not have matching ]");
|
||||
}
|
||||
if(closeBracket-openBracket > 3) {
|
||||
Node node("record");
|
||||
@@ -397,9 +383,8 @@ public:
|
||||
size_t openParan = request.find('(', offsetField);
|
||||
size_t closeParan = request.find(')', openParan);
|
||||
if(closeParan==string::npos) {
|
||||
message = request.substr(offsetField)
|
||||
+ " field( does not have matching )";
|
||||
return PVStructurePtr();
|
||||
throw std::runtime_error(request.substr(offsetField)
|
||||
+ " field( does not have matching )");
|
||||
}
|
||||
if(closeParan>openParan+1) {
|
||||
createSubNode(node,request.substr(openParan+1,closeParan-openParan-1));
|
||||
@@ -412,9 +397,8 @@ public:
|
||||
size_t openParan = request.find('(', offsetGetField);
|
||||
size_t closeParan = request.find(')', openParan);
|
||||
if(closeParan==string::npos) {
|
||||
message = request.substr(offsetField)
|
||||
+ " getField( does not have matching )";
|
||||
return PVStructurePtr();
|
||||
throw std::runtime_error(request.substr(offsetField)
|
||||
+ " getField( does not have matching )");
|
||||
}
|
||||
if(closeParan>openParan+1) {
|
||||
createSubNode(node,request.substr(openParan+1,closeParan-openParan-1));
|
||||
@@ -427,9 +411,8 @@ public:
|
||||
size_t openParan = request.find('(', offsetPutField);
|
||||
size_t closeParan = request.find(')', openParan);
|
||||
if(closeParan==string::npos) {
|
||||
message = request.substr(offsetField)
|
||||
+ " putField( does not have matching )";
|
||||
return PVStructurePtr();
|
||||
throw std::runtime_error(request.substr(offsetField)
|
||||
+ " putField( does not have matching )");
|
||||
}
|
||||
if(closeParan>openParan+1) {
|
||||
createSubNode(node,request.substr(openParan+1,closeParan-openParan-1));
|
||||
@@ -437,9 +420,7 @@ public:
|
||||
top.push_back(node);
|
||||
}
|
||||
} catch (std::exception &e) {
|
||||
string xxx = e.what();
|
||||
message = "while creating Structure exception " + xxx;
|
||||
return PVStructurePtr();
|
||||
throw std::runtime_error(std::string("while creating Structure exception ")+e.what());
|
||||
}
|
||||
size_t num = top.size();
|
||||
StringArray names(num);
|
||||
@@ -456,7 +437,7 @@ public:
|
||||
}
|
||||
StructureConstPtr structure = fieldCreate->createStructure(names, fields);
|
||||
if(!structure) throw std::invalid_argument("bad request " + crequest);
|
||||
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(structure);
|
||||
PVStructurePtr pvStructure = structure->build();
|
||||
for(size_t i=0; i<optionList.size(); ++i) {
|
||||
OptionPair pair = optionList[i];
|
||||
string name = pair.name;
|
||||
@@ -467,20 +448,38 @@ public:
|
||||
}
|
||||
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 CreateRequestImpl());
|
||||
CreateRequest::shared_pointer createRequest(new CreateRequest());
|
||||
return createRequest;
|
||||
}
|
||||
|
||||
}}
|
||||
PVStructure::shared_pointer CreateRequest::createRequest(std::string const & request)
|
||||
{
|
||||
message.clear();
|
||||
try {
|
||||
return ::createRequest(request);
|
||||
} catch(std::exception& e) {
|
||||
message = e.what();
|
||||
return PVStructure::shared_pointer();
|
||||
}
|
||||
}
|
||||
|
||||
PVStructure::shared_pointer createRequest(std::string const & request)
|
||||
{
|
||||
CreateRequestImpl I;
|
||||
return I.createRequest(request);
|
||||
}
|
||||
|
||||
|
||||
}} // namespace
|
||||
|
||||
@@ -8,14 +8,18 @@
|
||||
#define CREATEREQUEST_H
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/lock.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class BitSet;
|
||||
|
||||
/**
|
||||
* @brief Create pvRequest structure for Channel methods.
|
||||
*
|
||||
@@ -32,7 +36,7 @@ class epicsShareClass CreateRequest {
|
||||
* @returns A shared pointer to the new instance.
|
||||
*/
|
||||
static CreateRequest::shared_pointer create();
|
||||
virtual ~CreateRequest() {};
|
||||
~CreateRequest() {};
|
||||
/**
|
||||
* Create a request structure for the create calls in Channel.
|
||||
* See the package overview documentation for details.
|
||||
@@ -41,7 +45,7 @@ class epicsShareClass CreateRequest {
|
||||
* If a NULL PVStructure is returned then getMessage will return
|
||||
* the reason.
|
||||
*/
|
||||
virtual PVStructure::shared_pointer createRequest(std::string const & request) = 0;
|
||||
PVStructure::shared_pointer createRequest(std::string const & request);
|
||||
/**
|
||||
* Get the error message of createRequest returns NULL
|
||||
* return the error message
|
||||
@@ -52,6 +56,176 @@ 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!)
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
|
||||
@@ -1,232 +0,0 @@
|
||||
/* pvCopy.h */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author Marty Kraimer
|
||||
* @date 2013.04
|
||||
*/
|
||||
#ifndef PVCOPY_H
|
||||
#define PVCOPY_H
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
namespace epics { namespace pvData{
|
||||
|
||||
class PVCopyTraverseMasterCallback;
|
||||
typedef std::tr1::shared_ptr<PVCopyTraverseMasterCallback> PVCopyTraverseMasterCallbackPtr;
|
||||
|
||||
/**
|
||||
* @brief Callback for traversing master structure
|
||||
*
|
||||
* Must be implemented by code that creates pvCopy.
|
||||
*/
|
||||
class epicsShareClass PVCopyTraverseMasterCallback
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(PVCopyTraverseMasterCallback);
|
||||
virtual ~PVCopyTraverseMasterCallback() {}
|
||||
/**
|
||||
* Called once for each field in master.
|
||||
* @param pvField The field in master.
|
||||
*/
|
||||
virtual void nextMasterPVField(epics::pvData::PVFieldPtr const &pvField) = 0;
|
||||
};
|
||||
|
||||
|
||||
class PVCopy;
|
||||
typedef std::tr1::shared_ptr<PVCopy> PVCopyPtr;
|
||||
|
||||
struct CopyNode;
|
||||
typedef std::tr1::shared_ptr<CopyNode> CopyNodePtr;
|
||||
struct CopyMasterNode;
|
||||
typedef std::tr1::shared_ptr<CopyMasterNode> CopyMasterNodePtr;
|
||||
struct CopyStructureNode;
|
||||
typedef std::tr1::shared_ptr<CopyStructureNode> CopyStructureNodePtr;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Support for subset of fields in a pvStructure.
|
||||
*
|
||||
* Class that manages one or more PVStructures that holds an arbitrary subset of the fields
|
||||
* in another PVStructure called master.
|
||||
*/
|
||||
class epicsShareClass PVCopy :
|
||||
public std::tr1::enable_shared_from_this<PVCopy>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(PVCopy);
|
||||
/**
|
||||
* Create a new pvCopy
|
||||
* @param pvMaster The top-level structure for which a copy of
|
||||
* an arbitrary subset of the fields in master will be created and managed.
|
||||
* @param pvRequest Selects the set of subfields desired and options for each field.
|
||||
* @param structureName The name for the top level of any PVStructure created.
|
||||
*/
|
||||
static PVCopyPtr create(
|
||||
PVStructurePtr const &pvMaster,
|
||||
PVStructurePtr const &pvRequest,
|
||||
std::string const & structureName);
|
||||
virtual ~PVCopy(){}
|
||||
virtual void destroy();
|
||||
/**
|
||||
* Get the top-level structure of master
|
||||
* @returns The master top-level structure.
|
||||
* This should not be modified.
|
||||
*/
|
||||
PVStructurePtr getPVMaster();
|
||||
/**
|
||||
* Traverse all the fields in master.
|
||||
* @param callback This is called for each field on master.
|
||||
*/
|
||||
void traverseMaster(PVCopyTraverseMasterCallbackPtr const & callback)
|
||||
{
|
||||
traverseMaster(headNode,callback);
|
||||
}
|
||||
/**
|
||||
* Get the introspection interface for a PVStructure for e copy.
|
||||
*/
|
||||
StructureConstPtr getStructure();
|
||||
/**
|
||||
* Create a copy instance. Monitors keep a queue of monitor elements.
|
||||
* Since each element needs a PVStructure, multiple top-level structures will be created.
|
||||
*/
|
||||
PVStructurePtr createPVStructure();
|
||||
/**
|
||||
* Given a field in pvMaster. return the offset in copy for the same field.
|
||||
* A value of std::string::npos means that the copy does not have this field.
|
||||
* @param masterPVField The field in master.
|
||||
*/
|
||||
std::size_t getCopyOffset(PVFieldPtr const &masterPVField);
|
||||
/**
|
||||
* Given a field in pvMaster. return the offset in copy for the same field.
|
||||
* A value of std::string::npos means that the copy does not have this field.
|
||||
* @param masterPVStructure A structure in master that has masterPVField.
|
||||
* @param masterPVField The field in master.
|
||||
*/
|
||||
std::size_t getCopyOffset(
|
||||
PVStructurePtr const &masterPVStructure,
|
||||
PVFieldPtr const &masterPVField);
|
||||
/**
|
||||
* Given an offset in the copy get the corresponding field in pvMaster.
|
||||
* @param structureOffset The offset in the copy.
|
||||
*/
|
||||
PVFieldPtr getMasterPVField(std::size_t structureOffset);
|
||||
/**
|
||||
* Initialize the fields in copyPVStructure by giving each field
|
||||
* the value from the corresponding field in pvMaster.
|
||||
* bitSet will be set to show that all fields are changed.
|
||||
* @param copyPVStructure A copy top-level structure.
|
||||
* @param bitSet A bitSet for copyPVStructure.
|
||||
*/
|
||||
void initCopy(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
/**
|
||||
* Set all fields in copyPVStructure to the value of the corresponding field in pvMaster.
|
||||
* Each field that is changed has it's corresponding bit set in bitSet.
|
||||
* @param copyPVStructure A copy top-level structure.
|
||||
* @param bitSet A bitSet for copyPVStructure.
|
||||
*/
|
||||
void updateCopySetBitSet(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
/**
|
||||
* For each set bit in bitSet
|
||||
* set the field in copyPVStructure to the value of the corresponding field in pvMaster.
|
||||
* @param copyPVStructure A copy top-level structure.
|
||||
* @param bitSet A bitSet for copyPVStructure.
|
||||
*/
|
||||
void updateCopyFromBitSet(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
/**
|
||||
* For each set bit in bitSet
|
||||
* set the field in pvMaster to the value of the corresponding field in copyPVStructure
|
||||
* @param copyPVStructure A copy top-level structure.
|
||||
* @param bitSet A bitSet for copyPVStructure.
|
||||
*/
|
||||
void updateMaster(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
/**
|
||||
* Get the options for the field at the specified offset.
|
||||
* @param fieldOffset the offset in copy.
|
||||
* @returns A NULL is returned if no options were specified for the field.
|
||||
* If options were specified,PVStructurePtr is a structures
|
||||
* with a set of PVString subfields that specify name,value pairs.s
|
||||
* name is the subField name and value is the subField value.
|
||||
*/
|
||||
PVStructurePtr getOptions(std::size_t fieldOffset);
|
||||
/**
|
||||
* For debugging.
|
||||
*/
|
||||
std::string dump();
|
||||
private:
|
||||
void dump(
|
||||
std::string *builder,
|
||||
CopyNodePtr const &node,
|
||||
int indentLevel);
|
||||
PVCopyPtr getPtrSelf()
|
||||
{
|
||||
return shared_from_this();
|
||||
}
|
||||
void traverseMaster(CopyNodePtr const &node, PVCopyTraverseMasterCallbackPtr const & callback);
|
||||
|
||||
PVStructurePtr pvMaster;
|
||||
StructureConstPtr structure;
|
||||
CopyNodePtr headNode;
|
||||
PVStructurePtr cacheInitStructure;
|
||||
PVCopy(PVStructurePtr const &pvMaster);
|
||||
friend class PVCopyMonitor;
|
||||
bool init(PVStructurePtr const &pvRequest);
|
||||
std::string dump(
|
||||
std::string const &value,
|
||||
CopyNodePtr const &node,
|
||||
int indentLevel);
|
||||
StructureConstPtr createStructure(
|
||||
PVStructurePtr const &pvMaster,
|
||||
PVStructurePtr const &pvFromRequest);
|
||||
CopyNodePtr createStructureNodes(
|
||||
PVStructurePtr const &pvMasterStructure,
|
||||
PVStructurePtr const &pvFromRequest,
|
||||
PVStructurePtr const &pvFromField);
|
||||
void updateStructureNodeSetBitSet(
|
||||
PVStructurePtr const &pvCopy,
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
BitSetPtr const &bitSet);
|
||||
void updateSubFieldSetBitSet(
|
||||
PVFieldPtr const &pvCopy,
|
||||
PVFieldPtr const &pvMaster,
|
||||
BitSetPtr const &bitSet);
|
||||
void updateStructureNodeFromBitSet(
|
||||
PVStructurePtr const &pvCopy,
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
BitSetPtr const &bitSet,
|
||||
bool toCopy,
|
||||
bool doAll);
|
||||
void updateSubFieldFromBitSet(
|
||||
PVFieldPtr const &pvCopy,
|
||||
PVFieldPtr const &pvMasterField,
|
||||
BitSetPtr const &bitSet,
|
||||
bool toCopy,
|
||||
bool doAll);
|
||||
CopyMasterNodePtr getCopyOffset(
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
PVFieldPtr const &masterPVField);
|
||||
CopyMasterNodePtr getMasterNode(
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
std::size_t structureOffset);
|
||||
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* PVCOPY_H */
|
||||
@@ -1,656 +0,0 @@
|
||||
/* pvCopy.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author Marty Kraimer
|
||||
* @date 2013.04
|
||||
*/
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include <epicsThread.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
|
||||
#include <pv/thread.h>
|
||||
|
||||
#include <pv/pvCopy.h>
|
||||
|
||||
using std::tr1::static_pointer_cast;
|
||||
using std::tr1::dynamic_pointer_cast;
|
||||
using std::string;
|
||||
using std::size_t;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
/**
|
||||
* Convenience method for implementing dump.
|
||||
* It generates a newline and inserts blanks at the beginning of the newline.
|
||||
* @param builder The std::string * being constructed.
|
||||
* @param indentLevel Indent level, Each level is four spaces.
|
||||
*/
|
||||
static void newLine(string *buffer, int indentLevel)
|
||||
{
|
||||
*buffer += "\n";
|
||||
*buffer += string(indentLevel*4, ' ');
|
||||
}
|
||||
|
||||
static PVCopyPtr NULLPVCopy;
|
||||
static FieldConstPtr NULLField;
|
||||
static StructureConstPtr NULLStructure;
|
||||
static PVStructurePtr NULLPVStructure;
|
||||
static CopyNodePtr NULLCopyNode;
|
||||
static CopyMasterNodePtr NULLCopyMasterNode;
|
||||
|
||||
struct CopyNode {
|
||||
CopyNode()
|
||||
: isStructure(false),
|
||||
structureOffset(0),
|
||||
nfields(0)
|
||||
{}
|
||||
bool isStructure;
|
||||
size_t structureOffset; // In the copy
|
||||
size_t nfields;
|
||||
PVStructurePtr options;
|
||||
};
|
||||
|
||||
struct CopyMasterNode : public CopyNode{
|
||||
PVFieldPtr masterPVField;
|
||||
};
|
||||
|
||||
typedef std::vector<CopyNodePtr> CopyNodePtrArray;
|
||||
typedef std::tr1::shared_ptr<CopyNodePtrArray> CopyNodePtrArrayPtr;
|
||||
|
||||
struct CopyStructureNode : public CopyNode {
|
||||
CopyNodePtrArrayPtr nodes;
|
||||
};
|
||||
|
||||
PVCopyPtr PVCopy::create(
|
||||
PVStructurePtr const &pvMaster,
|
||||
PVStructurePtr const &pvRequest,
|
||||
string const & structureName)
|
||||
{
|
||||
PVStructurePtr pvStructure(pvRequest);
|
||||
if(structureName.size()>0) {
|
||||
if(pvRequest->getStructure()->getNumberFields()>0) {
|
||||
pvStructure = pvRequest->getSubField<PVStructure>(structureName);
|
||||
if(!pvStructure) return NULLPVCopy;
|
||||
}
|
||||
} else if(pvStructure->getSubField<PVStructure>("field")) {
|
||||
pvStructure = pvRequest->getSubField<PVStructure>("field");
|
||||
}
|
||||
PVCopyPtr pvCopy = PVCopyPtr(new PVCopy(pvMaster));
|
||||
bool result = pvCopy->init(pvStructure);
|
||||
if(!result) pvCopy.reset();
|
||||
return pvCopy;
|
||||
}
|
||||
|
||||
PVCopy::PVCopy(
|
||||
PVStructurePtr const &pvMaster)
|
||||
: pvMaster(pvMaster)
|
||||
{
|
||||
}
|
||||
|
||||
void PVCopy::destroy()
|
||||
{
|
||||
headNode.reset();
|
||||
}
|
||||
|
||||
PVStructurePtr PVCopy::getPVMaster()
|
||||
{
|
||||
return pvMaster;
|
||||
}
|
||||
|
||||
void PVCopy::traverseMaster(CopyNodePtr const &innode, PVCopyTraverseMasterCallbackPtr const & callback)
|
||||
{
|
||||
CopyNodePtr node = innode;
|
||||
if(!node->isStructure) {
|
||||
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(node);
|
||||
callback->nextMasterPVField(masterNode->masterPVField);
|
||||
return;
|
||||
}
|
||||
CopyStructureNodePtr structNode = static_pointer_cast<CopyStructureNode>(node);
|
||||
CopyNodePtrArrayPtr nodes = structNode->nodes;
|
||||
for(size_t i=0; i< nodes->size(); i++) {
|
||||
node = (*nodes)[i];
|
||||
traverseMaster(node,callback);
|
||||
}
|
||||
}
|
||||
|
||||
StructureConstPtr PVCopy::getStructure()
|
||||
{
|
||||
return structure;
|
||||
}
|
||||
|
||||
PVStructurePtr PVCopy::createPVStructure()
|
||||
{
|
||||
if(cacheInitStructure) {
|
||||
PVStructurePtr save = cacheInitStructure;
|
||||
cacheInitStructure.reset();
|
||||
return save;
|
||||
}
|
||||
PVStructurePtr pvStructure =
|
||||
getPVDataCreate()->createPVStructure(structure);
|
||||
return pvStructure;
|
||||
}
|
||||
|
||||
PVStructurePtr PVCopy::getOptions(std::size_t fieldOffset)
|
||||
{
|
||||
if(fieldOffset==0) return headNode->options;
|
||||
CopyNodePtr node = headNode;
|
||||
while(true) {
|
||||
if(!node->isStructure) {
|
||||
if(node->structureOffset==fieldOffset) return node->options;
|
||||
return NULLPVStructure;
|
||||
}
|
||||
CopyStructureNodePtr structNode = static_pointer_cast<CopyStructureNode>(node);
|
||||
CopyNodePtrArrayPtr nodes = structNode->nodes;
|
||||
boolean okToContinue = false;
|
||||
for(size_t i=0; i< nodes->size(); i++) {
|
||||
node = (*nodes)[i];
|
||||
size_t soff = node->structureOffset;
|
||||
if(fieldOffset>=soff && fieldOffset<soff+node->nfields) {
|
||||
if(fieldOffset==soff) return node->options;
|
||||
if(!node->isStructure) {
|
||||
return NULLPVStructure;
|
||||
}
|
||||
okToContinue = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(okToContinue) continue;
|
||||
throw std::invalid_argument("fieldOffset not valid");
|
||||
}
|
||||
}
|
||||
|
||||
size_t PVCopy::getCopyOffset(PVFieldPtr const &masterPVField)
|
||||
{
|
||||
if(masterPVField->getFieldOffset()==0) return 0;
|
||||
if(!headNode->isStructure) {
|
||||
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(headNode);
|
||||
if((masterNode->masterPVField.get())==masterPVField.get()) {
|
||||
return headNode->structureOffset;
|
||||
}
|
||||
PVStructure * parent = masterPVField->getParent();
|
||||
size_t offsetParent = parent->getFieldOffset();
|
||||
size_t off = masterPVField->getFieldOffset();
|
||||
size_t offdiff = off -offsetParent;
|
||||
if(offdiff<masterNode->nfields) return headNode->structureOffset + offdiff;
|
||||
return string::npos;
|
||||
}
|
||||
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
|
||||
CopyMasterNodePtr masterNode = getCopyOffset(node,masterPVField);
|
||||
if(masterNode) return masterNode->structureOffset;
|
||||
return string::npos;
|
||||
}
|
||||
|
||||
size_t PVCopy::getCopyOffset(
|
||||
PVStructurePtr const &masterPVStructure,
|
||||
PVFieldPtr const &masterPVField)
|
||||
{
|
||||
CopyMasterNodePtr masterNode;
|
||||
if(!headNode->isStructure) {
|
||||
masterNode = static_pointer_cast<CopyMasterNode>(headNode);
|
||||
if(masterNode->masterPVField.get()!=masterPVStructure.get()) return string::npos;
|
||||
} else {
|
||||
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
|
||||
masterNode = getCopyOffset(node,masterPVField);
|
||||
}
|
||||
if(!masterNode) return string::npos;
|
||||
size_t diff = masterPVField->getFieldOffset()
|
||||
- masterPVStructure->getFieldOffset();
|
||||
return masterNode->structureOffset + diff;
|
||||
}
|
||||
|
||||
PVFieldPtr PVCopy::getMasterPVField(size_t structureOffset)
|
||||
{
|
||||
CopyMasterNodePtr masterNode;
|
||||
if(!headNode->isStructure) {
|
||||
masterNode = static_pointer_cast<CopyMasterNode>(headNode);
|
||||
} else {
|
||||
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
|
||||
masterNode = getMasterNode(node,structureOffset);
|
||||
}
|
||||
if(!masterNode) {
|
||||
throw std::invalid_argument(
|
||||
"PVCopy::getMasterPVField: setstructureOffset not valid");
|
||||
}
|
||||
size_t diff = structureOffset - masterNode->structureOffset;
|
||||
PVFieldPtr pvMasterField = masterNode->masterPVField;
|
||||
if(diff==0) return pvMasterField;
|
||||
PVStructurePtr pvStructure
|
||||
= static_pointer_cast<PVStructure>(pvMasterField);
|
||||
return pvStructure->getSubField(
|
||||
pvMasterField->getFieldOffset() + diff);
|
||||
}
|
||||
|
||||
void PVCopy::initCopy(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet)
|
||||
{
|
||||
bitSet->clear();
|
||||
bitSet->set(0);
|
||||
updateCopyFromBitSet(copyPVStructure,bitSet);
|
||||
}
|
||||
|
||||
void PVCopy::updateCopySetBitSet(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet)
|
||||
{
|
||||
if(headNode->isStructure) {
|
||||
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
|
||||
updateStructureNodeSetBitSet(copyPVStructure,node,bitSet);
|
||||
} else {
|
||||
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(headNode);
|
||||
PVFieldPtr pvMasterField= masterNode->masterPVField;
|
||||
PVFieldPtr copyPVField = copyPVStructure;
|
||||
PVFieldPtr pvField = pvMasterField;
|
||||
if(pvField->getField()->getType()==epics::pvData::structure) {
|
||||
updateSubFieldSetBitSet(copyPVField,pvMasterField,bitSet);
|
||||
return;
|
||||
}
|
||||
bool isEqual = (*copyPVField == *pvField);
|
||||
if(!isEqual) {
|
||||
copyPVField->copyUnchecked(*pvField);
|
||||
bitSet->set(copyPVField->getFieldOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PVCopy::updateCopyFromBitSet(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet)
|
||||
{
|
||||
bool doAll = bitSet->get(0);
|
||||
if(headNode->isStructure) {
|
||||
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
|
||||
updateStructureNodeFromBitSet(copyPVStructure,node,bitSet,true,doAll);
|
||||
} else {
|
||||
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(headNode);
|
||||
updateSubFieldFromBitSet(copyPVStructure, masterNode->masterPVField,bitSet, true,doAll);
|
||||
}
|
||||
}
|
||||
|
||||
void PVCopy::updateMaster(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet)
|
||||
{
|
||||
bool doAll = bitSet->get(0);
|
||||
if(headNode->isStructure) {
|
||||
CopyStructureNodePtr node =
|
||||
static_pointer_cast<CopyStructureNode>(headNode);
|
||||
updateStructureNodeFromBitSet(
|
||||
copyPVStructure,node,bitSet,false,doAll);
|
||||
} else {
|
||||
CopyMasterNodePtr masterNode =
|
||||
static_pointer_cast<CopyMasterNode>(headNode);
|
||||
updateSubFieldFromBitSet( copyPVStructure,masterNode->masterPVField,bitSet,false,doAll);
|
||||
}
|
||||
}
|
||||
|
||||
string PVCopy::dump()
|
||||
{
|
||||
string builder;
|
||||
dump(&builder,headNode,0);
|
||||
return builder;
|
||||
}
|
||||
|
||||
void PVCopy::dump(string *builder,CopyNodePtr const &node,int indentLevel)
|
||||
{
|
||||
newLine(builder,indentLevel);
|
||||
std::stringstream ss;
|
||||
ss << (node->isStructure ? "structureNode" : "masterNode");
|
||||
ss << " structureOffset " << node->structureOffset;
|
||||
ss << " nfields " << node->nfields;
|
||||
*builder += ss.str();
|
||||
PVStructurePtr options = node->options;
|
||||
if(options) {
|
||||
newLine(builder,indentLevel +1);
|
||||
|
||||
// TODO !!! ugly
|
||||
std::ostringstream oss;
|
||||
oss << *options;
|
||||
*builder += oss.str();
|
||||
|
||||
newLine(builder,indentLevel);
|
||||
}
|
||||
if(!node->isStructure) {
|
||||
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(node);
|
||||
string name = masterNode->masterPVField->getFullName();
|
||||
*builder += " masterField " + name;
|
||||
return;
|
||||
}
|
||||
CopyStructureNodePtr structureNode =
|
||||
static_pointer_cast<CopyStructureNode>(node);
|
||||
CopyNodePtrArrayPtr nodes = structureNode->nodes;
|
||||
for(size_t i=0; i<nodes->size(); ++i) {
|
||||
if((*nodes)[i].get()==NULL) {
|
||||
newLine(builder,indentLevel +1);
|
||||
ss.str("");
|
||||
ss << "node[" << i << "] is null";
|
||||
*builder += ss.str();
|
||||
continue;
|
||||
}
|
||||
dump(builder,(*nodes)[i],indentLevel+1);
|
||||
}
|
||||
}
|
||||
|
||||
bool PVCopy::init(epics::pvData::PVStructurePtr const &pvRequest)
|
||||
{
|
||||
PVStructurePtr pvMasterStructure = pvMaster;
|
||||
size_t len = pvRequest->getPVFields().size();
|
||||
bool entireMaster = false;
|
||||
if(len==string::npos) entireMaster = true;
|
||||
if(len==0) entireMaster = true;
|
||||
PVStructurePtr pvOptions;
|
||||
if(len==1) {
|
||||
pvOptions = pvRequest->getSubField<PVStructure>("_options");
|
||||
}
|
||||
if(entireMaster) {
|
||||
structure = pvMasterStructure->getStructure();
|
||||
CopyMasterNodePtr masterNode(new CopyMasterNode());
|
||||
headNode = masterNode;
|
||||
masterNode->options = pvOptions;
|
||||
masterNode->isStructure = false;
|
||||
masterNode->structureOffset = 0;
|
||||
masterNode->masterPVField = pvMasterStructure;
|
||||
masterNode->nfields = pvMasterStructure->getNumberFields();
|
||||
return true;
|
||||
}
|
||||
structure = createStructure(pvMasterStructure,pvRequest);
|
||||
if(!structure) return false;
|
||||
cacheInitStructure = createPVStructure();
|
||||
headNode = createStructureNodes(
|
||||
pvMaster,
|
||||
pvRequest,
|
||||
cacheInitStructure);
|
||||
return true;
|
||||
}
|
||||
|
||||
string PVCopy::dump(
|
||||
string const &value,
|
||||
CopyNodePtr const &node,
|
||||
int indentLevel)
|
||||
{
|
||||
throw std::logic_error(string("Not Implemented"));
|
||||
}
|
||||
|
||||
|
||||
StructureConstPtr PVCopy::createStructure(
|
||||
PVStructurePtr const &pvMaster,
|
||||
PVStructurePtr const &pvFromRequest)
|
||||
{
|
||||
if(pvFromRequest->getStructure()->getNumberFields()==0) {
|
||||
return pvMaster->getStructure();
|
||||
}
|
||||
PVFieldPtrArray const &pvFromRequestFields = pvFromRequest->getPVFields();
|
||||
StringArray const &fromRequestFieldNames = pvFromRequest->getStructure()->getFieldNames();
|
||||
size_t length = pvFromRequestFields.size();
|
||||
if(length==0) return NULLStructure;
|
||||
FieldConstPtrArray fields; fields.reserve(length);
|
||||
StringArray fieldNames; fields.reserve(length);
|
||||
for(size_t i=0; i<length; ++i) {
|
||||
string const &fieldName = fromRequestFieldNames[i];
|
||||
PVFieldPtr pvMasterField = pvMaster->getSubField(fieldName);
|
||||
if(!pvMasterField) continue;
|
||||
FieldConstPtr field = pvMasterField->getField();
|
||||
if(field->getType()==epics::pvData::structure) {
|
||||
PVStructurePtr pvRequestStructure = static_pointer_cast<PVStructure>(
|
||||
pvFromRequestFields[i]);
|
||||
if(pvRequestStructure->getNumberFields()>0) {
|
||||
StringArray const &names = pvRequestStructure->getStructure()->
|
||||
getFieldNames();
|
||||
size_t num = names.size();
|
||||
if(num>0 && names[0].compare("_options")==0) --num;
|
||||
if(num>0) {
|
||||
if(pvMasterField->getField()->getType()!=epics::pvData::structure) continue;
|
||||
fieldNames.push_back(fieldName);
|
||||
fields.push_back(createStructure(
|
||||
static_pointer_cast<PVStructure>(pvMasterField),
|
||||
pvRequestStructure));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
fieldNames.push_back(fieldName);
|
||||
fields.push_back(field);
|
||||
}
|
||||
size_t numsubfields = fields.size();
|
||||
if(numsubfields==0) return NULLStructure;
|
||||
return getFieldCreate()->createStructure(fieldNames, fields);
|
||||
}
|
||||
|
||||
CopyNodePtr PVCopy::createStructureNodes(
|
||||
PVStructurePtr const &pvMasterStructure,
|
||||
PVStructurePtr const &pvFromRequest,
|
||||
PVStructurePtr const &pvFromCopy)
|
||||
{
|
||||
PVFieldPtrArray const & copyPVFields = pvFromCopy->getPVFields();
|
||||
PVStructurePtr pvOptions;
|
||||
PVFieldPtr pvField = pvFromRequest->getSubField("_options");
|
||||
if(pvField) pvOptions = static_pointer_cast<PVStructure>(pvField);
|
||||
size_t number = copyPVFields.size();
|
||||
CopyNodePtrArrayPtr nodes(new CopyNodePtrArray());
|
||||
nodes->reserve(number);
|
||||
for(size_t i=0; i<number; i++) {
|
||||
PVFieldPtr copyPVField = copyPVFields[i];
|
||||
string fieldName = copyPVField->getFieldName();
|
||||
|
||||
PVStructurePtr requestPVStructure = pvFromRequest->getSubField<PVStructure>(fieldName);
|
||||
PVStructurePtr pvSubFieldOptions = requestPVStructure->getSubField<PVStructure>("_options");
|
||||
PVFieldPtr pvMasterField;
|
||||
PVFieldPtrArray const & pvMasterFields = pvMasterStructure->getPVFields();
|
||||
for(size_t j=0; i<pvMasterFields.size(); j++ ) {
|
||||
if(pvMasterFields[j]->getFieldName().compare(fieldName)==0) {
|
||||
pvMasterField = pvMasterFields[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
size_t numberRequest = requestPVStructure->getPVFields().size();
|
||||
if(pvSubFieldOptions) numberRequest--;
|
||||
if(numberRequest>0) {
|
||||
nodes->push_back(createStructureNodes(
|
||||
static_pointer_cast<PVStructure>(pvMasterField),
|
||||
requestPVStructure,
|
||||
static_pointer_cast<PVStructure>(copyPVField)));
|
||||
continue;
|
||||
}
|
||||
CopyMasterNodePtr masterNode(new CopyMasterNode());
|
||||
masterNode->options = pvSubFieldOptions;
|
||||
masterNode->isStructure = false;
|
||||
masterNode->masterPVField = pvMasterField;
|
||||
masterNode->nfields = copyPVField->getNumberFields();
|
||||
masterNode->structureOffset = copyPVField->getFieldOffset();
|
||||
nodes->push_back(masterNode);
|
||||
}
|
||||
CopyStructureNodePtr structureNode(new CopyStructureNode());
|
||||
structureNode->isStructure = true;
|
||||
structureNode->nodes = nodes;
|
||||
structureNode->structureOffset = pvFromCopy->getFieldOffset();
|
||||
structureNode->nfields = pvFromCopy->getNumberFields();
|
||||
structureNode->options = pvOptions;
|
||||
return structureNode;
|
||||
}
|
||||
|
||||
void PVCopy::updateStructureNodeSetBitSet(
|
||||
PVStructurePtr const &pvCopy,
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
epics::pvData::BitSetPtr const &bitSet)
|
||||
{
|
||||
for(size_t i=0; i<structureNode->nodes->size(); i++) {
|
||||
CopyNodePtr node = (*structureNode->nodes)[i];
|
||||
PVFieldPtr pvField = pvCopy->getSubField(node->structureOffset);
|
||||
if(node->isStructure) {
|
||||
PVStructurePtr xxx = static_pointer_cast<PVStructure>(pvField);
|
||||
CopyStructureNodePtr yyy =
|
||||
static_pointer_cast<CopyStructureNode>(node);
|
||||
updateStructureNodeSetBitSet(xxx,yyy,bitSet);
|
||||
} else {
|
||||
CopyMasterNodePtr masterNode =
|
||||
static_pointer_cast<CopyMasterNode>(node);
|
||||
updateSubFieldSetBitSet(pvField,masterNode->masterPVField,bitSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PVCopy::updateSubFieldSetBitSet(
|
||||
PVFieldPtr const &pvCopy,
|
||||
PVFieldPtr const &pvMaster,
|
||||
BitSetPtr const &bitSet)
|
||||
{
|
||||
FieldConstPtr field = pvCopy->getField();
|
||||
Type type = field->getType();
|
||||
if(type!=epics::pvData::structure) {
|
||||
bool isEqual = (*pvCopy == *pvMaster);
|
||||
if(isEqual) {
|
||||
if(type==structureArray) {
|
||||
// always act as though a change occurred.
|
||||
// Note that array elements are shared.
|
||||
bitSet->set(pvCopy->getFieldOffset());
|
||||
}
|
||||
}
|
||||
if(isEqual) return;
|
||||
pvCopy->copyUnchecked(*pvMaster);
|
||||
bitSet->set(pvCopy->getFieldOffset());
|
||||
return;
|
||||
}
|
||||
PVStructurePtr pvCopyStructure = static_pointer_cast<PVStructure>(pvCopy);
|
||||
PVFieldPtrArray const & pvCopyFields = pvCopyStructure->getPVFields();
|
||||
PVStructurePtr pvMasterStructure =
|
||||
static_pointer_cast<PVStructure>(pvMaster);
|
||||
PVFieldPtrArray const & pvMasterFields =
|
||||
pvMasterStructure->getPVFields();
|
||||
size_t length = pvCopyFields.size();
|
||||
for(size_t i=0; i<length; i++) {
|
||||
updateSubFieldSetBitSet(pvCopyFields[i],pvMasterFields[i],bitSet);
|
||||
}
|
||||
}
|
||||
|
||||
void PVCopy::updateStructureNodeFromBitSet(
|
||||
PVStructurePtr const &pvCopy,
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
BitSetPtr const &bitSet,
|
||||
bool toCopy,
|
||||
bool doAll)
|
||||
{
|
||||
size_t offset = structureNode->structureOffset;
|
||||
if(!doAll) {
|
||||
size_t nextSet = bitSet->nextSetBit(offset);
|
||||
if(nextSet==string::npos) return;
|
||||
}
|
||||
if(offset>=pvCopy->getNextFieldOffset()) return;
|
||||
if(!doAll) doAll = bitSet->get(offset);
|
||||
CopyNodePtrArrayPtr nodes = structureNode->nodes;
|
||||
for(size_t i=0; i<nodes->size(); i++) {
|
||||
CopyNodePtr node = (*nodes)[i];
|
||||
PVFieldPtr pvField = pvCopy->getSubFieldT(node->structureOffset);
|
||||
if(node->isStructure) {
|
||||
PVStructurePtr xxx = static_pointer_cast<PVStructure>(pvField);
|
||||
CopyStructureNodePtr subStructureNode =
|
||||
static_pointer_cast<CopyStructureNode>(node);
|
||||
updateStructureNodeFromBitSet(
|
||||
xxx,subStructureNode,bitSet,toCopy,doAll);
|
||||
} else {
|
||||
CopyMasterNodePtr masterNode =
|
||||
static_pointer_cast<CopyMasterNode>(node);
|
||||
updateSubFieldFromBitSet(
|
||||
pvField,masterNode->masterPVField,bitSet,toCopy,doAll);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PVCopy::updateSubFieldFromBitSet(
|
||||
PVFieldPtr const &pvCopy,
|
||||
PVFieldPtr const &pvMasterField,
|
||||
BitSetPtr const &bitSet,
|
||||
bool toCopy,
|
||||
bool doAll)
|
||||
{
|
||||
if(!doAll) {
|
||||
doAll = bitSet->get(pvCopy->getFieldOffset());
|
||||
}
|
||||
if(!doAll) {
|
||||
size_t offset = pvCopy->getFieldOffset();
|
||||
size_t nextSet = bitSet->nextSetBit(offset);
|
||||
if(nextSet==string::npos) return;
|
||||
if(nextSet>=pvCopy->getNextFieldOffset()) return;
|
||||
}
|
||||
if(pvCopy->getField()->getType()==epics::pvData::structure) {
|
||||
PVStructurePtr pvCopyStructure =
|
||||
static_pointer_cast<PVStructure>(pvCopy);
|
||||
PVFieldPtrArray const & pvCopyFields = pvCopyStructure->getPVFields();
|
||||
if(pvMasterField->getField()->getType() !=epics::pvData::structure)
|
||||
{
|
||||
throw std::logic_error(string("Logic error"));
|
||||
}
|
||||
PVStructurePtr pvMasterStructure =
|
||||
static_pointer_cast<PVStructure>(pvMasterField);
|
||||
PVFieldPtrArray const & pvMasterFields =
|
||||
pvMasterStructure->getPVFields();
|
||||
for(size_t i=0; i<pvCopyFields.size(); i++) {
|
||||
updateSubFieldFromBitSet(
|
||||
pvCopyFields[i],
|
||||
pvMasterFields[i],
|
||||
bitSet,toCopy,doAll);
|
||||
}
|
||||
} else {
|
||||
if(toCopy) {
|
||||
pvCopy->copyUnchecked(*pvMasterField);
|
||||
} else {
|
||||
pvMasterField->copyUnchecked(*pvCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CopyMasterNodePtr PVCopy::getCopyOffset(
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
PVFieldPtr const &masterPVField)
|
||||
{
|
||||
size_t offset = masterPVField->getFieldOffset();
|
||||
CopyNodePtrArrayPtr nodes = structureNode->nodes;
|
||||
for(size_t i=0; i< nodes->size(); i++) {
|
||||
CopyNodePtr node = (*nodes)[i];
|
||||
if(!node->isStructure) {
|
||||
CopyMasterNodePtr masterNode =
|
||||
static_pointer_cast<CopyMasterNode>(node);
|
||||
size_t off = masterNode->masterPVField->getFieldOffset();
|
||||
size_t nextOffset = masterNode->masterPVField->getNextFieldOffset();
|
||||
if(offset>= off && offset<nextOffset) return masterNode;
|
||||
} else {
|
||||
CopyStructureNodePtr subNode =
|
||||
static_pointer_cast<CopyStructureNode>(node);
|
||||
CopyMasterNodePtr masterNode =
|
||||
getCopyOffset(subNode,masterPVField);
|
||||
if(masterNode) return masterNode;
|
||||
}
|
||||
}
|
||||
return NULLCopyMasterNode;
|
||||
}
|
||||
|
||||
CopyMasterNodePtr PVCopy::getMasterNode(
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
std::size_t structureOffset)
|
||||
{
|
||||
CopyNodePtrArrayPtr nodes = structureNode->nodes;
|
||||
for(size_t i=0; i< nodes->size(); i++) {
|
||||
CopyNodePtr node = (*nodes)[i];
|
||||
if(structureOffset>=(node->structureOffset + node->nfields)) continue;
|
||||
if(!node->isStructure) {
|
||||
CopyMasterNodePtr masterNode =
|
||||
static_pointer_cast<CopyMasterNode>(node);
|
||||
return masterNode;
|
||||
}
|
||||
CopyStructureNodePtr subNode =
|
||||
static_pointer_cast<CopyStructureNode>(node);
|
||||
return getMasterNode(subNode,structureOffset);
|
||||
}
|
||||
return NULLCopyMasterNode;
|
||||
}
|
||||
|
||||
}}
|
||||
328
src/copy/requestmapper.cpp
Normal file
328
src/copy/requestmapper.cpp
Normal file
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <epicsAssert.h>
|
||||
#include <epicsTypes.h>
|
||||
#include <epicsVersion.h>
|
||||
#include <epicsConvert.h>
|
||||
#include <epicsAssert.h>
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsGuard.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/createRequest.h>
|
||||
#include <pv/epicsException.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
// Our arbitrary limit on pvRequest structure depth to bound stack usage during recursion
|
||||
static const unsigned maxDepth = 5;
|
||||
|
||||
namespace epics{namespace pvData {
|
||||
|
||||
PVRequestMapper::PVRequestMapper() {}
|
||||
|
||||
PVRequestMapper::PVRequestMapper(const PVStructure &base,
|
||||
const PVStructure &pvRequest,
|
||||
mode_t mode)
|
||||
{
|
||||
compute(base, pvRequest, mode);
|
||||
}
|
||||
|
||||
PVStructurePtr PVRequestMapper::buildRequested() const
|
||||
{
|
||||
if(!typeRequested)
|
||||
THROW_EXCEPTION2(std::logic_error, "No mapping compute()d");
|
||||
return typeRequested->build();
|
||||
}
|
||||
|
||||
PVStructurePtr PVRequestMapper::buildBase() const
|
||||
{
|
||||
if(!typeBase)
|
||||
THROW_EXCEPTION2(std::logic_error, "No mapping compute()d");
|
||||
return typeBase->build();
|
||||
}
|
||||
|
||||
void PVRequestMapper::compute(const PVStructure &base,
|
||||
const PVStructure &pvRequest,
|
||||
mode_t mode)
|
||||
{
|
||||
if(base.getFieldOffset()!=0)
|
||||
THROW_EXCEPTION2(std::logic_error, "Mapper must be used with top level PVStructure");
|
||||
|
||||
bool ok = true;
|
||||
|
||||
// we want to be transactional, which requires a second copy of everything.
|
||||
PVRequestMapper temp;
|
||||
|
||||
// whether to preserve IDs of partial structures.
|
||||
bool keepids = false;
|
||||
PVScalar::const_shared_pointer pbp(pvRequest.getSubField<PVScalar>("record._options.keepIDs"));
|
||||
try {
|
||||
if(pbp) keepids = pbp->getAs<boolean>();
|
||||
}catch(std::runtime_error& e){
|
||||
std::ostringstream msg;
|
||||
msg<<"Can't parse keepIDs : '"<<e.what()<<"' ";
|
||||
temp.messages+=msg.str();
|
||||
}
|
||||
|
||||
PVStructure::const_shared_pointer fields(pvRequest.getSubField<PVStructure>("field"));
|
||||
if(!fields || fields->getPVFields().empty()) {
|
||||
// not selection, or empty selection, treated as select all
|
||||
temp.typeBase = temp.typeRequested = base.getStructure();
|
||||
|
||||
for(size_t i=1, N=base.getNextFieldOffset(); i<N; i++)
|
||||
temp.maskRequested.set(i);
|
||||
|
||||
} else {
|
||||
FieldBuilderPtr builder(getFieldCreate()->createFieldBuilder());
|
||||
|
||||
if(keepids)
|
||||
builder = builder->setId(base.getStructure()->getID());
|
||||
|
||||
ok &= temp._compute(base, *fields, builder, keepids, 0); // fills in builder
|
||||
|
||||
temp.typeBase = base.getStructure();
|
||||
temp.typeRequested = builder->createStructure();
|
||||
// possible that typeBase==typeRequested if all fields explicitly selected
|
||||
}
|
||||
|
||||
if(mode==Mask) {
|
||||
// short circuit use of masked Structure, but keep maskRequested
|
||||
temp.typeRequested = temp.typeBase;
|
||||
}
|
||||
|
||||
{
|
||||
PVStructurePtr proto(temp.typeRequested->build());
|
||||
|
||||
// base -> request may be sparce mapping
|
||||
temp.base2req.resize(base.getNextFieldOffset());
|
||||
// request -> base is dense mapping
|
||||
temp.req2base.resize(proto->getNextFieldOffset());
|
||||
|
||||
// special handling for whole structure mapping. in part because getSubField(0) isn't allowed
|
||||
temp.base2req[0] = Mapping(0, false);
|
||||
temp.req2base[0] = Mapping(0, false);
|
||||
|
||||
// Iterate prototype of requested to map with base field offsets.
|
||||
// which is handled as a special case below.
|
||||
// We also don't try to prevent redundant copies if both leaf and compress bits are set.
|
||||
for(size_t r=1, N=proto->getNextFieldOffset(); r<N; r++) {
|
||||
PVField::const_shared_pointer fld_req(proto->getSubFieldT(r)),
|
||||
fld_base(base.getSubFieldT(fld_req->getFullName()));
|
||||
const size_t b = fld_base->getFieldOffset();
|
||||
|
||||
if(!temp.requestedMask().get(b))
|
||||
continue;
|
||||
|
||||
bool leaf = fld_base->getField()->getType()!=structure;
|
||||
|
||||
// initialize mapping when our bit is set
|
||||
temp.base2req[b] = Mapping(r, leaf);
|
||||
temp.req2base[r] = Mapping(b, leaf);
|
||||
|
||||
// add ourself to all "compress" bit mappings of enclosing structures
|
||||
for(const PVStructure *parent = fld_req->getParent(); parent; parent = parent->getParent()) {
|
||||
temp.req2base[parent->getFieldOffset()].tomask .set(b);
|
||||
temp.req2base[parent->getFieldOffset()].frommask.set(r);
|
||||
}
|
||||
|
||||
for(const PVStructure *parent = fld_base->getParent(); parent; parent = parent->getParent()) {
|
||||
temp.base2req[parent->getFieldOffset()].tomask .set(r);
|
||||
temp.base2req[parent->getFieldOffset()].frommask.set(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
temp.maskRequested.set(0);
|
||||
|
||||
if(temp.maskRequested.nextSetBit(1)==-1) {
|
||||
ok = false;
|
||||
temp.messages+="Empty field selection";
|
||||
}
|
||||
|
||||
if(!ok)
|
||||
throw std::runtime_error(temp.messages);
|
||||
|
||||
swap(temp);
|
||||
}
|
||||
|
||||
bool PVRequestMapper::_compute(const PVStructure& base, const PVStructure& pvReq,
|
||||
FieldBuilderPtr& builder, bool keepids, unsigned depth)
|
||||
{
|
||||
bool ok = true;
|
||||
const StringArray& reqNames = pvReq.getStructure()->getFieldNames();
|
||||
|
||||
for(size_t i=0, N=reqNames.size(); i<N; i++) {
|
||||
// iterate through requested fields
|
||||
|
||||
PVField::const_shared_pointer subtype(base.getSubField(reqNames[i]));
|
||||
const FieldConstPtr& subReq = pvReq.getStructure()->getFields()[i];
|
||||
|
||||
if(subReq->getType()!=structure) {
|
||||
// pvRequest .field was not properly composed
|
||||
std::ostringstream msg;
|
||||
// not a great warning message as it doesn't distinguish 'a.value' from 'b.value',
|
||||
// but getFullName() whould prefix with 'field.', which would probably cause
|
||||
// more frequent confusion...
|
||||
msg<<"request invalid '"<<pvReq.getStructure()->getFieldNames()[i]<<"' ";
|
||||
messages+=msg.str();
|
||||
ok = false;
|
||||
|
||||
} else if(!subtype) {
|
||||
// requested field does not actually exist in base
|
||||
std::ostringstream msg;
|
||||
msg<<"No field '"<<pvReq.getStructure()->getFieldNames()[i]<<"' ";
|
||||
messages+=msg.str();
|
||||
|
||||
} else if(depth>=maxDepth // exceeds max recursion depth
|
||||
|| subtype->getField()->getType()!=structure // requested field is a leaf
|
||||
|| static_cast<const Structure&>(*subReq).getFieldNames().empty() // requests all sub-fields
|
||||
)
|
||||
{
|
||||
// just add the whole thing
|
||||
builder = builder->add(reqNames[i], subtype->getField());
|
||||
for(size_t j=subtype->getFieldOffset(), N=subtype->getNextFieldOffset(); j<N; j++)
|
||||
maskRequested.set(j);
|
||||
|
||||
if(subtype->getField()->getType()!=structure
|
||||
&& !static_cast<const Structure&>(*subReq).getFieldNames().empty())
|
||||
{
|
||||
// attempt to select below a leaf field
|
||||
std::ostringstream msg;
|
||||
msg<<"Leaf field '"<<pvReq.getFullName()<<"' ";
|
||||
messages+=msg.str();
|
||||
} else if(depth>=maxDepth) {
|
||||
std::ostringstream msg;
|
||||
msg<<"selection truncated at '"<<pvReq.getFullName()<<"' ";
|
||||
messages+=msg.str();
|
||||
}
|
||||
|
||||
} else {
|
||||
// recurse into sub-structure
|
||||
const PVStructure& substruct = static_cast<const PVStructure&>(*subtype);
|
||||
|
||||
builder = builder->addNestedStructure(reqNames[i]);
|
||||
maskRequested.set(substruct.getFieldOffset());
|
||||
|
||||
if(keepids)
|
||||
builder = builder->setId(substruct.getStructure()->getID());
|
||||
|
||||
_compute(substruct,
|
||||
static_cast<const PVStructure&>(*pvReq.getPVFields()[i]),
|
||||
builder, keepids, depth+1u);
|
||||
|
||||
builder = builder->endNested();
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
void PVRequestMapper::copyBaseToRequested(
|
||||
const PVStructure& base,
|
||||
const BitSet& baseMask,
|
||||
PVStructure& request,
|
||||
BitSet& requestMask
|
||||
) const {
|
||||
assert(base.getStructure()==typeBase);
|
||||
assert(request.getStructure()==typeRequested);
|
||||
_map(base, baseMask, request, requestMask, false);
|
||||
}
|
||||
|
||||
void PVRequestMapper::copyBaseFromRequested(
|
||||
PVStructure& base,
|
||||
BitSet& baseMask,
|
||||
const PVStructure& request,
|
||||
const BitSet& requestMask
|
||||
) const {
|
||||
assert(base.getStructure()==typeBase);
|
||||
assert(request.getStructure()==typeRequested);
|
||||
_map(request, requestMask, base, baseMask, true);
|
||||
}
|
||||
|
||||
void PVRequestMapper::_map(const PVStructure& src, const BitSet& maskSrc,
|
||||
PVStructure& dest, BitSet& maskDest,
|
||||
bool dir_r2b) const
|
||||
{
|
||||
{
|
||||
scratch = maskSrc;
|
||||
const mapping_t& map = dir_r2b ? req2base : base2req;
|
||||
|
||||
assert(map.size()==src.getNumberFields());
|
||||
|
||||
for(int32 i=scratch.nextSetBit(0), N=map.size(); i>=0 && i<N; i=scratch.nextSetBit(i+1)) {
|
||||
const Mapping& M = map[i];
|
||||
if(!M.valid) {
|
||||
assert(!dir_r2b); // only base -> requested mapping can have holes
|
||||
|
||||
} else if(M.leaf) {
|
||||
// just copy
|
||||
dest.getSubFieldT(M.to)->copy(*src.getSubFieldT(i));
|
||||
maskDest.set(M.to);
|
||||
|
||||
} else {
|
||||
// set bits of all sub-fields (in requested structure)
|
||||
// these indicies are always >i
|
||||
scratch |= M.frommask;
|
||||
|
||||
// we will also set the individual bits, but if a compress bit is set in the input,
|
||||
// then set the corresponding bit in the output.
|
||||
maskDest.set(M.to);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PVRequestMapper::_mapMask(const BitSet& maskSrc,
|
||||
BitSet& maskDest,
|
||||
bool dir_r2b) const
|
||||
{
|
||||
if(maskSrc.isEmpty()) {
|
||||
// no-op
|
||||
|
||||
} else {
|
||||
const mapping_t& map = dir_r2b ? req2base : base2req;
|
||||
|
||||
for(int32 i=maskSrc.nextSetBit(0), N=map.size(); i>=0 && i<N; i=maskSrc.nextSetBit(i+1)) {
|
||||
const Mapping& M = map[i];
|
||||
if(!M.valid) {
|
||||
assert(!dir_r2b); // only base -> requested mapping can have holes
|
||||
|
||||
} else {
|
||||
maskDest.set(M.to);
|
||||
|
||||
if(!M.leaf) {
|
||||
maskDest |= M.tomask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void PVRequestMapper::swap(PVRequestMapper& other)
|
||||
{
|
||||
typeBase.swap(other.typeBase);
|
||||
typeRequested.swap(other.typeRequested);
|
||||
maskRequested.swap(other.maskRequested);
|
||||
base2req.swap(other.base2req);
|
||||
req2base.swap(other.req2base);
|
||||
messages.swap(other.messages);
|
||||
scratch.swap(other.scratch); // paranoia
|
||||
}
|
||||
|
||||
void PVRequestMapper::reset()
|
||||
{
|
||||
typeBase.reset();
|
||||
typeRequested.reset();
|
||||
maskRequested.clear();
|
||||
base2req.clear();
|
||||
req2base.clear();
|
||||
messages.clear();
|
||||
scratch.clear(); // paranoia
|
||||
}
|
||||
|
||||
}} //namespace epics::pvData
|
||||
@@ -23,7 +23,7 @@ namespace epics { namespace pvData {
|
||||
* 1) same instance
|
||||
* 2) same type (field and scalar/element), same name, same subfields (if any)
|
||||
*/
|
||||
bool operator==(const Field& a, const Field& b)
|
||||
bool compare(const Field& a, const Field& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
@@ -33,58 +33,58 @@ bool operator==(const Field& a, const Field& b)
|
||||
case scalar: {
|
||||
const Scalar &A=static_cast<const Scalar&>(a);
|
||||
const Scalar &B=static_cast<const Scalar&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
case scalarArray: {
|
||||
const ScalarArray &A=static_cast<const ScalarArray&>(a);
|
||||
const ScalarArray &B=static_cast<const ScalarArray&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
case structure: {
|
||||
const Structure &A=static_cast<const Structure&>(a);
|
||||
const Structure &B=static_cast<const Structure&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
case structureArray: {
|
||||
const StructureArray &A=static_cast<const StructureArray&>(a);
|
||||
const StructureArray &B=static_cast<const StructureArray&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
case union_: {
|
||||
const Union &A=static_cast<const Union&>(a);
|
||||
const Union &B=static_cast<const Union&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
case unionArray: {
|
||||
const UnionArray &A=static_cast<const UnionArray&>(a);
|
||||
const UnionArray &B=static_cast<const UnionArray&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
default:
|
||||
throw std::logic_error("Invalid Field type in comparison");
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const Scalar& a, const Scalar& b)
|
||||
bool compare(const Scalar& a, const Scalar& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
return a.getScalarType()==b.getScalarType();
|
||||
}
|
||||
|
||||
bool operator==(const ScalarArray& a, const ScalarArray& b)
|
||||
bool compare(const ScalarArray& a, const ScalarArray& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
return a.getElementType()==b.getElementType();
|
||||
}
|
||||
|
||||
bool operator==(const Structure& a, const Structure& b)
|
||||
bool compare(const Structure& a, const Structure& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
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 operator==(const Structure& a, const Structure& b)
|
||||
return std::equal( an.begin(), an.end(), bn.begin() );
|
||||
}
|
||||
|
||||
bool operator==(const StructureArray& a, const StructureArray& b)
|
||||
bool compare(const StructureArray& a, const StructureArray& b)
|
||||
{
|
||||
return *(a.getStructure().get())==*(b.getStructure().get());
|
||||
}
|
||||
|
||||
bool operator==(const Union& a, const Union& b)
|
||||
bool compare(const Union& a, const Union& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
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 operator==(const Union& a, const Union& b)
|
||||
return std::equal( an.begin(), an.end(), bn.begin() );
|
||||
}
|
||||
|
||||
bool operator==(const UnionArray& a, const UnionArray& b)
|
||||
bool compare(const UnionArray& a, const UnionArray& b)
|
||||
{
|
||||
return *(a.getUnion().get())==*(b.getUnion().get());
|
||||
}
|
||||
|
||||
bool operator==(const BoundedString& a, const BoundedString& b)
|
||||
bool compare(const BoundedString& a, const BoundedString& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
@@ -268,10 +268,10 @@ bool compareField(const PVUnion* left, const PVUnion* right)
|
||||
|
||||
if(*ls!=*right->getUnion())
|
||||
return false;
|
||||
|
||||
|
||||
if (ls->isVariant())
|
||||
{
|
||||
PVFieldPtr lval = left->get();
|
||||
const PVField::const_shared_pointer& lval = left->get();
|
||||
if (lval.get() == 0)
|
||||
return right->get().get() == 0;
|
||||
else
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
@@ -7,16 +7,13 @@
|
||||
* @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>
|
||||
@@ -24,6 +21,7 @@
|
||||
#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;
|
||||
@@ -59,148 +57,123 @@ 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>
|
||||
BasePVScalar<T>::BasePVScalar(ScalarConstPtr const & scalar)
|
||||
: PVScalarValue<T>(scalar),value(0)
|
||||
{}
|
||||
//Note: '0' is a suitable default for all POD types (not string)
|
||||
PVScalarValue<T>::~PVScalarValue() {}
|
||||
|
||||
template<typename T>
|
||||
BasePVScalar<T>::~BasePVScalar() {}
|
||||
|
||||
template<typename T>
|
||||
T BasePVScalar<T>::get() const { return value;}
|
||||
|
||||
template<typename T>
|
||||
void BasePVScalar<T>::put(T val)
|
||||
std::ostream& PVScalarValue<T>::dumpValue(std::ostream& o) const
|
||||
{
|
||||
value = val;
|
||||
PVField::postPut();
|
||||
return o << get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void BasePVScalar<T>::serialize(ByteBuffer *pbuffer,
|
||||
void PVScalarValue<T>::operator>>=(T& value) const
|
||||
{
|
||||
value = get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::operator<<=(typename storage_t::arg_type value)
|
||||
{
|
||||
put(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::assign(const PVScalar& scalar)
|
||||
{
|
||||
if(isImmutable())
|
||||
throw std::invalid_argument("destination is immutable");
|
||||
copyUnchecked(scalar);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::copy(const PVScalar& from)
|
||||
{
|
||||
assign(from);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::copyUnchecked(const PVScalar& from)
|
||||
{
|
||||
if(this==&from)
|
||||
return;
|
||||
T result;
|
||||
from.getAs((void*)&result, typeCode);
|
||||
put(result);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const {
|
||||
pflusher->ensureBuffer(sizeof(T));
|
||||
pbuffer->put(value);
|
||||
pbuffer->put(storage.value);
|
||||
}
|
||||
|
||||
template<>
|
||||
void PVScalarValue<std::string>::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const {
|
||||
SerializeHelper::serializeString(storage.value, pbuffer, pflusher);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void BasePVScalar<T>::deserialize(ByteBuffer *pbuffer,
|
||||
void PVScalarValue<T>::deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pflusher)
|
||||
{
|
||||
pflusher->ensureData(sizeof(T));
|
||||
value = pbuffer->GET(T);
|
||||
storage.value = pbuffer->GET(T);
|
||||
}
|
||||
|
||||
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;
|
||||
template<>
|
||||
void PVScalarValue<std::string>::deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pflusher)
|
||||
{
|
||||
storage.value = SerializeHelper::deserializeString(pbuffer, pflusher);
|
||||
// TODO: check for violations of maxLength?
|
||||
}
|
||||
|
||||
// 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()
|
||||
PVString::PVString(ScalarConstPtr const & scalar)
|
||||
: PVScalarValue<std::string>(scalar)
|
||||
{
|
||||
BoundedStringConstPtr boundedString = std::tr1::dynamic_pointer_cast<const BoundedString>(scalar);
|
||||
if (boundedString.get())
|
||||
maxLength = boundedString->getMaximumLength();
|
||||
storage.maxLength = boundedString->getMaximumLength();
|
||||
else
|
||||
maxLength = 0;
|
||||
storage.maxLength = 0;
|
||||
}
|
||||
|
||||
BasePVString::~BasePVString() {}
|
||||
|
||||
string BasePVString::get() const { return value;}
|
||||
|
||||
void BasePVString::put(string val)
|
||||
std::ostream& PVString::dumpValue(std::ostream& o) const
|
||||
{
|
||||
if (maxLength > 0 && val.length() > maxLength)
|
||||
throw std::overflow_error("string too long");
|
||||
|
||||
value = val;
|
||||
postPut();
|
||||
o<<maybeQuote(get());
|
||||
return o;
|
||||
}
|
||||
|
||||
void BasePVString::serialize(ByteBuffer *pbuffer,
|
||||
/* 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,
|
||||
SerializableControl *pflusher) const
|
||||
{
|
||||
SerializeHelper::serializeString(value, pbuffer, pflusher);
|
||||
}
|
||||
{PVScalarValue<std::string>::serialize(pbuffer, pflusher);}
|
||||
|
||||
void BasePVString::deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pflusher)
|
||||
{
|
||||
value = SerializeHelper::deserializeString(pbuffer, pflusher);
|
||||
}
|
||||
|
||||
void BasePVString::serialize(ByteBuffer *pbuffer,
|
||||
void PVString::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const
|
||||
{
|
||||
// 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;
|
||||
// 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;
|
||||
|
||||
const size_t maxCount = length - offset;
|
||||
if (count > maxCount)
|
||||
count = maxCount;
|
||||
|
||||
// write
|
||||
SerializeHelper::serializeSubstring(value, offset, count, pbuffer, pflusher);
|
||||
const size_t maxCount = length - offset;
|
||||
if (count > maxCount)
|
||||
count = maxCount;
|
||||
|
||||
// write
|
||||
SerializeHelper::serializeSubstring(storage.value, offset, count, pbuffer, pflusher);
|
||||
}
|
||||
|
||||
void PVArray::checkLength(size_t len)
|
||||
void PVArray::checkLength(size_t len) const
|
||||
{
|
||||
Array::ArraySizeType type = getArray()->getArraySizeType();
|
||||
if (type != Array::variable)
|
||||
@@ -213,61 +186,79 @@ void PVArray::checkLength(size_t len)
|
||||
}
|
||||
}
|
||||
|
||||
/** Default storage for arrays
|
||||
*/
|
||||
template<typename T>
|
||||
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;
|
||||
PVValueArray<T>::PVValueArray(ScalarArrayConstPtr const & scalarArray)
|
||||
:base_t(scalarArray)
|
||||
,value()
|
||||
|
||||
typedef ::epics::pvData::shared_vector<T> svector;
|
||||
typedef ::epics::pvData::shared_vector<const T> const_svector;
|
||||
{}
|
||||
|
||||
DefaultPVArray(ScalarArrayConstPtr const & scalarArray);
|
||||
virtual ~DefaultPVArray();
|
||||
PVValueArray<PVStructurePtr>::PVValueArray(StructureArrayConstPtr const & structureArray)
|
||||
:base_t(structureArray)
|
||||
,structureArray(structureArray)
|
||||
|
||||
virtual size_t getLength() const {return value.size();}
|
||||
virtual size_t getCapacity() const {return value.capacity();}
|
||||
{}
|
||||
|
||||
virtual void setCapacity(size_t capacity);
|
||||
virtual void setLength(size_t length);
|
||||
|
||||
virtual const_svector view() const {return value;}
|
||||
virtual void swap(const_svector &other);
|
||||
virtual void replace(const const_svector& next);
|
||||
|
||||
// from Serializable
|
||||
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) const;
|
||||
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher);
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const;
|
||||
private:
|
||||
const_svector value;
|
||||
};
|
||||
PVValueArray<PVUnionPtr>::PVValueArray(UnionArrayConstPtr const & unionArray)
|
||||
:base_t(unionArray)
|
||||
,unionArray(unionArray)
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
DefaultPVArray<T>::DefaultPVArray(ScalarArrayConstPtr const & scalarArray)
|
||||
: PVValueArray<T>(scalarArray),
|
||||
value()
|
||||
|
||||
PVValueArray<T>::~PVValueArray() {}
|
||||
|
||||
template<typename T>
|
||||
ArrayConstPtr PVValueArray<T>::getArray() const
|
||||
{
|
||||
ArrayConstPtr array = this->getArray();
|
||||
if (array->getArraySizeType() == Array::fixed)
|
||||
{
|
||||
// this->setLength(array->getMaximumCapacity());
|
||||
this->setCapacityMutable(false);
|
||||
}
|
||||
return std::tr1::static_pointer_cast<const Array>(this->getField());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
DefaultPVArray<T>::~DefaultPVArray()
|
||||
{ }
|
||||
std::ostream& PVValueArray<T>::dumpValue(std::ostream& o) const
|
||||
{
|
||||
const_svector v(this->view());
|
||||
typename const_svector::const_iterator it(v.begin()),
|
||||
end(v.end());
|
||||
o << '[';
|
||||
if(it!=end) {
|
||||
o << print_cast(*it++);
|
||||
for(; it!=end; ++it)
|
||||
o << ',' << print_cast(*it);
|
||||
|
||||
}
|
||||
return o << ']';
|
||||
}
|
||||
|
||||
template<>
|
||||
std::ostream& PVValueArray<std::string>::dumpValue(std::ostream& o, size_t index) const
|
||||
{
|
||||
return o << 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>
|
||||
void DefaultPVArray<T>::setCapacity(size_t capacity)
|
||||
std::ostream& PVValueArray<T>::dumpValue(std::ostream& o, size_t index) const
|
||||
{
|
||||
return o << print_cast(this->view().at(index));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::setCapacity(size_t capacity)
|
||||
{
|
||||
if(this->isCapacityMutable()) {
|
||||
this->checkLength(capacity);
|
||||
@@ -278,7 +269,7 @@ void DefaultPVArray<T>::setCapacity(size_t capacity)
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void DefaultPVArray<T>::setLength(size_t length)
|
||||
void PVValueArray<T>::setLength(size_t length)
|
||||
{
|
||||
if(this->isImmutable())
|
||||
THROW_EXCEPTION2(std::logic_error, "immutable");
|
||||
@@ -295,7 +286,7 @@ void DefaultPVArray<T>::setLength(size_t length)
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void DefaultPVArray<T>::replace(const const_svector& next)
|
||||
void PVValueArray<T>::replace(const const_svector& next)
|
||||
{
|
||||
this->checkLength(next.size());
|
||||
|
||||
@@ -304,7 +295,7 @@ void DefaultPVArray<T>::replace(const const_svector& next)
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void DefaultPVArray<T>::swap(const_svector &other)
|
||||
void PVValueArray<T>::swap(const_svector &other)
|
||||
{
|
||||
if (this->isImmutable())
|
||||
THROW_EXCEPTION2(std::logic_error, "immutable");
|
||||
@@ -316,13 +307,13 @@ void DefaultPVArray<T>::swap(const_svector &other)
|
||||
|
||||
|
||||
template<typename T>
|
||||
void DefaultPVArray<T>::serialize(ByteBuffer *pbuffer,
|
||||
void PVValueArray<T>::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const {
|
||||
serialize(pbuffer, pflusher, 0, this->getLength());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void DefaultPVArray<T>::deserialize(ByteBuffer *pbuffer,
|
||||
void PVValueArray<T>::deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pcontrol) {
|
||||
|
||||
size_t size = this->getArray()->getArraySizeType() == Array::fixed ?
|
||||
@@ -371,7 +362,7 @@ void DefaultPVArray<T>::deserialize(ByteBuffer *pbuffer,
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void DefaultPVArray<T>::serialize(ByteBuffer *pbuffer,
|
||||
void PVValueArray<T>::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const
|
||||
{
|
||||
//TODO: avoid incrementing the ref counter...
|
||||
@@ -415,7 +406,7 @@ void DefaultPVArray<T>::serialize(ByteBuffer *pbuffer,
|
||||
// specializations for string
|
||||
|
||||
template<>
|
||||
void DefaultPVArray<string>::deserialize(ByteBuffer *pbuffer,
|
||||
void PVValueArray<string>::deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pcontrol) {
|
||||
|
||||
size_t size = this->getArray()->getArraySizeType() == Array::fixed ?
|
||||
@@ -442,7 +433,7 @@ void DefaultPVArray<string>::deserialize(ByteBuffer *pbuffer,
|
||||
}
|
||||
|
||||
template<>
|
||||
void DefaultPVArray<string>::serialize(ByteBuffer *pbuffer,
|
||||
void PVValueArray<string>::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const {
|
||||
|
||||
const_svector temp(value);
|
||||
@@ -458,18 +449,17 @@ void DefaultPVArray<string>::serialize(ByteBuffer *pbuffer,
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
template<typename T>
|
||||
void PVValueArray<T>::_getAsVoid(epics::pvData::shared_vector<const void>& out) const
|
||||
{
|
||||
out = static_shared_vector_cast<const void>(this->view());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::_putFromVoid(const epics::pvData::shared_vector<const void>& in)
|
||||
{
|
||||
this->replace(shared_vector_convert<const T>(in));
|
||||
}
|
||||
|
||||
// Factory
|
||||
|
||||
@@ -564,29 +554,29 @@ PVScalarPtr PVDataCreate::createPVScalar(ScalarConstPtr const & scalar)
|
||||
ScalarType scalarType = scalar->getScalarType();
|
||||
switch(scalarType) {
|
||||
case pvBoolean:
|
||||
return PVScalarPtr(new BasePVBoolean(scalar));
|
||||
return PVScalarPtr(new PVBoolean(scalar));
|
||||
case pvByte:
|
||||
return PVScalarPtr(new BasePVByte(scalar));
|
||||
return PVScalarPtr(new PVByte(scalar));
|
||||
case pvShort:
|
||||
return PVScalarPtr(new BasePVShort(scalar));
|
||||
return PVScalarPtr(new PVShort(scalar));
|
||||
case pvInt:
|
||||
return PVScalarPtr(new BasePVInt(scalar));
|
||||
return PVScalarPtr(new PVInt(scalar));
|
||||
case pvLong:
|
||||
return PVScalarPtr(new BasePVLong(scalar));
|
||||
return PVScalarPtr(new PVLong(scalar));
|
||||
case pvUByte:
|
||||
return PVScalarPtr(new BasePVUByte(scalar));
|
||||
return PVScalarPtr(new PVUByte(scalar));
|
||||
case pvUShort:
|
||||
return PVScalarPtr(new BasePVUShort(scalar));
|
||||
return PVScalarPtr(new PVUShort(scalar));
|
||||
case pvUInt:
|
||||
return PVScalarPtr(new BasePVUInt(scalar));
|
||||
return PVScalarPtr(new PVUInt(scalar));
|
||||
case pvULong:
|
||||
return PVScalarPtr(new BasePVULong(scalar));
|
||||
return PVScalarPtr(new PVULong(scalar));
|
||||
case pvFloat:
|
||||
return PVScalarPtr(new BasePVFloat(scalar));
|
||||
return PVScalarPtr(new PVFloat(scalar));
|
||||
case pvDouble:
|
||||
return PVScalarPtr(new BasePVDouble(scalar));
|
||||
return PVScalarPtr(new PVDouble(scalar));
|
||||
case pvString:
|
||||
return PVScalarPtr(new BasePVString(scalar));
|
||||
return PVScalarPtr(new PVString(scalar));
|
||||
}
|
||||
throw std::logic_error("PVDataCreate::createPVScalar should never get here");
|
||||
}
|
||||
@@ -611,32 +601,32 @@ PVScalarArrayPtr PVDataCreate::createPVScalarArray(
|
||||
{
|
||||
switch(scalarArray->getElementType()) {
|
||||
case pvBoolean:
|
||||
return PVScalarArrayPtr(new DefaultPVBooleanArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVBooleanArray(scalarArray));
|
||||
case pvByte:
|
||||
return PVScalarArrayPtr(new BasePVByteArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVByteArray(scalarArray));
|
||||
case pvShort:
|
||||
return PVScalarArrayPtr(new BasePVShortArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVShortArray(scalarArray));
|
||||
case pvInt:
|
||||
return PVScalarArrayPtr(new BasePVIntArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVIntArray(scalarArray));
|
||||
case pvLong:
|
||||
return PVScalarArrayPtr(new BasePVLongArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVLongArray(scalarArray));
|
||||
case pvUByte:
|
||||
return PVScalarArrayPtr(new BasePVUByteArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVUByteArray(scalarArray));
|
||||
case pvUShort:
|
||||
return PVScalarArrayPtr(new BasePVUShortArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVUShortArray(scalarArray));
|
||||
case pvUInt:
|
||||
return PVScalarArrayPtr(new BasePVUIntArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVUIntArray(scalarArray));
|
||||
case pvULong:
|
||||
return PVScalarArrayPtr(new BasePVULongArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVULongArray(scalarArray));
|
||||
case pvFloat:
|
||||
return PVScalarArrayPtr(new BasePVFloatArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVFloatArray(scalarArray));
|
||||
case pvDouble:
|
||||
return PVScalarArrayPtr(new BasePVDoubleArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVDoubleArray(scalarArray));
|
||||
case pvString:
|
||||
return PVScalarArrayPtr(new BasePVStringArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVStringArray(scalarArray));
|
||||
}
|
||||
throw std::logic_error("PVDataCreate::createPVScalarArray should never get here");
|
||||
|
||||
|
||||
}
|
||||
|
||||
PVScalarArrayPtr PVDataCreate::createPVScalarArray(
|
||||
@@ -724,23 +714,63 @@ PVUnionPtr PVDataCreate::createPVUnion(PVUnionPtr const & unionToClone)
|
||||
return punion;
|
||||
}
|
||||
|
||||
// TODO not thread-safe (local static initializers)
|
||||
// TODO replace with non-locking singleton pattern
|
||||
PVDataCreatePtr PVDataCreate::getPVDataCreate()
|
||||
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*)
|
||||
{
|
||||
static PVDataCreatePtr pvDataCreate;
|
||||
static Mutex mutex;
|
||||
Lock xx(mutex);
|
||||
|
||||
if(pvDataCreate.get()==0) pvDataCreate = PVDataCreatePtr(new PVDataCreate());
|
||||
return pvDataCreate;
|
||||
try {
|
||||
pvfield_factory_s = new detail::pvfield_factory;
|
||||
}catch(std::exception& e){
|
||||
std::cerr<<"Error initializing getFieldCreate() : "<<e.what()<<"\n";
|
||||
}
|
||||
}
|
||||
|
||||
PVDataCreatePtr getPVDataCreate() {
|
||||
return PVDataCreate::getPVDataCreate();
|
||||
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;
|
||||
}
|
||||
|
||||
}}
|
||||
// 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)
|
||||
@@ -749,4 +779,3 @@ namespace std{
|
||||
return o << "nullptr";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#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;
|
||||
@@ -24,15 +25,20 @@ 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
|
||||
@@ -54,22 +60,16 @@ size_t PVField::getNumberFields() const
|
||||
}
|
||||
|
||||
|
||||
bool PVField::isImmutable() const {return immutable;}
|
||||
|
||||
void PVField::setImmutable() {immutable = true;}
|
||||
|
||||
const FieldConstPtr & PVField::getField() const {return field;}
|
||||
|
||||
PVStructure *PVField::getParent() const {return parent;}
|
||||
|
||||
void PVField::postPut()
|
||||
void PVField::postPut()
|
||||
{
|
||||
if(postHandler.get()!=NULL) postHandler->postPut();
|
||||
if(postHandler) postHandler->postPut();
|
||||
}
|
||||
|
||||
void PVField::setPostHandler(PostHandlerPtr const &handler)
|
||||
{
|
||||
if(postHandler.get()!=NULL) {
|
||||
if(postHandler) {
|
||||
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(PVField *fld=getParent(); fld; fld=fld->getParent())
|
||||
for(const PVField *fld=getParent(); fld; fld=fld->getParent())
|
||||
{
|
||||
if(fld->getFieldName().size()==0) break;
|
||||
ret = fld->getFieldName() + '.' + ret;
|
||||
@@ -184,62 +184,16 @@ void PVField::copy(const PVField& from)
|
||||
if(isImmutable())
|
||||
throw std::invalid_argument("destination is immutable");
|
||||
|
||||
if (getField()->getType() != from.getField()->getType())
|
||||
if (getField() != from.getField())
|
||||
throw std::invalid_argument("field types do not match");
|
||||
|
||||
switch(getField()->getType())
|
||||
{
|
||||
case scalar:
|
||||
{
|
||||
const PVScalar* fromS = static_cast<const PVScalar*>(&from);
|
||||
PVScalar* toS = static_cast<PVScalar*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
case scalarArray:
|
||||
{
|
||||
const PVScalarArray* fromS = static_cast<const PVScalarArray*>(&from);
|
||||
PVScalarArray* toS = static_cast<PVScalarArray*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
case structure:
|
||||
{
|
||||
const PVStructure* fromS = static_cast<const PVStructure*>(&from);
|
||||
PVStructure* toS = static_cast<PVStructure*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
case structureArray:
|
||||
{
|
||||
const PVStructureArray* fromS = static_cast<const PVStructureArray*>(&from);
|
||||
PVStructureArray* toS = static_cast<PVStructureArray*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
case union_:
|
||||
{
|
||||
const PVUnion* fromS = static_cast<const PVUnion*>(&from);
|
||||
PVUnion* toS = static_cast<PVUnion*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
case unionArray:
|
||||
{
|
||||
const PVUnionArray* fromS = static_cast<const PVUnionArray*>(&from);
|
||||
PVUnionArray* toS = static_cast<PVUnionArray*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw std::logic_error("PVField::copy unknown type");
|
||||
}
|
||||
}
|
||||
copyUnchecked(from);
|
||||
}
|
||||
|
||||
void PVField::copyUnchecked(const PVField& from)
|
||||
{
|
||||
assert(getField()==from.getField());
|
||||
|
||||
switch(getField()->getType())
|
||||
{
|
||||
case scalar:
|
||||
|
||||
@@ -24,25 +24,6 @@ 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),
|
||||
@@ -57,7 +38,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]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,9 +60,7 @@ PVStructure::PVStructure(StructureConstPtr const & structurePtr,
|
||||
}
|
||||
}
|
||||
|
||||
PVStructure::~PVStructure()
|
||||
{
|
||||
}
|
||||
PVStructure::~PVStructure() {}
|
||||
|
||||
void PVStructure::setImmutable()
|
||||
{
|
||||
@@ -93,60 +72,42 @@ void PVStructure::setImmutable()
|
||||
PVField::setImmutable();
|
||||
}
|
||||
|
||||
StructureConstPtr PVStructure::getStructure() const
|
||||
PVFieldPtr PVStructure::getSubFieldImpl(size_t fieldOffset, bool throws) const
|
||||
{
|
||||
return structurePtr;
|
||||
}
|
||||
const PVStructure *current = this;
|
||||
|
||||
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);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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::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
|
||||
PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
|
||||
{
|
||||
const PVStructure *parent = this;
|
||||
if(!name)
|
||||
@@ -154,7 +115,7 @@ PVField* 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 NULL;
|
||||
return PVFieldPtr();
|
||||
}
|
||||
const char *fullName = name;
|
||||
while(true) {
|
||||
@@ -170,7 +131,7 @@ PVField* PVStructure::getSubFieldImpl(const char *name, bool throws) const
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
return PVFieldPtr();
|
||||
}
|
||||
size_t N = sep-name;
|
||||
if(N==0)
|
||||
@@ -183,7 +144,7 @@ PVField* PVStructure::getSubFieldImpl(const char *name, bool throws) const
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
return PVFieldPtr();
|
||||
}
|
||||
|
||||
const PVFieldPtrArray& pvFields = parent->getPVFields();
|
||||
@@ -207,10 +168,10 @@ PVField* 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 NULL;
|
||||
return PVFieldPtr();
|
||||
}
|
||||
|
||||
if(*sep) {
|
||||
@@ -223,22 +184,36 @@ PVField* 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 NULL;
|
||||
return PVFieldPtr();
|
||||
}
|
||||
child = NULL;
|
||||
name = sep+1; // skip past '.'
|
||||
// loop around to new parent
|
||||
|
||||
} else {
|
||||
return child;
|
||||
return child->shared_from_this();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -272,7 +247,7 @@ void PVStructure::serialize(ByteBuffer *pbuffer,
|
||||
|
||||
size_t fieldsSize = pvFields.size();
|
||||
for(size_t i = 0; i<fieldsSize; i++) {
|
||||
PVFieldPtr pvField = pvFields[i];
|
||||
PVField* pvField = pvFields[i].get();
|
||||
offset = pvField->getFieldOffset();
|
||||
int32 inumberFields = static_cast<int32>(pvField->getNumberFields());
|
||||
next = pbitSet->nextSetBit(static_cast<uint32>(offset));
|
||||
@@ -286,8 +261,7 @@ void PVStructure::serialize(ByteBuffer *pbuffer,
|
||||
if(inumberFields==1) {
|
||||
pvField->serialize(pbuffer, pflusher);
|
||||
} else {
|
||||
PVStructurePtr pvStructure = std::tr1::static_pointer_cast<PVStructure>(pvField);
|
||||
pvStructure->serialize(pbuffer, pflusher, pbitSet);
|
||||
static_cast<PVStructure*>(pvField)->serialize(pbuffer, pflusher, pbitSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -333,22 +307,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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -76,14 +76,14 @@ void PVStructureArray::compress() {
|
||||
size_t newLength = 0;
|
||||
|
||||
for(size_t i=0; i<length; i++) {
|
||||
if(vec[i].get()!=NULL) {
|
||||
if(vec[i]) {
|
||||
newLength++;
|
||||
continue;
|
||||
}
|
||||
// find first non 0
|
||||
size_t notNull = 0;
|
||||
for(size_t j=i+1;j<length;j++) {
|
||||
if(vec[j].get()!=NULL) {
|
||||
if(vec[j]) {
|
||||
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;
|
||||
|
||||
@@ -23,9 +23,9 @@ using std::size_t;
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
|
||||
#define PVUNION_UNDEFINED_INDEX -1
|
||||
int32 PVUnion::UNDEFINED_INDEX = PVUNION_UNDEFINED_INDEX;
|
||||
const int32 PVUnion::UNDEFINED_INDEX = PVUNION_UNDEFINED_INDEX;
|
||||
|
||||
PVDataCreatePtr PVUnion::pvDataCreate(getPVDataCreate());
|
||||
|
||||
@@ -40,24 +40,7 @@ PVUnion::PVUnion(UnionConstPtr const & unionPtr)
|
||||
|
||||
#undef PVUNION_UNDEFINED_INDEX
|
||||
|
||||
PVUnion::~PVUnion()
|
||||
{
|
||||
}
|
||||
|
||||
UnionConstPtr PVUnion::getUnion() const
|
||||
{
|
||||
return unionPtr;
|
||||
}
|
||||
|
||||
PVFieldPtr PVUnion::get() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
int32 PVUnion::getSelectedIndex() const
|
||||
{
|
||||
return selector;
|
||||
}
|
||||
PVUnion::~PVUnion() {}
|
||||
|
||||
string PVUnion::getSelectedFieldName() const
|
||||
{
|
||||
@@ -70,8 +53,11 @@ 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)
|
||||
if (selector == index && !variant)
|
||||
return value;
|
||||
|
||||
if (index == UNDEFINED_INDEX)
|
||||
@@ -80,9 +66,7 @@ PVFieldPtr PVUnion::select(int32 index)
|
||||
value.reset();
|
||||
return value;
|
||||
}
|
||||
else if (variant)
|
||||
throw std::invalid_argument("index out of bounds");
|
||||
else if (index < 0 || index > static_cast<int32>(unionPtr->getFields().size()))
|
||||
else if (index < 0 || size_t(index) >= unionPtr->getFields().size())
|
||||
throw std::invalid_argument("index out of bounds");
|
||||
|
||||
FieldConstPtr field = unionPtr->getField(index);
|
||||
@@ -91,18 +75,13 @@ 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);
|
||||
}
|
||||
|
||||
void PVUnion::set(PVFieldPtr const & value)
|
||||
{
|
||||
set(selector, value);
|
||||
return select(index);
|
||||
}
|
||||
|
||||
void PVUnion::set(int32 index, PVFieldPtr const & value)
|
||||
@@ -114,15 +93,15 @@ void PVUnion::set(int32 index, PVFieldPtr const & value)
|
||||
if (index == UNDEFINED_INDEX)
|
||||
{
|
||||
// for undefined index we accept only null values
|
||||
if (value.get())
|
||||
throw std::invalid_argument("non-null value for index == UNDEFINED_INDEX");
|
||||
if (value)
|
||||
throw std::invalid_argument("non-null value for index == UNDEFINED_INDEX");
|
||||
}
|
||||
else if (index < 0 || index > static_cast<int32>(unionPtr->getFields().size()))
|
||||
else if (index < 0 || size_t(index) >= unionPtr->getFields().size())
|
||||
throw std::invalid_argument("index out of bounds");
|
||||
|
||||
// value type must match
|
||||
if (value->getField() != unionPtr->getField(index))
|
||||
throw std::invalid_argument("selected field and its introspection data do not match");
|
||||
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");
|
||||
}
|
||||
|
||||
selector = index;
|
||||
@@ -136,7 +115,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
|
||||
@@ -157,7 +136,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);
|
||||
}
|
||||
}
|
||||
@@ -201,20 +180,20 @@ std::ostream& PVUnion::dumpValue(std::ostream& o) const
|
||||
{
|
||||
o << format::indent() << getUnion()->getID() << ' ' << getFieldName() << std::endl;
|
||||
{
|
||||
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());
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
return o;
|
||||
return o;
|
||||
}
|
||||
|
||||
void PVUnion::copy(const PVUnion& from)
|
||||
@@ -231,7 +210,7 @@ void PVUnion::copy(const PVUnion& from)
|
||||
void PVUnion::copyUnchecked(const PVUnion& from)
|
||||
{
|
||||
|
||||
PVFieldPtr fromValue = from.get();
|
||||
const PVField::const_shared_pointer& fromValue = from.get();
|
||||
if (from.getUnion()->isVariant())
|
||||
{
|
||||
if (fromValue.get() == 0)
|
||||
|
||||
@@ -76,14 +76,14 @@ void PVUnionArray::compress() {
|
||||
size_t newLength = 0;
|
||||
|
||||
for(size_t i=0; i<length; i++) {
|
||||
if(vec[i].get()!=NULL) {
|
||||
if(vec[i]) {
|
||||
newLength++;
|
||||
continue;
|
||||
}
|
||||
// find first non 0
|
||||
size_t notNull = 0;
|
||||
for(size_t j=i+1;j<length;j++) {
|
||||
if(vec[j].get()!=NULL) {
|
||||
if(vec[j]) {
|
||||
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;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <stdexcept>
|
||||
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/lock.h>
|
||||
@@ -20,34 +21,87 @@
|
||||
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")
|
||||
{}
|
||||
:fieldCreate(getFieldCreate())
|
||||
,notImplemented("not implemented")
|
||||
,valueFieldName("value")
|
||||
|
||||
void StandardField::init()
|
||||
{
|
||||
createAlarm();
|
||||
createTimeStamp();
|
||||
createDisplay();
|
||||
createControl();
|
||||
createBooleanAlarm();
|
||||
createByteAlarm();
|
||||
createShortAlarm();
|
||||
createIntAlarm();
|
||||
createLongAlarm();
|
||||
createUByteAlarm();
|
||||
createUShortAlarm();
|
||||
createUIntAlarm();
|
||||
createULongAlarm();
|
||||
createFloatAlarm();
|
||||
createDoubleAlarm();
|
||||
createEnumeratedAlarm();
|
||||
}
|
||||
,alarmField(FieldBuilder::begin()
|
||||
->setId("alarm_t")
|
||||
->add("severity", pvInt)
|
||||
->add("status", pvInt)
|
||||
->add("message", pvString)
|
||||
->createStructure())
|
||||
|
||||
,timeStampField(FieldBuilder::begin()
|
||||
->setId("time_t")
|
||||
->add("secondsPastEpoch", pvLong)
|
||||
->add("nanoseconds", pvInt)
|
||||
->add("userTag", pvInt)
|
||||
->createStructure())
|
||||
|
||||
,displayField(FieldBuilder::begin()
|
||||
->setId("display_t")
|
||||
->add("limitLow", pvDouble)
|
||||
->add("limitHigh", pvDouble)
|
||||
->add("description", pvString)
|
||||
->add("format", pvString)
|
||||
->add("units", pvString)
|
||||
->createStructure())
|
||||
|
||||
,controlField(FieldBuilder::begin()
|
||||
->setId("control_t")
|
||||
->add("limitLow", pvDouble)
|
||||
->add("limitHigh", pvDouble)
|
||||
->add("minStep", pvDouble)
|
||||
->createStructure())
|
||||
|
||||
,booleanAlarmField(FieldBuilder::begin()
|
||||
->setId("valueAlarm_t")
|
||||
->add("active", pvBoolean)
|
||||
->add("falseSeverity", pvInt)
|
||||
->add("trueSeverity", pvInt)
|
||||
->add("changeStateSeverity", pvInt)
|
||||
->createStructure())
|
||||
|
||||
,byteAlarmField(buildValueAlarm(pvByte))
|
||||
,shortAlarmField(buildValueAlarm(pvShort))
|
||||
,intAlarmField(buildValueAlarm(pvInt))
|
||||
,longAlarmField(buildValueAlarm(pvLong))
|
||||
,ubyteAlarmField(buildValueAlarm(pvUByte))
|
||||
,ushortAlarmField(buildValueAlarm(pvUShort))
|
||||
,uintAlarmField(buildValueAlarm(pvUInt))
|
||||
,ulongAlarmField(buildValueAlarm(pvULong))
|
||||
,floatAlarmField(buildValueAlarm(pvFloat))
|
||||
,doubleAlarmField(buildValueAlarm(pvDouble))
|
||||
|
||||
,enumeratedAlarmField(FieldBuilder::begin()
|
||||
->setId("valueAlarm_t")
|
||||
->add("active", pvBoolean)
|
||||
->add("stateSeverity", pvInt)
|
||||
->add("changeStateSeverity", pvInt)
|
||||
->createStructure())
|
||||
{}
|
||||
|
||||
StandardField::~StandardField(){}
|
||||
|
||||
@@ -69,8 +123,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;
|
||||
@@ -103,7 +157,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) {
|
||||
@@ -145,361 +199,6 @@ StructureConstPtr StandardField::createProperties(string id,FieldConstPtr field,
|
||||
return fieldCreate->createStructure(id,names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createAlarm() {
|
||||
size_t num = 3;
|
||||
FieldConstPtrArray fields(num);
|
||||
StringArray names(num);
|
||||
names[0] = "severity";
|
||||
names[1] = "status";
|
||||
names[2] = "message";
|
||||
fields[0] = fieldCreate->createScalar(pvInt);
|
||||
fields[1] = fieldCreate->createScalar(pvInt);
|
||||
fields[2] = fieldCreate->createScalar(pvString);
|
||||
alarmField = fieldCreate->createStructure("alarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createTimeStamp() {
|
||||
size_t num = 3;
|
||||
FieldConstPtrArray fields(num);
|
||||
StringArray names(num);
|
||||
names[0] = "secondsPastEpoch";
|
||||
names[1] = "nanoseconds";
|
||||
names[2] = "userTag";
|
||||
fields[0] = fieldCreate->createScalar(pvLong);
|
||||
fields[1] = fieldCreate->createScalar(pvInt);
|
||||
fields[2] = fieldCreate->createScalar(pvInt);
|
||||
timeStampField = fieldCreate->createStructure("time_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createDisplay() {
|
||||
size_t num = 5;
|
||||
FieldConstPtrArray fields(num);
|
||||
StringArray names(num);
|
||||
names[0] = "limitLow";
|
||||
names[1] = "limitHigh";
|
||||
names[2] = "description";
|
||||
names[3] = "format";
|
||||
names[4] = "units";
|
||||
fields[0] = fieldCreate->createScalar(pvDouble);
|
||||
fields[1] = fieldCreate->createScalar(pvDouble);
|
||||
fields[2] = fieldCreate->createScalar(pvString);
|
||||
fields[3] = fieldCreate->createScalar(pvString);
|
||||
fields[4] = fieldCreate->createScalar(pvString);
|
||||
displayField = fieldCreate->createStructure("display_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createControl() {
|
||||
size_t num = 3;
|
||||
FieldConstPtrArray fields(num);
|
||||
StringArray names(num);
|
||||
names[0] = "limitLow";
|
||||
names[1] = "limitHigh";
|
||||
names[2] = "minStep";
|
||||
fields[0] = fieldCreate->createScalar(pvDouble);
|
||||
fields[1] = fieldCreate->createScalar(pvDouble);
|
||||
fields[2] = fieldCreate->createScalar(pvDouble);
|
||||
controlField = fieldCreate->createStructure("control_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createBooleanAlarm() {
|
||||
size_t numFields = 4;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "falseSeverity";
|
||||
names[2] = "trueSeverity";
|
||||
names[3] = "changeStateSeverity";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvInt);
|
||||
fields[2] = fieldCreate->createScalar(pvInt);
|
||||
fields[3] = fieldCreate->createScalar(pvInt);
|
||||
booleanAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createByteAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvByte);
|
||||
fields[2] = fieldCreate->createScalar(pvByte);
|
||||
fields[3] = fieldCreate->createScalar(pvByte);
|
||||
fields[4] = fieldCreate->createScalar(pvByte);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvByte);
|
||||
byteAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createShortAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvShort);
|
||||
fields[2] = fieldCreate->createScalar(pvShort);
|
||||
fields[3] = fieldCreate->createScalar(pvShort);
|
||||
fields[4] = fieldCreate->createScalar(pvShort);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvShort);
|
||||
shortAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createIntAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvInt);
|
||||
fields[2] = fieldCreate->createScalar(pvInt);
|
||||
fields[3] = fieldCreate->createScalar(pvInt);
|
||||
fields[4] = fieldCreate->createScalar(pvInt);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvInt);
|
||||
intAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createLongAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvLong);
|
||||
fields[2] = fieldCreate->createScalar(pvLong);
|
||||
fields[3] = fieldCreate->createScalar(pvLong);
|
||||
fields[4] = fieldCreate->createScalar(pvLong);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvLong);
|
||||
longAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createUByteAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvUByte);
|
||||
fields[2] = fieldCreate->createScalar(pvUByte);
|
||||
fields[3] = fieldCreate->createScalar(pvUByte);
|
||||
fields[4] = fieldCreate->createScalar(pvUByte);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvUByte);
|
||||
ubyteAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createUShortAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvUShort);
|
||||
fields[2] = fieldCreate->createScalar(pvUShort);
|
||||
fields[3] = fieldCreate->createScalar(pvUShort);
|
||||
fields[4] = fieldCreate->createScalar(pvUShort);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvUShort);
|
||||
ushortAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createUIntAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvUInt);
|
||||
fields[2] = fieldCreate->createScalar(pvUInt);
|
||||
fields[3] = fieldCreate->createScalar(pvUInt);
|
||||
fields[4] = fieldCreate->createScalar(pvUInt);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvUInt);
|
||||
uintAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createULongAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvULong);
|
||||
fields[2] = fieldCreate->createScalar(pvULong);
|
||||
fields[3] = fieldCreate->createScalar(pvULong);
|
||||
fields[4] = fieldCreate->createScalar(pvULong);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvULong);
|
||||
ulongAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createFloatAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvFloat);
|
||||
fields[2] = fieldCreate->createScalar(pvFloat);
|
||||
fields[3] = fieldCreate->createScalar(pvFloat);
|
||||
fields[4] = fieldCreate->createScalar(pvFloat);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvFloat);
|
||||
floatAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createDoubleAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvDouble);
|
||||
fields[2] = fieldCreate->createScalar(pvDouble);
|
||||
fields[3] = fieldCreate->createScalar(pvDouble);
|
||||
fields[4] = fieldCreate->createScalar(pvDouble);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvDouble);
|
||||
doubleAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createEnumeratedAlarm() {
|
||||
size_t numFields = 3;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "stateSeverity";
|
||||
names[2] = "changeStateSeverity";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalarArray(pvInt);
|
||||
fields[2] = fieldCreate->createScalar(pvInt);
|
||||
enumeratedAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
|
||||
StructureConstPtr StandardField::scalar(
|
||||
ScalarType type,string const &properties)
|
||||
{
|
||||
@@ -564,104 +263,21 @@ StructureConstPtr StandardField::enumerated(string const &properties)
|
||||
return createProperties("epics:nt/NTEnum:1.0",field,properties);
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::alarm()
|
||||
static StandardFieldPtr *stdFieldGbl;
|
||||
|
||||
static epicsThreadOnceId stdFieldGblOnce = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
void StandardField::once(void*)
|
||||
{
|
||||
return alarmField;
|
||||
stdFieldGbl = new StandardFieldPtr;
|
||||
stdFieldGbl->reset(new StandardField);
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::timeStamp()
|
||||
const StandardFieldPtr &StandardField::getStandardField()
|
||||
{
|
||||
return timeStampField;
|
||||
}
|
||||
epicsThreadOnce(&stdFieldGblOnce, &StandardField::once, 0);
|
||||
|
||||
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();
|
||||
return *stdFieldGbl;
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
StandardPVField::StandardPVField()
|
||||
: standardField(getStandardField()),
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace ScalarTypeFunc {
|
||||
if(type>=pvBoolean && type<=pvDouble) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static const char* names[] = {
|
||||
"boolean",
|
||||
"byte", "short", "int", "long",
|
||||
|
||||
@@ -3,42 +3,549 @@
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#if defined(__linux__) || defined(__APPLE__)
|
||||
#include <unistd.h>
|
||||
#define HAVE_ISATTY
|
||||
#endif
|
||||
|
||||
#include <deque>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvIntrospect.h>
|
||||
#include <epicsTime.h>
|
||||
#include <epicsString.h>
|
||||
|
||||
using std::string;
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/bitSet.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/json.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
namespace format
|
||||
{
|
||||
static int indent_index = std::ios_base::xalloc();
|
||||
static int indent_index = std::ios_base::xalloc();
|
||||
|
||||
long& indent_value(std::ios_base& ios)
|
||||
{
|
||||
return ios.iword(indent_index);
|
||||
long& indent_value(std::ios_base& ios)
|
||||
{
|
||||
return ios.iword(indent_index);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, indent_level const& indent)
|
||||
{
|
||||
indent_value(os) = indent.level;
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, indent const&)
|
||||
{
|
||||
long il = indent_value(os);
|
||||
for(long i=0, spaces = il * 4; i<spaces; i++)
|
||||
os.put(' ');
|
||||
return os;
|
||||
}
|
||||
|
||||
array_at_internal operator<<(std::ostream& str, array_at const& manip)
|
||||
{
|
||||
return array_at_internal(manip.index, str);
|
||||
}
|
||||
}
|
||||
|
||||
}} //epics::pvData
|
||||
namespace {
|
||||
using namespace epics::pvData;
|
||||
|
||||
bool useEscapes(std::ostream& strm) {
|
||||
FILE *fp = 0;
|
||||
// TODO: a better way to do this...
|
||||
if(&std::cout==&strm)
|
||||
fp = stdout;
|
||||
if(&std::cerr==&strm)
|
||||
fp = stderr;
|
||||
if(!fp) return false;
|
||||
#ifdef HAVE_ISATTY
|
||||
// check $TERM as well?
|
||||
return isatty(fileno(fp))==1;
|
||||
#else
|
||||
// TODO: in theory windows 10 can be made to understand escapes as well
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void printAlarmTx(std::ostream& strm, const PVStructure& sub)
|
||||
{
|
||||
PVScalar::const_shared_pointer pvSeverity(sub.getSubField<PVInt>("severity"));
|
||||
PVScalar::const_shared_pointer pvStatus(sub.getSubField<PVInt>("status"));
|
||||
PVString::const_shared_pointer pvMessage(sub.getSubField<PVString>("message"));
|
||||
|
||||
switch(pvSeverity ? pvSeverity->getAs<int32>() : 0) {
|
||||
case 0: return; // nothing more to do here...
|
||||
case 1: strm<<"MINOR "; break;
|
||||
case 2: strm<<"MAJOR "; break;
|
||||
case 3: strm<<"INVALID "; break;
|
||||
case 4: strm<<"UNDEFINED "; break;
|
||||
default: strm<<pvSeverity->getAs<int32>()<<' ';
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, indent_level const& indent)
|
||||
{
|
||||
indent_value(os) = indent.level;
|
||||
return os;
|
||||
}
|
||||
switch(pvStatus ? pvStatus->getAs<int32>() : 0) {
|
||||
case 0: break;
|
||||
case 1: strm<<"DEVICE "; break;
|
||||
case 2: strm<<"DRIVER "; break;
|
||||
case 3: strm<<"RECORD "; break;
|
||||
case 4: strm<<"DB "; break;
|
||||
case 5: strm<<"CONF "; break;
|
||||
case 6: strm<<"UNDEFINED "; break;
|
||||
case 7: strm<<"CLIENT "; break;
|
||||
default: strm<<pvStatus->getAs<int32>()<<' ';
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, indent const&)
|
||||
{
|
||||
long il = indent_value(os);
|
||||
std::size_t spaces = static_cast<std::size_t>(il) * 4;
|
||||
return os << string(spaces, ' ');
|
||||
}
|
||||
if(pvMessage && !pvMessage->get().empty())
|
||||
strm<<pvMessage->get()<<' ';
|
||||
}
|
||||
|
||||
array_at_internal operator<<(std::ostream& str, array_at const& manip)
|
||||
{
|
||||
return array_at_internal(manip.index, str);
|
||||
}
|
||||
};
|
||||
|
||||
}}
|
||||
void printAlarmT(std::ostream& strm, const PVStructure& top)
|
||||
{
|
||||
PVStructure::const_shared_pointer sub(top.getSubField<PVStructure>("alarm"));
|
||||
if(sub)
|
||||
printAlarmTx(strm, *sub);
|
||||
}
|
||||
|
||||
void printTimeTx(std::ostream& strm, const PVStructure& tsubop)
|
||||
{
|
||||
char timeText[32];
|
||||
epicsTimeStamp epicsTS;
|
||||
|
||||
PVScalar::const_shared_pointer secf(tsubop.getSubField<PVScalar>("secondsPastEpoch")),
|
||||
nsecf(tsubop.getSubField<PVScalar>("nanoseconds")),
|
||||
tagf(tsubop.getSubField<PVScalar>("userTag"));
|
||||
|
||||
epicsTS.secPastEpoch = secf ? secf->getAs<int64>() : 0;
|
||||
epicsTS.nsec = nsecf ? nsecf->getAs<int32>() : 0;
|
||||
|
||||
if(epicsTS.secPastEpoch > POSIX_TIME_AT_EPICS_EPOCH)
|
||||
epicsTS.secPastEpoch -= POSIX_TIME_AT_EPICS_EPOCH;
|
||||
else
|
||||
epicsTS.secPastEpoch = 0;
|
||||
|
||||
epicsTimeToStrftime(timeText, sizeof(timeText), "%Y-%m-%d %H:%M:%S.%03f", &epicsTS);
|
||||
strm <<std::setw(24) <<std::left <<timeText <<' ';
|
||||
if (tagf) {
|
||||
int64 tagv = tagf->getAs<int64>();
|
||||
if(tagv)
|
||||
strm << tagv << ' ';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void printTimeT(std::ostream& strm, const PVStructure& top)
|
||||
{
|
||||
PVStructure::const_shared_pointer sub(top.getSubField<PVStructure>("timeStamp"));
|
||||
if(sub)
|
||||
printTimeTx(strm, *sub);
|
||||
}
|
||||
|
||||
bool printEnumT(std::ostream& strm, const PVStructure& top, bool fromtop)
|
||||
{
|
||||
PVStructure::const_shared_pointer value;
|
||||
if(fromtop) {
|
||||
value = top.getSubField<PVStructure>("value");
|
||||
} else {
|
||||
value = std::tr1::static_pointer_cast<const PVStructure>(top.shared_from_this());
|
||||
}
|
||||
PVScalar::const_shared_pointer idx(value->getSubField<PVScalar>("index"));
|
||||
PVStringArray::const_shared_pointer choices(value->getSubField<PVStringArray>("choices"));
|
||||
if(!idx || !choices) return false;
|
||||
|
||||
if(fromtop) {
|
||||
strm<<format::indent();
|
||||
printTimeT(strm, top);
|
||||
printAlarmT(strm, top);
|
||||
}
|
||||
|
||||
PVStringArray::const_svector ch(choices->view());
|
||||
uint32 I = idx->getAs<uint32>();
|
||||
strm<<'('<<I<<')';
|
||||
if(I>=ch.size()) {
|
||||
strm<<" <undefined>";
|
||||
} else {
|
||||
strm<<' '<<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;
|
||||
|
||||
{
|
||||
const FieldConstPtrArray& fields = cf->getStructure()->getFields();
|
||||
if(fields.empty()) return false;
|
||||
for(size_t i=0, N=fields.size(); i<N; i++) {
|
||||
if(fields[i]->getType()!=scalarArray)
|
||||
return false; // cowardly refusing to handle anything else...
|
||||
}
|
||||
}
|
||||
|
||||
// maybe output a line with meta-data
|
||||
{
|
||||
bool havets = !!top.getSubField("timeStamp");
|
||||
bool haveal = !!top.getSubField("alarm");
|
||||
if(havets || haveal)
|
||||
strm<<format::indent();
|
||||
if(havets) {
|
||||
printTimeT(strm, top);
|
||||
strm<<' ';
|
||||
}
|
||||
if(haveal) {
|
||||
printAlarmT(strm, top);
|
||||
strm<<' ';
|
||||
}
|
||||
strm<<'\n';
|
||||
}
|
||||
|
||||
PVStringArray::svector labels;
|
||||
{
|
||||
PVStringArray::const_shared_pointer lf(top.getSubField<PVStringArray>("labels"));
|
||||
if(lf) {
|
||||
PVStringArray::const_svector L(lf->view());
|
||||
labels = thaw(L);
|
||||
}
|
||||
}
|
||||
|
||||
const PVFieldPtrArray& columns = cf->getPVFields();
|
||||
std::vector<shared_vector<std::string> > coldat(columns.size());
|
||||
|
||||
std::vector<size_t> widths(columns.size());
|
||||
labels.reserve(columns.size());
|
||||
|
||||
size_t nrows = size_t(-1);
|
||||
|
||||
for(size_t i=0, N=columns.size(); i<N; i++) {
|
||||
if(i>=labels.size()) {
|
||||
labels.push_back(cf->getStructure()->getFieldName(i));
|
||||
} else {
|
||||
csvEscape(labels[i]);
|
||||
}
|
||||
widths[i] = labels[i].size();
|
||||
|
||||
{
|
||||
shared_vector<const std::string> ro;
|
||||
static_cast<const PVScalarArray*>(columns[i].get())->getAs(ro);
|
||||
coldat[i] = thaw(ro);
|
||||
}
|
||||
|
||||
// truncate if some columns are longer than others
|
||||
nrows = std::min(nrows, coldat[i].size());
|
||||
|
||||
for(size_t j=0, M=coldat[i].size(); j<M; j++) {
|
||||
csvEscape(coldat[i][j]);
|
||||
widths[i] = std::max(widths[i], coldat[i][j].size());
|
||||
}
|
||||
}
|
||||
|
||||
// output header line
|
||||
strm<<format::indent();
|
||||
for(size_t c=0, N=coldat.size(); c<N; c++) {
|
||||
strm<<std::setw(widths[c])<<std::right<<labels[c];
|
||||
if(c+1!=N) {
|
||||
strm<<' ';
|
||||
}
|
||||
}
|
||||
strm<<'\n';
|
||||
|
||||
for(size_t r=0; r<nrows; r++) {
|
||||
strm<<format::indent();
|
||||
for(size_t c=0, N=coldat.size(); c<N; c++) {
|
||||
strm<<std::setw(widths[c])<<std::right<<coldat[c][r];
|
||||
if(c+1!=N)
|
||||
strm<<' ';
|
||||
}
|
||||
strm<<'\n';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void expandBS(const PVStructure& top, BitSet& mask, bool parents) {
|
||||
if(mask.get(0)) { // special handling because getSubField(0) not allowed
|
||||
// wildcard
|
||||
for(size_t idx=1, N=top.getNumberFields(); idx<N; idx++) {
|
||||
mask.set(idx);
|
||||
}
|
||||
|
||||
} else {
|
||||
for(int32 idx = mask.nextSetBit(0), N=top.getNumberFields(); idx>=0 && idx<N; idx=mask.nextSetBit(idx+1)) {
|
||||
PVField::const_shared_pointer fld = top.getSubFieldT(idx);
|
||||
|
||||
// look forward and mark all children
|
||||
for(size_t i=idx+1, N=fld->getNextFieldOffset(); i<N; i++)
|
||||
mask.set(i);
|
||||
|
||||
if(parents) {
|
||||
// look back and mark all parents
|
||||
// we've already stepped past all parents so siblings will not be automatically marked
|
||||
for(const PVStructure *parent = fld->getParent(); parent; parent = parent->getParent()) {
|
||||
mask.set(parent->getFieldOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
void printRaw(std::ostream& strm, const PVStructure::Formatter& format, const PVStructure& cur)
|
||||
{
|
||||
typedef std::deque<std::pair<const PVField*, size_t> > todo_t;
|
||||
todo_t todo;
|
||||
BitSet show, highlight;
|
||||
|
||||
const long orig_indent = format::indent_value(strm);
|
||||
|
||||
{
|
||||
if(format.xshow)
|
||||
show = *format.xshow;
|
||||
else
|
||||
show.set(0);
|
||||
|
||||
if(format.xhighlight)
|
||||
highlight = *format.xhighlight;
|
||||
|
||||
expandBS(format.xtop, show, true);
|
||||
expandBS(format.xtop, highlight, false);
|
||||
highlight &= show; // can't highlight hidden fields (paranoia)
|
||||
}
|
||||
|
||||
if(!show.get(0)) return; // nothing to do here...
|
||||
|
||||
todo.push_front(std::make_pair(&format.xtop, orig_indent));
|
||||
|
||||
while(!todo.empty()) {
|
||||
todo_t::value_type cur(todo.front());
|
||||
todo.pop_front();
|
||||
|
||||
format::indent_value(strm) = cur.second;
|
||||
|
||||
bool hl = highlight.get(cur.first->getFieldOffset()) && format.xmode==PVStructure::Formatter::ANSI;
|
||||
if(hl)
|
||||
strm<<"\x1b[1m"; // Bold
|
||||
|
||||
switch(cur.first->getField()->getType()) {
|
||||
case structure: {
|
||||
const PVStructure* str = static_cast<const PVStructure*>(cur.first);
|
||||
|
||||
const PVFieldPtrArray& flds = str->getPVFields();
|
||||
|
||||
for(PVFieldPtrArray::const_reverse_iterator it(flds.rbegin()), end(flds.rend()); it!=end; ++it) {
|
||||
const PVField *fld = (*it).get();
|
||||
if(!show.get(fld->getFieldOffset())) continue;
|
||||
|
||||
todo.push_front(std::make_pair(fld, cur.second+1));
|
||||
}
|
||||
|
||||
std::string id(cur.first->getField()->getID());
|
||||
|
||||
strm<<format::indent()<<id<<' '<<cur.first->getFieldName();
|
||||
if(id=="alarm_t") {
|
||||
strm.put(' ');
|
||||
printAlarmTx(strm, *str);
|
||||
} else if(id=="time_t") {
|
||||
strm.put(' ');
|
||||
printTimeTx(strm, *str);
|
||||
} else if(id=="enum_t") {
|
||||
strm.put(' ');
|
||||
printEnumT(strm, *str, false);
|
||||
}
|
||||
strm.put('\n');
|
||||
}
|
||||
break;
|
||||
case scalar:
|
||||
case scalarArray:
|
||||
strm<<format::indent()<<cur.first->getField()->getID()<<' '<<cur.first->getFieldName()
|
||||
<<' '<<*cur.first
|
||||
<<'\n';
|
||||
break;
|
||||
case structureArray:
|
||||
case union_:
|
||||
case unionArray:
|
||||
strm<<*cur.first;
|
||||
break;
|
||||
}
|
||||
|
||||
if(hl)
|
||||
strm<<"\x1b[0m"; // reset
|
||||
}
|
||||
|
||||
format::indent_value(strm) = orig_indent;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& strm, const PVStructure::Formatter& format)
|
||||
{
|
||||
if(format.xfmt==PVStructure::Formatter::JSON) {
|
||||
JSONPrintOptions opts;
|
||||
opts.multiLine = false;
|
||||
printJSON(strm, format.xtop, format.xshow ? *format.xshow : BitSet().set(0), opts);
|
||||
strm<<'\n';
|
||||
return strm;
|
||||
|
||||
} else if(format.xfmt==PVStructure::Formatter::NT) {
|
||||
std::string id(format.xtop.getStructure()->getID()),
|
||||
idprefix(id.substr(0, id.find_first_of('.')));
|
||||
|
||||
// NTTable
|
||||
if(idprefix=="epics:nt/NTTable:1") {
|
||||
if(printTable(strm, format.xtop))
|
||||
return strm;
|
||||
} else {
|
||||
//NTScalar, NTScalarArray, NTEnum, or anything with '.value'
|
||||
|
||||
PVField::const_shared_pointer value(format.xtop.getSubField("value"));
|
||||
if(value) {
|
||||
switch(value->getField()->getType()) {
|
||||
case scalar:
|
||||
strm<<format::indent();
|
||||
printTimeT(strm, format.xtop);
|
||||
strm<<std::setprecision(6)<<*static_cast<const PVScalar*>(value.get())<<' ';
|
||||
printAlarmT(strm, format.xtop);
|
||||
strm<<'\n';
|
||||
return strm;
|
||||
|
||||
case scalarArray:
|
||||
strm<<format::indent();
|
||||
printTimeT(strm, format.xtop);
|
||||
printAlarmT(strm, format.xtop);
|
||||
strm<<std::setprecision(6)<<*static_cast<const PVScalarArray*>(value.get())<<'\n';
|
||||
return strm;
|
||||
|
||||
case structure:
|
||||
if(printEnumT(strm, format.xtop, true)) {
|
||||
strm<<'\n';
|
||||
return strm;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// fall through unhandled as Raw
|
||||
|
||||
PVStructure::Formatter format2(format);
|
||||
|
||||
if(format2.xmode==PVStructure::Formatter::Auto)
|
||||
format2.xmode = useEscapes(strm) ? PVStructure::Formatter::ANSI : PVStructure::Formatter::Plain;
|
||||
|
||||
printRaw(strm, format2, format.xtop);
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
static char hexdigit(char c) {
|
||||
c &= 0xf;
|
||||
if(c<9)
|
||||
return '0'+c;
|
||||
else
|
||||
return 'A'+c-10;
|
||||
}
|
||||
|
||||
escape::~escape() {}
|
||||
|
||||
std::string escape::str() const
|
||||
{
|
||||
std::ostringstream strm;
|
||||
strm<<(*this);
|
||||
return strm.str();
|
||||
}
|
||||
|
||||
epicsShareFunc
|
||||
std::ostream& operator<<(std::ostream& strm, const escape& Q)
|
||||
{
|
||||
for(size_t pos = 0, len = Q.orig.size(); pos < len; pos++) {
|
||||
const char C = Q.orig[pos];
|
||||
char quote = '\\', next;
|
||||
// compare me with epicsStrnEscapedFromRaw()
|
||||
switch(C) {
|
||||
case '\a': next = 'a'; break;
|
||||
case '\b': next = 'b'; break;
|
||||
case '\f': next = 'f'; break;
|
||||
case '\n': next = 'n'; break;
|
||||
case '\r': next = 'r'; break;
|
||||
case '\t': next = 't'; break;
|
||||
case '\v': next = 'v'; break;
|
||||
case '\\': next = '\\'; break;
|
||||
case '\'': next = '\''; break;
|
||||
case '\"': next = '\"'; if(Q.S==escape::CSV) quote = '"'; break;
|
||||
default:
|
||||
if(!isprint(C)) {
|
||||
// print three charator escape
|
||||
strm<<"\\x"<<hexdigit(C>>4)<<hexdigit(C);
|
||||
} else {
|
||||
// literal
|
||||
strm.put(C);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// print two charactor escape
|
||||
strm.put(quote);
|
||||
strm.put(next);
|
||||
}
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
@@ -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,4 +272,3 @@ void copy(
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
|
||||
10
src/json/Makefile
Normal file
10
src/json/Makefile
Normal file
@@ -0,0 +1,10 @@
|
||||
# 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
|
||||
280
src/json/parseany.cpp
Normal file
280
src/json/parseany.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* 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
|
||||
118
src/json/parsehelper.cpp
Normal file
118
src/json/parsehelper.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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
|
||||
346
src/json/parseinto.cpp
Normal file
346
src/json/parseinto.cpp
Normal file
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* 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
|
||||
231
src/json/print.cpp
Normal file
231
src/json/print.cpp
Normal file
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* 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
|
||||
148
src/json/pv/json.h
Normal file
148
src/json/pv/json.h
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -4,7 +4,6 @@ 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
|
||||
@@ -12,32 +11,29 @@ 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/localStaticLock.h
|
||||
INC += pv/debugPtr.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
|
||||
|
||||
161
src/misc/anyscalar.cpp
Normal file
161
src/misc/anyscalar.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
|
||||
#include <epicsAssert.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <shareLib.h>
|
||||
|
||||
#include "pv/anyscalar.h"
|
||||
|
||||
namespace epics {namespace pvData {
|
||||
|
||||
AnyScalar::AnyScalar(ScalarType type, const void *buf)
|
||||
{
|
||||
if(type==pvString) {
|
||||
new (_wrap.blob) std::string(*static_cast<const std::string*>(buf));
|
||||
|
||||
} else {
|
||||
memcpy(_wrap.blob, buf, ScalarTypeFunc::elementSize(type));
|
||||
}
|
||||
|
||||
_stype = type;
|
||||
}
|
||||
|
||||
AnyScalar::AnyScalar(const AnyScalar& o)
|
||||
:_stype(o._stype)
|
||||
{
|
||||
if(o._stype==pvString) {
|
||||
new (_wrap.blob) std::string(o._as<std::string>());
|
||||
} else if(o._stype!=(ScalarType)-1) {
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
}
|
||||
}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
AnyScalar::AnyScalar(AnyScalar&& o)
|
||||
:_stype(o._stype)
|
||||
{
|
||||
typedef std::string string;
|
||||
if(o._stype==pvString) {
|
||||
new (_wrap.blob) std::string();
|
||||
_as<std::string>() = std::move(o._as<std::string>());
|
||||
o._as<string>().~string();
|
||||
} else if(o._stype!=(ScalarType)-1) {
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
}
|
||||
o._stype = (ScalarType)-1;
|
||||
}
|
||||
#endif
|
||||
|
||||
void AnyScalar::clear() {
|
||||
if(_stype==pvString) {
|
||||
typedef std::string string;
|
||||
_as<string>().~string();
|
||||
}
|
||||
// other types need no cleanup
|
||||
_stype = (ScalarType)-1;
|
||||
}
|
||||
|
||||
void AnyScalar::swap(AnyScalar& o) {
|
||||
typedef std::string string;
|
||||
switch((int)_stype) {
|
||||
case -1:
|
||||
switch((int)o._stype) {
|
||||
case -1:
|
||||
// nil <-> nil
|
||||
break;
|
||||
case pvString:
|
||||
// nil <-> string
|
||||
new (_wrap.blob) std::string();
|
||||
_as<std::string>().swap(o._as<std::string>());
|
||||
o._as<std::string>().~string();
|
||||
break;
|
||||
default:
|
||||
// nil <-> non-string
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case pvString:
|
||||
switch((int)o._stype) {
|
||||
case -1:
|
||||
// string <-> nil
|
||||
new (o._wrap.blob) std::string();
|
||||
_as<std::string>().swap(o._as<std::string>());
|
||||
_as<std::string>().~string();
|
||||
break;
|
||||
case pvString:
|
||||
// string <-> string
|
||||
_as<std::string>().swap(o._as<std::string>());
|
||||
break;
|
||||
default: {
|
||||
// string <-> non-string
|
||||
std::string temp;
|
||||
temp.swap(_as<std::string>());
|
||||
|
||||
_as<std::string>().~string();
|
||||
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
|
||||
new (o._wrap.blob) std::string();
|
||||
temp.swap(o._as<std::string>());
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
switch((int)o._stype) {
|
||||
case -1:
|
||||
// non-string <-> nil
|
||||
memcpy(o._wrap.blob, _wrap.blob, sizeof(_largest_blob));
|
||||
break;
|
||||
case pvString: {
|
||||
// non-string <-> string
|
||||
std::string temp;
|
||||
temp.swap(o._as<std::string>());
|
||||
|
||||
o._as<std::string>().~string();
|
||||
|
||||
memcpy(o._wrap.blob, _wrap.blob, sizeof(_largest_blob));
|
||||
|
||||
new (_wrap.blob) std::string();
|
||||
temp.swap(_as<std::string>());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// non-string <-> non-string
|
||||
_largest_blob temp;
|
||||
memcpy(&temp, _wrap.blob, sizeof(_largest_blob));
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
memcpy(o._wrap.blob, &temp, sizeof(_largest_blob));
|
||||
// std::swap(o._wrap.blob, _wrap.blob); // gcc <=4.3 doesn't like this
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
std::swap(_stype, o._stype);
|
||||
}
|
||||
const void* AnyScalar::bufferUnsafe() const {
|
||||
if(_stype==pvString) {
|
||||
return as<std::string>().c_str();
|
||||
} else {
|
||||
return _wrap.blob;
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& strm, const AnyScalar& v)
|
||||
{
|
||||
switch(v.type()) {
|
||||
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case pv ## PVACODE: strm<<v._as<PVATYPE>(); break;
|
||||
#define CASE_REAL_INT64
|
||||
#define CASE_STRING
|
||||
#include "pv/typemap.h"
|
||||
#undef CASE
|
||||
#undef CASE_REAL_INT64
|
||||
#undef CASE_STRING
|
||||
default:
|
||||
strm<<"(nil)"; break;
|
||||
}
|
||||
return strm;
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
|
||||
#include <epicsMutex.h>
|
||||
|
||||
@@ -41,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)
|
||||
@@ -54,6 +55,19 @@ 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() {
|
||||
@@ -75,7 +89,7 @@ namespace epics { namespace pvData {
|
||||
ensureCapacity(wordIndex+1);
|
||||
}
|
||||
|
||||
void BitSet::flip(uint32 bitIndex) {
|
||||
BitSet& BitSet::flip(uint32 bitIndex) {
|
||||
|
||||
uint32 wordIdx = WORD_INDEX(bitIndex);
|
||||
expandTo(wordIdx);
|
||||
@@ -83,25 +97,27 @@ namespace epics { namespace pvData {
|
||||
words[wordIdx] ^= (((uint64)1) << WORD_OFFSET(bitIndex));
|
||||
|
||||
recalculateWordsInUse();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void BitSet::set(uint32 bitIndex) {
|
||||
BitSet& BitSet::set(uint32 bitIndex) {
|
||||
|
||||
uint32 wordIdx = WORD_INDEX(bitIndex);
|
||||
expandTo(wordIdx);
|
||||
|
||||
words[wordIdx] |= (((uint64)1) << WORD_OFFSET(bitIndex));
|
||||
return *this;
|
||||
}
|
||||
|
||||
void BitSet::clear(uint32 bitIndex) {
|
||||
BitSet& BitSet::clear(uint32 bitIndex) {
|
||||
|
||||
uint32 wordIdx = WORD_INDEX(bitIndex);
|
||||
if (wordIdx >= words.size())
|
||||
return;
|
||||
if (wordIdx < words.size()) {
|
||||
words[wordIdx] &= ~(((uint64)1) << WORD_OFFSET(bitIndex));
|
||||
|
||||
words[wordIdx] &= ~(((uint64)1) << WORD_OFFSET(bitIndex));
|
||||
|
||||
recalculateWordsInUse();
|
||||
recalculateWordsInUse();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void BitSet::set(uint32 bitIndex, bool value) {
|
||||
@@ -195,6 +211,20 @@ 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;
|
||||
@@ -297,7 +327,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]);
|
||||
|
||||
@@ -308,7 +338,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);
|
||||
|
||||
165
src/misc/debugPtr.cpp
Normal file
165
src/misc/debugPtr.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
|
||||
Event::~Event() {
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/* 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;
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -1,39 +0,0 @@
|
||||
/* localStaticLock.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mse
|
||||
*/
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/localStaticLock.h>
|
||||
|
||||
static int nifty_counter;
|
||||
static epics::pvData::Mutex* g_localStaticInitMutex;
|
||||
|
||||
epics::pvData::Mutex& getLocalStaticInitMutex()
|
||||
{
|
||||
return *g_localStaticInitMutex;
|
||||
}
|
||||
|
||||
|
||||
// The counter is initialized at load-time, i.e., before any of the static objects are initialized.
|
||||
MutexInitializer::MutexInitializer ()
|
||||
{
|
||||
if (0 == nifty_counter++)
|
||||
{
|
||||
// Initialize static members.
|
||||
g_localStaticInitMutex = new epics::pvData::Mutex();
|
||||
}
|
||||
}
|
||||
|
||||
MutexInitializer::~MutexInitializer ()
|
||||
{
|
||||
if (0 == --nifty_counter)
|
||||
{
|
||||
// Clean-up.
|
||||
delete g_localStaticInitMutex;
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
/* 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));
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
@@ -25,406 +25,6 @@ using std::string;
|
||||
#define NEED_LONGLONG
|
||||
#endif
|
||||
|
||||
#ifndef EPICS_VERSION_INT
|
||||
#define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P))
|
||||
#define EPICS_VERSION_INT VERSION_INT(EPICS_VERSION, EPICS_REVISION, EPICS_MODIFICATION, EPICS_PATCH_LEVEL)
|
||||
#endif
|
||||
|
||||
#if EPICS_VERSION_INT < VERSION_INT(3,15,0,1)
|
||||
/* These integer conversion primitives added to epicsStdlib.c in 3.15.0.1 */
|
||||
|
||||
#define S_stdlib_noConversion 1 /* No digits to convert */
|
||||
#define S_stdlib_extraneous 2 /* Extraneous characters */
|
||||
#define S_stdlib_underflow 3 /* Too small to represent */
|
||||
#define S_stdlib_overflow 4 /* Too large to represent */
|
||||
#define S_stdlib_badBase 5 /* Number base not supported */
|
||||
|
||||
static int
|
||||
epicsParseLong(const char *str, long *to, int base, char **units)
|
||||
{
|
||||
int c;
|
||||
char *endp;
|
||||
long value;
|
||||
|
||||
while ((c = *str) && isspace(c))
|
||||
++str;
|
||||
|
||||
errno = 0;
|
||||
value = strtol(str, &endp, base);
|
||||
|
||||
if (endp == str)
|
||||
return S_stdlib_noConversion;
|
||||
if (errno == EINVAL) /* Not universally supported */
|
||||
return S_stdlib_badBase;
|
||||
if (errno == ERANGE)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
while ((c = *endp) && isspace(c))
|
||||
++endp;
|
||||
if (c && !units)
|
||||
return S_stdlib_extraneous;
|
||||
|
||||
*to = value;
|
||||
if (units)
|
||||
*units = endp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseULong(const char *str, unsigned long *to, int base, char **units)
|
||||
{
|
||||
int c;
|
||||
char *endp;
|
||||
unsigned long value;
|
||||
|
||||
while ((c = *str) && isspace(c))
|
||||
++str;
|
||||
|
||||
errno = 0;
|
||||
value = strtoul(str, &endp, base);
|
||||
|
||||
if (endp == str)
|
||||
return S_stdlib_noConversion;
|
||||
if (errno == EINVAL) /* Not universally supported */
|
||||
return S_stdlib_badBase;
|
||||
if (errno == ERANGE)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
while ((c = *endp) && isspace(c))
|
||||
++endp;
|
||||
if (c && !units)
|
||||
return S_stdlib_extraneous;
|
||||
|
||||
*to = value;
|
||||
if (units)
|
||||
*units = endp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseDouble(const char *str, double *to, char **units)
|
||||
{
|
||||
int c;
|
||||
char *endp;
|
||||
double value;
|
||||
|
||||
while ((c = *str) && isspace(c))
|
||||
++str;
|
||||
|
||||
errno = 0;
|
||||
value = epicsStrtod(str, &endp);
|
||||
|
||||
if (endp == str)
|
||||
return S_stdlib_noConversion;
|
||||
if (errno == ERANGE)
|
||||
return (value == 0) ? S_stdlib_underflow : S_stdlib_overflow;
|
||||
|
||||
while ((c = *endp) && isspace(c))
|
||||
++endp;
|
||||
if (c && !units)
|
||||
return S_stdlib_extraneous;
|
||||
|
||||
*to = value;
|
||||
if (units)
|
||||
*units = endp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* These call the primitives */
|
||||
|
||||
static int
|
||||
epicsParseInt8(const char *str, epicsInt8 *to, int base, char **units)
|
||||
{
|
||||
long value;
|
||||
int status = epicsParseLong(str, &value, base, units);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (value < -0x80 || value > 0x7f)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = (epicsInt8)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseUInt8(const char *str, epicsUInt8 *to, int base, char **units)
|
||||
{
|
||||
unsigned long value;
|
||||
int status = epicsParseULong(str, &value, base, units);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (value > 0xff && value <= ~0xffUL)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = (epicsUInt8)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseInt16(const char *str, epicsInt16 *to, int base, char **units)
|
||||
{
|
||||
long value;
|
||||
int status = epicsParseLong(str, &value, base, units);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (value < -0x8000 || value > 0x7fff)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = (epicsInt16)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseUInt16(const char *str, epicsUInt16 *to, int base, char **units)
|
||||
{
|
||||
unsigned long value;
|
||||
int status = epicsParseULong(str, &value, base, units);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (value > 0xffff && value <= ~0xffffUL)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = (epicsUInt16)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseInt32(const char *str, epicsInt32 *to, int base, char **units)
|
||||
{
|
||||
long value;
|
||||
int status = epicsParseLong(str, &value, base, units);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
#if (LONG_MAX > 0x7fffffff)
|
||||
if (value < -0x80000000L || value > 0x7fffffffL)
|
||||
return S_stdlib_overflow;
|
||||
#endif
|
||||
|
||||
*to = (epicsInt32)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseUInt32(const char *str, epicsUInt32 *to, int base, char **units)
|
||||
{
|
||||
unsigned long value;
|
||||
int status = epicsParseULong(str, &value, base, units);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
#if (ULONG_MAX > 0xffffffff)
|
||||
if (value > 0xffffffffUL && value <= ~0xffffffffUL)
|
||||
return S_stdlib_overflow;
|
||||
#endif
|
||||
|
||||
*to = (epicsUInt32)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseFloat(const char *str, float *to, char **units)
|
||||
{
|
||||
double value, abs;
|
||||
int status = epicsParseDouble(str, &value, units);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
abs = fabs(value);
|
||||
if (value > 0 && abs <= FLT_MIN)
|
||||
return S_stdlib_underflow;
|
||||
if (finite(value) && abs >= FLT_MAX)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = (float)value;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Sometimes we have to provide our own copy of strtoll()
|
||||
#if defined(_WIN32) && !defined(_MINGW)
|
||||
// On Windows with MSVC, Base-3.15 provides strtoll()
|
||||
# define NEED_OLL_FUNCS (EPICS_VERSION_INT < VERSION_INT(3,15,0,1))
|
||||
#elif defined(vxWorks)
|
||||
// On VxWorks, Base-3.15 provides strtoll()
|
||||
# define NEED_OLL_FUNCS (EPICS_VERSION_INT < VERSION_INT(3,15,0,1))
|
||||
#else
|
||||
// Other architectures all provide strtoll()
|
||||
# define NEED_OLL_FUNCS 0
|
||||
#endif
|
||||
|
||||
#if defined(NEED_LONGLONG) && NEED_OLL_FUNCS
|
||||
static
|
||||
long long strtoll(const char *ptr, char ** endp, int base)
|
||||
{
|
||||
size_t inlen = strlen(ptr);
|
||||
long long result;
|
||||
unsigned char offset=0;
|
||||
|
||||
assert(base==0);
|
||||
|
||||
if(ptr[0]=='-')
|
||||
offset=1;
|
||||
|
||||
try {
|
||||
std::istringstream strm(ptr);
|
||||
|
||||
assert(strm.rdbuf()->in_avail()>=0
|
||||
&& inlen==(size_t)strm.rdbuf()->in_avail());
|
||||
|
||||
if(ptr[offset]=='0') {
|
||||
if(ptr[offset+1]=='x')
|
||||
strm >> std::hex;
|
||||
else
|
||||
strm >> std::oct;
|
||||
}
|
||||
|
||||
strm >> result;
|
||||
if(strm.fail())
|
||||
goto noconvert;
|
||||
|
||||
assert(strm.rdbuf()->in_avail()>=0
|
||||
&& inlen>=(size_t)strm.rdbuf()->in_avail());
|
||||
|
||||
size_t consumed = inlen - strm.rdbuf()->in_avail();
|
||||
*endp = (char*)ptr + consumed;
|
||||
|
||||
return result;
|
||||
|
||||
} catch(...) {
|
||||
goto noconvert;
|
||||
}
|
||||
|
||||
return result;
|
||||
noconvert:
|
||||
*endp = (char*)ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(vxWorks)
|
||||
/* The VxWorks version of std::istringstream >> uint64_t is buggy,
|
||||
* provide our own implementation
|
||||
*/
|
||||
static
|
||||
unsigned long long strtoull(const char *nptr, char **endptr, int base)
|
||||
{
|
||||
const char *s = nptr;
|
||||
unsigned long long acc;
|
||||
int c;
|
||||
unsigned long long cutoff;
|
||||
int neg = 0, any, cutlim;
|
||||
|
||||
do
|
||||
c = *s++;
|
||||
while (isspace(c));
|
||||
if (c == '-')
|
||||
{
|
||||
neg = 1;
|
||||
c = *s++;
|
||||
}
|
||||
else if (c == '+')
|
||||
c = *s++;
|
||||
if ((base == 0 || base == 16) &&
|
||||
c == '0' && (*s == 'x' || *s == 'X'))
|
||||
{
|
||||
c = s[1];
|
||||
s += 2;
|
||||
base = 16;
|
||||
}
|
||||
if (base == 0)
|
||||
base = c == '0' ? 8 : 10;
|
||||
|
||||
cutoff = (unsigned long long) UINT64_MAX / (unsigned long long) base;
|
||||
cutlim = (unsigned long long) UINT64_MAX % (unsigned long long) base;
|
||||
|
||||
for (acc = 0, any = 0;; c = *s++)
|
||||
{
|
||||
if (isdigit(c))
|
||||
c -= '0';
|
||||
else if (isalpha(c))
|
||||
c -= isupper(c) ? 'A' - 10 : 'a' - 10;
|
||||
else
|
||||
break;
|
||||
if (c >= base)
|
||||
break;
|
||||
if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
|
||||
any = -1;
|
||||
else
|
||||
{
|
||||
any = 1;
|
||||
acc *= base;
|
||||
acc += c;
|
||||
}
|
||||
}
|
||||
if (any < 0)
|
||||
{
|
||||
acc = UINT64_MAX;
|
||||
errno = ERANGE;
|
||||
}
|
||||
else if (neg)
|
||||
acc = -acc;
|
||||
if (endptr != 0)
|
||||
*endptr = any ? (char *) s - 1 : (char *) nptr;
|
||||
return (acc);
|
||||
}
|
||||
#else
|
||||
static
|
||||
unsigned long long strtoull(const char *ptr, char ** endp, int base)
|
||||
{
|
||||
size_t inlen = strlen(ptr);
|
||||
unsigned long long result;
|
||||
|
||||
assert(base==0);
|
||||
|
||||
try {
|
||||
std::istringstream strm(ptr);
|
||||
|
||||
assert(strm.rdbuf()->in_avail()>=0
|
||||
&& inlen==(size_t)strm.rdbuf()->in_avail());
|
||||
|
||||
if(ptr[0]=='0') {
|
||||
if(ptr[1]=='x')
|
||||
strm >> std::hex;
|
||||
else
|
||||
strm >> std::oct;
|
||||
}
|
||||
|
||||
strm >> result;
|
||||
if(strm.fail())
|
||||
goto noconvert;
|
||||
|
||||
assert(strm.rdbuf()->in_avail()>=0
|
||||
&& inlen>=(size_t)strm.rdbuf()->in_avail());
|
||||
|
||||
size_t consumed = inlen - strm.rdbuf()->in_avail();
|
||||
*endp = (char*)ptr + consumed;
|
||||
|
||||
return result;
|
||||
|
||||
} catch(...) {
|
||||
goto noconvert;
|
||||
}
|
||||
|
||||
return result;
|
||||
noconvert:
|
||||
*endp = (char*)ptr;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* do we need long long? */
|
||||
#ifdef NEED_LONGLONG
|
||||
static int
|
||||
@@ -507,20 +107,20 @@ void handleParseError(int err)
|
||||
|
||||
namespace epics { namespace pvData { namespace detail {
|
||||
|
||||
void parseToPOD(const string & in, boolean *out)
|
||||
void parseToPOD(const char* in, boolean *out)
|
||||
{
|
||||
if(epicsStrCaseCmp(in.c_str(),"true")==0)
|
||||
if(epicsStrCaseCmp(in,"true")==0)
|
||||
*out = 1;
|
||||
else if(epicsStrCaseCmp(in.c_str(),"false")==0)
|
||||
else if(epicsStrCaseCmp(in,"false")==0)
|
||||
*out = 0;
|
||||
else
|
||||
throw std::runtime_error("parseToPOD: string no match true/false");
|
||||
}
|
||||
|
||||
#define INTFN(T, S) \
|
||||
void parseToPOD(const string& in, T *out) { \
|
||||
void parseToPOD(const char* in, T *out) { \
|
||||
epics ## S temp; \
|
||||
int err = epicsParse ## S (in.c_str(), &temp, 0, NULL); \
|
||||
int err = epicsParse ## S (in, &temp, 0, NULL); \
|
||||
if(err) handleParseError(err); \
|
||||
else *out = temp; \
|
||||
}
|
||||
@@ -532,31 +132,31 @@ INTFN(uint16_t, UInt16);
|
||||
INTFN(int32_t, Int32);
|
||||
INTFN(uint32_t, UInt32);
|
||||
|
||||
void parseToPOD(const string& in, int64_t *out) {
|
||||
void parseToPOD(const char* in, int64_t *out) {
|
||||
#ifdef NEED_LONGLONG
|
||||
int err = epicsParseLongLong(in.c_str(), out, 0, NULL);
|
||||
int err = epicsParseLongLong(in, out, 0, NULL);
|
||||
#else
|
||||
int err = epicsParseLong(in.c_str(), out, 0, NULL);
|
||||
int err = epicsParseLong(in, out, 0, NULL);
|
||||
#endif
|
||||
if(err) handleParseError(err);
|
||||
}
|
||||
|
||||
void parseToPOD(const string& in, uint64_t *out) {
|
||||
void parseToPOD(const char* in, uint64_t *out) {
|
||||
#ifdef NEED_LONGLONG
|
||||
int err = epicsParseULongLong(in.c_str(), out, 0, NULL);
|
||||
int err = epicsParseULongLong(in, out, 0, NULL);
|
||||
#else
|
||||
int err = epicsParseULong(in.c_str(), out, 0, NULL);
|
||||
int err = epicsParseULong(in, out, 0, NULL);
|
||||
#endif
|
||||
if(err) handleParseError(err);
|
||||
}
|
||||
|
||||
void parseToPOD(const string& in, float *out) {
|
||||
int err = epicsParseFloat(in.c_str(), out, NULL);
|
||||
void parseToPOD(const char* in, float *out) {
|
||||
int err = epicsParseFloat(in, out, NULL);
|
||||
if(err) handleParseError(err);
|
||||
}
|
||||
|
||||
void parseToPOD(const string& in, double *out) {
|
||||
int err = epicsParseDouble(in.c_str(), out, NULL);
|
||||
void parseToPOD(const char* in, double *out) {
|
||||
int err = epicsParseDouble(in, out, NULL);
|
||||
if(err) handleParseError(err);
|
||||
#if defined(vxWorks)
|
||||
/* vxWorks strtod returns [-]epicsINF when it should return ERANGE error.
|
||||
@@ -564,7 +164,7 @@ void parseToPOD(const string& in, double *out) {
|
||||
* this into an ERANGE error
|
||||
*/
|
||||
else if (*out == epicsINF || *out == -epicsINF) {
|
||||
const char* s = in.c_str();
|
||||
const char* s = in;
|
||||
int c;
|
||||
|
||||
/* skip spaces and the sign */
|
||||
|
||||
251
src/misc/pv/anyscalar.h
Normal file
251
src/misc/pv/anyscalar.h
Normal file
@@ -0,0 +1,251 @@
|
||||
#ifndef PV_ANYSCALAR_H
|
||||
#define PV_ANYSCALAR_H
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
# include <type_traits>
|
||||
#endif
|
||||
|
||||
#include <ostream>
|
||||
#include <exception>
|
||||
#include <map>
|
||||
|
||||
#include <epicsAssert.h>
|
||||
|
||||
#include <pv/templateMeta.h>
|
||||
#include <pv/typeCast.h>
|
||||
#include <pv/pvIntrospect.h> /* for ScalarType enum */
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
namespace detail {
|
||||
|
||||
// special mangling for AnyScalar ctor to map from argument type to storage type.
|
||||
// allow construction from constants.
|
||||
template <typename T>
|
||||
struct any_storage_type { typedef T type; };
|
||||
template<> struct any_storage_type<int> { typedef int32 type; };
|
||||
template<> struct any_storage_type<unsigned> { typedef uint32 type; };
|
||||
template<> struct any_storage_type<char*> { typedef std::string type; };
|
||||
template<> struct any_storage_type<const char*> { typedef std::string type; };
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
// std::max() isn't constexpr until c++14 :(
|
||||
constexpr size_t cmax(size_t A, size_t B) {
|
||||
return A>B ? A : B;
|
||||
}
|
||||
#endif
|
||||
|
||||
}// namespace detail
|
||||
|
||||
/** A type-safe variant union capable of holding
|
||||
* any of the PVD scalar types (POD or string)
|
||||
*
|
||||
@code
|
||||
AnyScalar A(5);
|
||||
assert(A.type()==pvInt);
|
||||
assert(A.ref<int32>()==5);
|
||||
assert(A.as<int32>()==5);
|
||||
assert(A.as<double>()==5.0);
|
||||
assert(A.ref<double>()==5.0); // throws AnyScalar::bad_cast
|
||||
@endcode
|
||||
*/
|
||||
class epicsShareClass AnyScalar {
|
||||
public:
|
||||
struct bad_cast : public std::exception {
|
||||
#if __cplusplus>=201103L
|
||||
bad_cast() noexcept {}
|
||||
virtual ~bad_cast() noexcept {}
|
||||
virtual const char* what() const noexcept
|
||||
#else
|
||||
bad_cast() throw() {}
|
||||
virtual ~bad_cast() throw() {}
|
||||
virtual const char* what() const throw()
|
||||
#endif
|
||||
{ return "bad_cast() type mis-match"; }
|
||||
};
|
||||
|
||||
private:
|
||||
ScalarType _stype;
|
||||
|
||||
// always reserve enough storage for std::string or double (assumed worst case)
|
||||
#if __cplusplus>=201103L
|
||||
|
||||
struct wrap_t {
|
||||
typename std::aligned_storage<detail::cmax(sizeof(std::string), sizeof(double)),
|
||||
detail::cmax(alignof(std::string), alignof(double))
|
||||
>::type blob[1];
|
||||
} _wrap;
|
||||
#else
|
||||
struct wrap_t {
|
||||
union blob_t {
|
||||
char data[sizeof(std::string)];
|
||||
double align_f; // assume std::string alignment <= 8
|
||||
} blob[1];
|
||||
} _wrap;
|
||||
#endif
|
||||
|
||||
// assumed largest non-string type
|
||||
typedef double _largest_blob;
|
||||
|
||||
template<typename T>
|
||||
inline T& _as() {
|
||||
return *reinterpret_cast<T*>(_wrap.blob);
|
||||
}
|
||||
template<typename T>
|
||||
inline const T& _as() const {
|
||||
return *reinterpret_cast<const T*>(_wrap.blob);
|
||||
}
|
||||
public:
|
||||
//! Construct empty
|
||||
//! @post empty()==true
|
||||
AnyScalar() : _stype((ScalarType)-1) {}
|
||||
|
||||
//! Construct from provided value.
|
||||
template<typename T>
|
||||
explicit AnyScalar(T v)
|
||||
{
|
||||
typedef typename meta::strip_const<T>::type T2;
|
||||
typedef typename detail::any_storage_type<T2>::type TT;
|
||||
|
||||
STATIC_ASSERT(sizeof(TT)<=sizeof(_wrap.blob));
|
||||
|
||||
new (_wrap.blob) TT(v);
|
||||
|
||||
// this line fails to compile when type T can't be mapped to one of
|
||||
// the PVD scalar types.
|
||||
_stype = (ScalarType)ScalarTypeID<TT>::value;
|
||||
}
|
||||
|
||||
//! Construct from un-typed pointer.
|
||||
//! Caller is responsible to ensure that buf actually points to the provided type
|
||||
//! @version Added after 7.0.0
|
||||
AnyScalar(ScalarType type, const void *buf);
|
||||
|
||||
AnyScalar(const AnyScalar& o);
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
AnyScalar(AnyScalar&& o);
|
||||
#endif
|
||||
|
||||
inline ~AnyScalar() {clear();}
|
||||
|
||||
inline AnyScalar& operator=(const AnyScalar& o) {
|
||||
AnyScalar(o).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AnyScalar& operator=(T v) {
|
||||
AnyScalar(v).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
inline AnyScalar& operator=(AnyScalar&& o) {
|
||||
clear();
|
||||
swap(o);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
//! Reset internal state.
|
||||
//! @version Added after 7.0.0
|
||||
//! @post empty()==true
|
||||
void clear();
|
||||
|
||||
void swap(AnyScalar& o);
|
||||
|
||||
//! Type code of contained value. Or (ScalarType)-1 is empty.
|
||||
inline ScalarType type() const {
|
||||
return _stype;
|
||||
}
|
||||
|
||||
inline void* unsafe() { return _wrap.blob; }
|
||||
inline const void* unsafe() const { return _wrap.blob; }
|
||||
|
||||
inline bool empty() const { return _stype==(ScalarType)-1; }
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
explicit operator bool() const { return !empty(); }
|
||||
#else
|
||||
private:
|
||||
typedef void (AnyScalar::*bool_type)(AnyScalar&);
|
||||
public:
|
||||
operator bool_type() const { return !empty() ? &AnyScalar::swap : 0; }
|
||||
#endif
|
||||
|
||||
//! Provide read-only access to underlying buffer.
|
||||
//! For a string this is std::string::c_str().
|
||||
//! @version Added after 7.0.0
|
||||
const void* bufferUnsafe() const;
|
||||
|
||||
/** Return typed reference to wrapped value. Non-const reference allows value modification
|
||||
*
|
||||
* @throws bad_cast when the requested type does not match the stored type
|
||||
@code
|
||||
AnyScalar v(42);
|
||||
v.ref<uint32>() = 43;
|
||||
assert(v.ref<uint32>() == 43);
|
||||
@endcode
|
||||
*/
|
||||
template<typename T>
|
||||
inline
|
||||
// T -> strip_const -> map to storage type -> add reference
|
||||
typename detail::any_storage_type<typename meta::strip_const<T>::type>::type&
|
||||
ref() {
|
||||
typedef typename meta::strip_const<T>::type T2;
|
||||
typedef typename detail::any_storage_type<T2>::type TT;
|
||||
|
||||
if(_stype!=(ScalarType)ScalarTypeID<TT>::value)
|
||||
throw bad_cast();
|
||||
return reinterpret_cast<TT&>(_wrap.blob);
|
||||
}
|
||||
|
||||
/** Return typed reference to wrapped value. Const reference does not allow modification.
|
||||
*
|
||||
* @throws bad_cast when the requested type does not match the stored type
|
||||
@code
|
||||
const AnyScalar v(42);
|
||||
assert(v.ref<uint32>() == 42);
|
||||
@endcode
|
||||
*/
|
||||
template<typename T>
|
||||
inline
|
||||
// T -> strip_const -> map to storage type -> add const reference
|
||||
typename meta::decorate_const<typename detail::any_storage_type<typename meta::strip_const<T>::type>::type>::type&
|
||||
ref() const {
|
||||
typedef typename meta::strip_const<T>::type T2;
|
||||
typedef typename detail::any_storage_type<T2>::type TT;
|
||||
|
||||
if(_stype!=(ScalarType)ScalarTypeID<TT>::value)
|
||||
throw bad_cast();
|
||||
return reinterpret_cast<typename meta::decorate_const<TT>::type&>(_wrap.blob);
|
||||
}
|
||||
|
||||
/** copy out wrapped value, with a value conversion.
|
||||
*
|
||||
* @throws bad_cast when empty()==true
|
||||
*/
|
||||
template<typename T>
|
||||
inline
|
||||
T as() const {
|
||||
typedef typename meta::strip_const<T>::type T2;
|
||||
typedef typename detail::any_storage_type<T2>::type TT;
|
||||
|
||||
if(_stype==(ScalarType)-1)
|
||||
throw bad_cast();
|
||||
TT ret;
|
||||
castUnsafeV(1, (ScalarType)ScalarTypeID<T2>::value, &ret,
|
||||
_stype, _wrap.blob);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
friend epicsShareFunc std::ostream& operator<<(std::ostream& strm, const AnyScalar& v);
|
||||
};
|
||||
|
||||
epicsShareExtern
|
||||
std::ostream& operator<<(std::ostream& strm, const AnyScalar& v);
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#endif // PV_ANYSCALAR_H
|
||||
@@ -9,6 +9,10 @@
|
||||
#ifndef BITSET_H
|
||||
#define BITSET_H
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
# include <initializer_list>
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <pv/pvType.h>
|
||||
@@ -46,6 +50,8 @@ namespace epics { namespace pvData {
|
||||
* synchronization.
|
||||
*
|
||||
* Based on Java implementation.
|
||||
*
|
||||
* @since 7.0.0 Many methods return BitSet& to facilite method chaining.
|
||||
*/
|
||||
class epicsShareClass BitSet : public Serializable {
|
||||
public:
|
||||
@@ -65,6 +71,16 @@ 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.
|
||||
*/
|
||||
@@ -76,21 +92,21 @@ namespace epics { namespace pvData {
|
||||
*
|
||||
* @param bitIndex the index of the bit to flip
|
||||
*/
|
||||
void flip(uint32 bitIndex);
|
||||
BitSet& flip(uint32 bitIndex);
|
||||
|
||||
/**
|
||||
* Sets the bit at the specified index to @c true.
|
||||
*
|
||||
* @param bitIndex a bit index
|
||||
*/
|
||||
void set(uint32 bitIndex);
|
||||
BitSet& set(uint32 bitIndex);
|
||||
|
||||
/**
|
||||
* Sets the bit specified by the index to @c false.
|
||||
*
|
||||
* @param bitIndex the index of the bit to be cleared
|
||||
*/
|
||||
void clear(uint32 bitIndex);
|
||||
BitSet& clear(uint32 bitIndex);
|
||||
|
||||
/**
|
||||
* Sets the bit at the specified index to the specified value.
|
||||
@@ -167,8 +183,13 @@ 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 logical <b>AND</b> of this target bit set with the
|
||||
* Performs a bitwise <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
|
||||
@@ -179,7 +200,7 @@ namespace epics { namespace pvData {
|
||||
BitSet& operator&=(const BitSet& set);
|
||||
|
||||
/**
|
||||
* Performs a logical <b>OR</b> of this bit set with the bit set
|
||||
* Performs a bitwise <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
|
||||
@@ -190,7 +211,7 @@ namespace epics { namespace pvData {
|
||||
BitSet& operator|=(const BitSet& set);
|
||||
|
||||
/**
|
||||
* Performs a logical <b>XOR</b> of this bit set with the bit set
|
||||
* Performs a bitwise <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
328
src/misc/pv/debugPtr.h
Normal file
328
src/misc/pv/debugPtr.h
Normal file
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/* 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
|
||||
@@ -1,40 +0,0 @@
|
||||
/* destroyable.h */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mse
|
||||
*/
|
||||
#ifndef DESTROYABLE_H
|
||||
#define DESTROYABLE_H
|
||||
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Instance declaring destroy method.
|
||||
*
|
||||
* @author mse
|
||||
*/
|
||||
class epicsShareClass Destroyable {
|
||||
public:
|
||||
POINTER_DEFINITIONS(Destroyable);
|
||||
/**
|
||||
* Destroy this instance.
|
||||
*/
|
||||
virtual void destroy() = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Do not allow delete on this instance and derived classes, destroy() must be used instead.
|
||||
*/
|
||||
virtual ~Destroyable() {};
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* DESTROYABLE_H */
|
||||
@@ -33,10 +33,6 @@
|
||||
#ifndef EPICSEXCEPTION_H_
|
||||
#define EPICSEXCEPTION_H_
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
@@ -181,7 +177,7 @@ do { \
|
||||
|
||||
#define PRINT_EXCEPTION(EI) PRINT_EXCEPTION2(EI,stderr)
|
||||
|
||||
#ifndef __GNUC__
|
||||
#if !defined(__GNUC__) || __GNUC__ < 4
|
||||
# define SHOW_EXCEPTION(EI) ::epics::pvData::detail::showException(EI)
|
||||
#else
|
||||
# define SHOW_EXCEPTION(EI) \
|
||||
@@ -211,7 +207,7 @@ public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
explicit BaseException(const std::string msg) : std::logic_error(msg) {}
|
||||
explicit BaseException(const std::string& msg) : std::logic_error(msg) {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
@@ -233,7 +229,6 @@ 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)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
/* 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 */
|
||||
@@ -1,32 +0,0 @@
|
||||
/* localStaticLock.h */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mse
|
||||
*/
|
||||
#ifndef LOCALSTATICLOCK_H
|
||||
#define LOCALSTATICLOCK_H
|
||||
|
||||
#include <pv/lock.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
epicsShareExtern epics::pvData::Mutex& getLocalStaticInitMutex();
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ >= 4
|
||||
// noop
|
||||
#define LOCAL_STATIC_LOCK
|
||||
#else
|
||||
#define LOCAL_STATIC_LOCK epics::pvData::Lock localStaticInitMutexLock(getLocalStaticInitMutex());
|
||||
#endif
|
||||
|
||||
static class epicsShareClass MutexInitializer {
|
||||
public:
|
||||
MutexInitializer ();
|
||||
~MutexInitializer ();
|
||||
} localStaticMutexInitializer; // Note object here in the header.
|
||||
|
||||
|
||||
#endif /* LOCALSTATICLOCK_H */
|
||||
@@ -33,7 +33,8 @@ typedef epicsMutex Mutex;
|
||||
* This is based on item 14 of
|
||||
* * Effective C++, Third Edition, Scott Meyers
|
||||
*/
|
||||
class epicsShareClass Lock : private NoDefaultMethods {
|
||||
class Lock {
|
||||
EPICS_NOT_COPYABLE(Lock)
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
/* messageQueue.h */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
#ifndef MESSAGEQUEUE_H
|
||||
#define MESSAGEQUEUE_H
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/requester.h>
|
||||
#include <pv/queue.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class MessageNode;
|
||||
class MessageQueue;
|
||||
typedef std::tr1::shared_ptr<MessageNode> MessageNodePtr;
|
||||
typedef std::vector<MessageNodePtr> MessageNodePtrArray;
|
||||
typedef std::tr1::shared_ptr<MessageQueue> MessageQueuePtr;
|
||||
|
||||
/**
|
||||
* @brief A node that can be put on a MessageQueue.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass MessageNode {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
MessageNode();
|
||||
/**
|
||||
*
|
||||
* Get the message value.
|
||||
* @return The message value.
|
||||
*/
|
||||
std::string getMessage() const;
|
||||
/**
|
||||
* Get the message type.
|
||||
* @return The message type which is defined in Requester.
|
||||
*/
|
||||
MessageType getMessageType() const;
|
||||
private:
|
||||
std::string message;
|
||||
MessageType messageType;
|
||||
friend class MessageQueue;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A bounded queue for messages.
|
||||
*
|
||||
*
|
||||
*/
|
||||
class epicsShareClass MessageQueue : public Queue<MessageNode> {
|
||||
public:
|
||||
POINTER_DEFINITIONS(MessageQueue);
|
||||
/**
|
||||
* Factory method to create a MessageQueue.
|
||||
* @param size The number of MessageNodes in the queue.
|
||||
* @return shared_ptr to MessageQueue.
|
||||
*/
|
||||
static MessageQueuePtr create(int size);
|
||||
/**
|
||||
* Constructor
|
||||
* @param nodeArray an array of shared_ptr to MessageNodes,
|
||||
*/
|
||||
MessageQueue(MessageNodePtrArray &nodeArray);
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~MessageQueue();
|
||||
/**
|
||||
* get the next MessageNode of the queue.
|
||||
* @return A shared_ptr to the MessageNode.
|
||||
* This will be a null pointer if queue is empty.
|
||||
* If get is successful then release for this MessageNode
|
||||
* must be called before another get can be issued.
|
||||
*/
|
||||
MessageNodePtr &get();
|
||||
/**
|
||||
* Release the MessageNode that was returned by the previous call to get.
|
||||
*/
|
||||
void release();
|
||||
/**
|
||||
*
|
||||
* put a message into the message queue
|
||||
* @param message The message string.
|
||||
* @param messageType The message type as defined in Requester,
|
||||
* @param replaceLast If true and queue is full then replace.
|
||||
* @return (false,true) if a message (was not, was) put in queiue.
|
||||
*/
|
||||
bool put(std::string message,MessageType messageType,bool replaceLast);
|
||||
/**
|
||||
* Is queue empty?
|
||||
* @return (false,true) if (is not, is) empty.
|
||||
*/
|
||||
bool isEmpty() ;
|
||||
/**
|
||||
* Is queue full?
|
||||
* @return (false,true) if (is not, is) full.
|
||||
*/
|
||||
bool isFull() ;
|
||||
/**
|
||||
*
|
||||
* Clear number of times queue was overrun and return the number
|
||||
* of times the queue was overrun.
|
||||
*/
|
||||
int getClearOverrun();
|
||||
private:
|
||||
MessageNodePtr nullNode;
|
||||
MessageNodePtr lastGet;
|
||||
MessageNodePtr lastPut;
|
||||
uint32 overrun;
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* MESSAGEQUEUE_H */
|
||||
|
||||
|
||||
|
||||
@@ -11,30 +11,54 @@
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
/* This is based on Item 6 of
|
||||
* Effective C++, Third Edition, Scott Meyers
|
||||
/** @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:'.
|
||||
*/
|
||||
#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 epicsShareClass NoDefaultMethods {
|
||||
protected:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
NoDefaultMethods(){};
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~NoDefaultMethods(){}
|
||||
private:
|
||||
class NoDefaultMethods {
|
||||
public:
|
||||
NoDefaultMethods() {}
|
||||
private:
|
||||
#if __cplusplus>=201103L
|
||||
NoDefaultMethods(const NoDefaultMethods&) = delete;
|
||||
NoDefaultMethods & operator=(const NoDefaultMethods &) = delete;
|
||||
#else
|
||||
// do not implement
|
||||
NoDefaultMethods(const NoDefaultMethods&);
|
||||
NoDefaultMethods & operator=(const NoDefaultMethods &);
|
||||
#endif
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
188
src/misc/pv/pvUnitTest.h
Normal file
188
src/misc/pv/pvUnitTest.h
Normal file
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -1,183 +0,0 @@
|
||||
/* 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 */
|
||||
|
||||
|
||||
|
||||
156
src/misc/pv/reftrack.h
Normal file
156
src/misc/pv/reftrack.h
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -1,71 +0,0 @@
|
||||
/* 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 */
|
||||
@@ -150,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.
|
||||
@@ -213,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.
|
||||
|
||||
@@ -26,7 +26,8 @@ namespace epics {
|
||||
* @brief Serialization helper.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass SerializeHelper : public NoDefaultMethods {
|
||||
class epicsShareClass SerializeHelper {
|
||||
EPICS_NOT_COPYABLE(SerializeHelper)
|
||||
public:
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
#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
|
||||
@@ -23,12 +25,58 @@
|
||||
* function enable_shared_from_this
|
||||
*/
|
||||
|
||||
// where should we look?
|
||||
/* 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
|
||||
*/
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__>=4 && !defined(vxWorks)
|
||||
/* 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)
|
||||
// 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
|
||||
@@ -37,34 +85,51 @@
|
||||
# define SHARED_FROM_BOOST
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER>=1600)
|
||||
// MSVC 2010 has it in <memory>
|
||||
# undef SHARED_FROM_BOOST
|
||||
# undef SHARED_FROM_TR1
|
||||
#endif
|
||||
// go and get it
|
||||
|
||||
#if defined(__clang__)
|
||||
# undef SHARED_FROM_BOOST
|
||||
# undef SHARED_FROM_TR1
|
||||
#if defined(SHARED_FROM_MANUAL)
|
||||
// no-op
|
||||
#elif defined(SHARED_FROM_STD)
|
||||
|
||||
#include <memory>
|
||||
|
||||
// import std classes into std::tr1
|
||||
namespace std {
|
||||
#ifndef DEBUG_SHARED_PTR
|
||||
|
||||
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::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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// go and get it
|
||||
#else // DEBUG_SHARED_PTR
|
||||
|
||||
#if defined(SHARED_FROM_TR1)
|
||||
#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)
|
||||
# include <tr1/memory>
|
||||
|
||||
#elif defined(SHARED_FROM_BOOST)
|
||||
@@ -77,12 +142,15 @@ namespace std {
|
||||
# include <boost/tr1/memory.hpp>
|
||||
|
||||
#else
|
||||
// eventually...
|
||||
# include <memory>
|
||||
# error No shared_ptr selection
|
||||
#endif
|
||||
|
||||
// cleanup
|
||||
|
||||
#ifdef SHARED_FROM_STD
|
||||
# undef SHARED_FROM_STD
|
||||
#endif
|
||||
|
||||
#ifdef SHARED_FROM_TR1
|
||||
# undef SHARED_FROM_TR1
|
||||
#endif
|
||||
@@ -91,10 +159,78 @@ namespace std {
|
||||
# undef SHARED_FROM_BOOST
|
||||
#endif
|
||||
|
||||
namespace detail {
|
||||
template<typename T>
|
||||
struct ref_shower {
|
||||
const std::tr1::shared_ptr<T>& ptr;
|
||||
bool self, weak;
|
||||
ref_shower(const std::tr1::shared_ptr<T>& ptr, bool self, bool weak) :ptr(ptr),self(self),weak(weak) {}
|
||||
};
|
||||
}
|
||||
|
||||
/** Print a list (one per line) of shared_ptr which refer to the same object
|
||||
*
|
||||
* @param ptr Use the object pointed to by this shared_ptr
|
||||
* @param self include or omit a line for this shared_ptr
|
||||
* @param weak include a line for each weak_ptr (not implemented)
|
||||
@code
|
||||
shared_ptr<int> x;
|
||||
std::cout << show_referrers(x);
|
||||
@endcode
|
||||
*/
|
||||
template<typename T>
|
||||
inline ::detail::ref_shower<T> show_referrers(const std::tr1::shared_ptr<T>& ptr, bool self=true, bool weak=false)
|
||||
{
|
||||
return ::detail::ref_shower<T>(ptr, self, weak);
|
||||
}
|
||||
|
||||
namespace std{
|
||||
template<typename T>
|
||||
inline std::ostream& operator<<(std::ostream& strm, const ::detail::ref_shower<T>& refs)
|
||||
{
|
||||
#ifdef HAVE_SHOW_REFS
|
||||
refs.ptr.show_refs(strm, refs.self, refs.weak);
|
||||
#endif // HAVE_SHOW_REFS
|
||||
return strm;
|
||||
}
|
||||
}//namespace std
|
||||
|
||||
#define POINTER_DEFINITIONS(clazz) \
|
||||
typedef std::tr1::shared_ptr<clazz> shared_pointer; \
|
||||
typedef std::tr1::shared_ptr<const clazz> const_shared_pointer; \
|
||||
typedef std::tr1::weak_ptr<clazz> weak_pointer; \
|
||||
typedef std::tr1::weak_ptr<const clazz> const_weak_pointer
|
||||
|
||||
namespace epics{
|
||||
/** A semi-hack to help with migration from std::auto_ptr to std::unique_ptr,
|
||||
* and avoid copious deprecation warning spam
|
||||
* which may be hiding legitimate issues.
|
||||
*
|
||||
* Provides epics::auto_ptr<T> and epics::swap()
|
||||
*
|
||||
* epics::auto_ptr<T> is std::auto_ptr<T> for c++98
|
||||
* and std::unique_ptr<T> for >= c++11.
|
||||
*
|
||||
* epics::swap() is the only supported operation.
|
||||
* copy/assignment/return are not supported
|
||||
* (use auto_ptr or unique_ptr explicitly).
|
||||
*/
|
||||
#if __cplusplus>=201103L
|
||||
template<typename T>
|
||||
using auto_ptr = std::unique_ptr<T>;
|
||||
template<typename T>
|
||||
static inline void swap(auto_ptr<T>& lhs, auto_ptr<T>& rhs) {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
#else
|
||||
using std::auto_ptr;
|
||||
template<typename T>
|
||||
static inline void swap(auto_ptr<T>& lhs, auto_ptr<T>& rhs) {
|
||||
auto_ptr<T> temp(lhs);
|
||||
lhs = rhs;
|
||||
rhs = temp;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // SHAREDPTR_H
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
#ifndef SHAREDVECTOR_H
|
||||
#define SHAREDVECTOR_H
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <ostream>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <iterator>
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
# include <initializer_list>
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "pv/sharedPtr.h"
|
||||
@@ -70,18 +70,24 @@ 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.get()) {
|
||||
if(!m_sdata) {
|
||||
m_offset = m_total = m_count = 0;
|
||||
} else {
|
||||
// ensure we won't have integer overflows later
|
||||
@@ -90,19 +96,12 @@ 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();}
|
||||
@@ -118,6 +117,17 @@ 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:
|
||||
@@ -132,7 +142,11 @@ 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();
|
||||
}
|
||||
|
||||
@@ -146,7 +160,11 @@ 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();
|
||||
}
|
||||
|
||||
@@ -162,6 +180,21 @@ 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) {
|
||||
@@ -180,11 +213,12 @@ namespace detail {
|
||||
}
|
||||
|
||||
//! @brief Data is not shared?
|
||||
bool unique() const {return !m_sdata || m_sdata.unique();}
|
||||
bool unique() const {return !m_sdata || m_sdata.use_count()<=1;}
|
||||
|
||||
|
||||
//! @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;}
|
||||
|
||||
|
||||
@@ -218,7 +252,9 @@ namespace detail {
|
||||
|
||||
m_total -= offset;
|
||||
|
||||
m_count = std::min(length, max_count);
|
||||
if(length > max_count)
|
||||
length = max_count;
|
||||
m_count = length;
|
||||
}
|
||||
|
||||
// Access to members.
|
||||
@@ -272,9 +308,22 @@ 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 Allocate (with new[]) a new vector of size c
|
||||
explicit shared_vector(size_t c)
|
||||
@@ -321,6 +370,11 @@ 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>
|
||||
@@ -342,6 +396,20 @@ 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; }
|
||||
@@ -353,11 +421,16 @@ 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 = std::min(this->m_count, i);
|
||||
size_t new_count = this->m_count;
|
||||
if(new_count > i)
|
||||
new_count = i;
|
||||
_E_non_const* temp=new _E_non_const[i];
|
||||
try{
|
||||
std::copy(begin(), begin()+new_count, temp);
|
||||
@@ -377,13 +450,16 @@ 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.unique()) {
|
||||
if(this->m_sdata && this->m_sdata.use_count()==1) {
|
||||
// we have data and exclusive ownership of it
|
||||
if(i<=this->m_total) {
|
||||
// We have room to grow (or shrink)!
|
||||
@@ -392,13 +468,18 @@ public:
|
||||
}
|
||||
}
|
||||
// must re-allocate :(
|
||||
size_t new_total = std::max(this->m_total, i);
|
||||
size_t new_total = this->m_total;
|
||||
if(new_total < i)
|
||||
new_total = i;
|
||||
_E_non_const* temp=new _E_non_const[new_total];
|
||||
try{
|
||||
size_t n = this->size();
|
||||
if(n > i)
|
||||
n = i;
|
||||
// Copy as much as possible from old,
|
||||
// remaining elements are uninitialized.
|
||||
std::copy(begin(),
|
||||
begin()+std::min(i,this->size()),
|
||||
begin()+n,
|
||||
temp);
|
||||
this->m_sdata.reset(temp, detail::default_array_deleter<pointer>());
|
||||
}catch(...){
|
||||
@@ -438,10 +519,14 @@ 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,
|
||||
@@ -455,13 +540,27 @@ 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->m_sdata.get()+this->m_offset;}
|
||||
iterator begin() const{return this->base_ptr()+this->m_offset;}
|
||||
const_iterator cbegin() const{return begin();}
|
||||
|
||||
iterator end() const{return this->m_sdata.get()+this->m_offset+this->m_count;}
|
||||
iterator end() const{return this->base_ptr()+this->m_offset+this->m_count;}
|
||||
const_iterator cend() const{return end();}
|
||||
|
||||
reverse_iterator rbegin() const{return reverse_iterator(end());}
|
||||
@@ -513,10 +612,15 @@ 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)
|
||||
@@ -563,7 +667,11 @@ 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) {}
|
||||
@@ -579,6 +687,11 @@ 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>
|
||||
@@ -609,6 +722,22 @@ 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{
|
||||
@@ -623,33 +752,21 @@ 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>
|
||||
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) {
|
||||
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) {
|
||||
return shared_vector<TO>(src, detail::_shared_vector_cast_tag());
|
||||
}
|
||||
};
|
||||
// from non-void to void with same const-ness
|
||||
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());
|
||||
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());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -829,7 +946,7 @@ const_shared_vector_cast(shared_vector<FROM>& src)
|
||||
|
||||
namespace ScalarTypeFunc {
|
||||
//! Allocate an untyped array based on ScalarType
|
||||
shared_vector<void> allocArray(ScalarType id, size_t len);
|
||||
epicsShareFunc shared_vector<void> allocArray(ScalarType id, size_t len);
|
||||
|
||||
//! Allocate an untyped array based on ScalarType
|
||||
template<ScalarType ID>
|
||||
|
||||
@@ -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,20 +40,24 @@ namespace epics { namespace pvData {
|
||||
STATUSTYPE_ERROR,
|
||||
/** Operation failed due to an unexpected error. */
|
||||
STATUSTYPE_FATAL
|
||||
};
|
||||
|
||||
static const char* StatusTypeName[];
|
||||
|
||||
static Status Ok;
|
||||
};
|
||||
|
||||
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); }
|
||||
|
||||
/**
|
||||
* Creates OK status; STATUSTYPE_OK, empty message and stackDump.
|
||||
*/
|
||||
Status();
|
||||
|
||||
/**
|
||||
* Create non-OK status.
|
||||
*/
|
||||
Status() :m_statusType(STATUSTYPE_OK) {}
|
||||
|
||||
/**
|
||||
* Create non-OK status.
|
||||
*/
|
||||
Status(StatusType type, std::string const & message);
|
||||
|
||||
/**
|
||||
@@ -61,25 +65,25 @@ namespace epics { namespace pvData {
|
||||
*/
|
||||
Status(StatusType type, std::string const & message, std::string const & stackDump);
|
||||
|
||||
~Status();
|
||||
virtual ~Status() {}
|
||||
|
||||
/**
|
||||
* Get status type.
|
||||
* @return status type, non-<code>null</code>.
|
||||
*/
|
||||
StatusType getType() const;
|
||||
inline StatusType getType() const { return m_statusType; }
|
||||
|
||||
/**
|
||||
* Get error message describing an error. Required if error status.
|
||||
* @return error message.
|
||||
*/
|
||||
std::string getMessage() const;
|
||||
inline const std::string& getMessage() const { return m_message; }
|
||||
|
||||
/**
|
||||
* Get stack dump where error (exception) happened. Optional.
|
||||
* @return stack dump.
|
||||
*/
|
||||
std::string getStackDump() const;
|
||||
inline const std::string& getStackDump() const { return m_stackDump; }
|
||||
|
||||
/**
|
||||
* Convenient OK test. Same as <code>(getType() == StatusType.OK)</code>.
|
||||
@@ -88,13 +92,47 @@ namespace epics { namespace pvData {
|
||||
* @return OK status.
|
||||
* @see #isSuccess()
|
||||
*/
|
||||
bool isOK() const;
|
||||
inline bool isOK() const {
|
||||
return (m_statusType == STATUSTYPE_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if operation succeeded.
|
||||
* Check if operation succeeded (OK or WARNING).
|
||||
* @return operation success status.
|
||||
*/
|
||||
bool isSuccess() const;
|
||||
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;
|
||||
}
|
||||
|
||||
void serialize(ByteBuffer *buffer, SerializableControl *flusher) const;
|
||||
void deserialize(ByteBuffer *buffer, DeserializableControl *flusher);
|
||||
@@ -109,8 +147,15 @@ namespace epics { namespace pvData {
|
||||
|
||||
};
|
||||
|
||||
epicsShareExtern std::ostream& operator<<(std::ostream& o, const Status& status);
|
||||
epicsShareExtern std::ostream& operator<<(std::ostream& o, const Status::StatusType& statusType);
|
||||
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;
|
||||
}
|
||||
|
||||
}}
|
||||
#endif /* STATUS_H */
|
||||
|
||||
@@ -10,7 +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(__GNUC__) && __GNUC__>=3
|
||||
#if defined(__MINGW32__)
|
||||
# define FORCE_INLINE inline
|
||||
#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 402)
|
||||
# define FORCE_INLINE __attribute__((always_inline)) inline
|
||||
#elif defined(_MSC_VER)
|
||||
# define FORCE_INLINE __forceinline
|
||||
@@ -114,6 +116,31 @@ struct _and {};
|
||||
template<typename A, typename B, class R>
|
||||
struct _and<A,B, typename A::type, typename B::type, R> { typedef R type; };
|
||||
|
||||
/** Mangle type to best pass as an argument.
|
||||
*
|
||||
* POD types passed by value.
|
||||
* All others by const reference.
|
||||
*/
|
||||
template<typename T>
|
||||
struct arg_type {typedef const T& type;};
|
||||
#define SIMPLE_ARG_TYPE(TYPE) template<> struct arg_type<TYPE> { typedef TYPE type; };
|
||||
SIMPLE_ARG_TYPE(bool)
|
||||
SIMPLE_ARG_TYPE(char)
|
||||
SIMPLE_ARG_TYPE(signed char)
|
||||
SIMPLE_ARG_TYPE(unsigned char)
|
||||
SIMPLE_ARG_TYPE(short)
|
||||
SIMPLE_ARG_TYPE(unsigned short)
|
||||
SIMPLE_ARG_TYPE(int)
|
||||
SIMPLE_ARG_TYPE(unsigned int)
|
||||
SIMPLE_ARG_TYPE(long)
|
||||
SIMPLE_ARG_TYPE(unsigned long)
|
||||
SIMPLE_ARG_TYPE(long long)
|
||||
SIMPLE_ARG_TYPE(unsigned long long)
|
||||
SIMPLE_ARG_TYPE(float)
|
||||
SIMPLE_ARG_TYPE(double)
|
||||
SIMPLE_ARG_TYPE(long double)
|
||||
#undef SIMPLE_ARG_TYPE
|
||||
|
||||
}}}
|
||||
|
||||
#endif // TEMPLATEMETA_H
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
#include <functional>
|
||||
@@ -41,37 +42,7 @@ typedef std::tr1::shared_ptr<epicsThread> EpicsThreadPtr;
|
||||
|
||||
typedef epicsThreadRunable Runnable;
|
||||
|
||||
//! Helper for those cases where a class should have more than one runnable
|
||||
template<typename C>
|
||||
class 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
|
||||
{
|
||||
@@ -85,26 +56,14 @@ 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, private NoDefaultMethods {
|
||||
class epicsShareClass Thread : public epicsThread {
|
||||
EPICS_NOT_COPYABLE(Thread)
|
||||
public:
|
||||
/** @brief Holds all the configuration necessary to launch a @class Thread
|
||||
*
|
||||
@@ -139,56 +98,31 @@ public:
|
||||
std::ostringstream p_strm;
|
||||
bool p_autostart;
|
||||
Runnable *p_runner;
|
||||
#if __cplusplus>=201103L
|
||||
typedef std::unique_ptr<Runnable> p_owned_runner_t;
|
||||
#else
|
||||
typedef std::auto_ptr<Runnable> p_owned_runner_t;
|
||||
#endif
|
||||
typedef epics::auto_ptr<Runnable> p_owned_runner_t;
|
||||
p_owned_runner_t p_owned_runner;
|
||||
friend class Thread;
|
||||
Runnable& x_getrunner()
|
||||
{
|
||||
if(!this->p_runner)
|
||||
throw std::logic_error("Thread::Config missing run()");
|
||||
return *this->p_runner;
|
||||
}
|
||||
void x_setdefault()
|
||||
{
|
||||
this->p_prio = epicsThreadPriorityLow;
|
||||
this->p_autostart = true;
|
||||
this->p_runner = NULL;
|
||||
(*this).stack(epicsThreadStackSmall);
|
||||
}
|
||||
Runnable& x_getrunner();
|
||||
void x_setdefault();
|
||||
|
||||
public:
|
||||
Config() {this->x_setdefault();}
|
||||
Config(Runnable *r) {this->x_setdefault();this->run(r);}
|
||||
Config(void(*fn)(void*), void *ptr) {this->x_setdefault();this->run(fn, ptr);}
|
||||
Config();
|
||||
Config(Runnable *r);
|
||||
Config(void(*fn)(void*), void *ptr);
|
||||
template<typename C>
|
||||
Config(C* inst, void(C::*meth)()) {this->x_setdefault();this->run(inst, meth);}
|
||||
#if __cplusplus>=201103L
|
||||
Config(const std::function<void()>& fn) {this->x_setdefault();this->run(fn);}
|
||||
Config(std::function<void()>&& fn);
|
||||
#endif
|
||||
|
||||
inline Config& name(const std::string& n)
|
||||
{ this->p_strm.str(n); return *this; }
|
||||
inline Config& prio(unsigned int p)
|
||||
{ this->p_prio = p; return *this; }
|
||||
inline Config& stack(epicsThreadStackSizeClass s)
|
||||
{ this->p_stack = epicsThreadGetStackSize(s); return *this; }
|
||||
inline Config& autostart(bool a)
|
||||
{ this->p_autostart = a; return *this; }
|
||||
Config& name(const std::string& n);
|
||||
Config& prio(unsigned int p);
|
||||
Config& stack(epicsThreadStackSizeClass s);
|
||||
Config& autostart(bool a);
|
||||
|
||||
//! Thread will execute Runnable::run()
|
||||
Config& run(Runnable* r)
|
||||
{ this->p_runner = r; return *this; }
|
||||
Config& run(Runnable* r);
|
||||
//! Thread will execute (*fn)(ptr)
|
||||
Config& run(void(*fn)(void*), void *ptr)
|
||||
{
|
||||
this->p_owned_runner.reset(new detail::FuncRunner(fn, ptr));
|
||||
this->p_runner = this->p_owned_runner.get();
|
||||
return *this;
|
||||
}
|
||||
Config& run(void(*fn)(void*), void *ptr);
|
||||
//! Thread will execute (inst->*meth)()
|
||||
template<typename C>
|
||||
Config& run(C* inst, void(C::*meth)())
|
||||
@@ -198,12 +132,7 @@ public:
|
||||
return *this;
|
||||
}
|
||||
#if __cplusplus>=201103L
|
||||
Config& run(const std::function<void()>& fn)
|
||||
{
|
||||
this->p_owned_runner.reset(new detail::BindRunner(fn));
|
||||
this->p_runner = this->p_owned_runner.get();
|
||||
return *this;
|
||||
}
|
||||
Config& run(std::function<void()>&& fn);
|
||||
#endif
|
||||
|
||||
//! Append to thread name string. Argument must be understood by std::ostream::operator<<
|
||||
@@ -229,14 +158,7 @@ public:
|
||||
Thread(std::string name,
|
||||
ThreadPriority priority,
|
||||
Runnable *runnable,
|
||||
epicsThreadStackSizeClass stkcls=epicsThreadStackSmall)
|
||||
:epicsThread(*runnable,
|
||||
name.c_str(),
|
||||
epicsThreadGetStackSize(stkcls),
|
||||
priority)
|
||||
{
|
||||
this->start();
|
||||
}
|
||||
epicsThreadStackSizeClass stkcls=epicsThreadStackBig);
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -256,38 +178,19 @@ public:
|
||||
Thread(Runnable &runnable,
|
||||
std::string name,
|
||||
unsigned int stksize,
|
||||
unsigned int priority=lowestPriority)
|
||||
:epicsThread(runnable,
|
||||
name.c_str(),
|
||||
stksize,
|
||||
priority)
|
||||
{
|
||||
this->start();
|
||||
}
|
||||
unsigned int priority=lowestPriority);
|
||||
|
||||
//! @brief Create a new thread using the given @class Config
|
||||
//! @throws std::logic_error for improper @class Config (ie. missing runner)
|
||||
Thread(Config& c)
|
||||
:epicsThread(c.x_getrunner(), c.p_strm.str().c_str(),
|
||||
c.p_stack, c.p_prio)
|
||||
{
|
||||
#if __cplusplus>=201103L
|
||||
p_owned = std::move(c.p_owned_runner);
|
||||
#else
|
||||
p_owned = c.p_owned_runner;
|
||||
#endif
|
||||
if(c.p_autostart)
|
||||
this->start();
|
||||
}
|
||||
Thread(Config& c);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Thread()
|
||||
{
|
||||
this->exitWait();
|
||||
}
|
||||
~Thread();
|
||||
|
||||
static size_t num_instances;
|
||||
private:
|
||||
Config::p_owned_runner_t p_owned;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
/* timeFunction.h */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
#ifndef TIMEFUNCTION_H
|
||||
#define TIMEFUNCTION_H
|
||||
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class TimeFunctionRequester;
|
||||
class TimeFunction;
|
||||
typedef std::tr1::shared_ptr<TimeFunctionRequester> TimeFunctionRequesterPtr;
|
||||
typedef std::tr1::shared_ptr<TimeFunction> TimeFunctionPtr;
|
||||
|
||||
/**
|
||||
* @brief Class that must be implemented by timeFunction requester.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass TimeFunctionRequester {
|
||||
public:
|
||||
POINTER_DEFINITIONS(TimeFunctionRequester);
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~TimeFunctionRequester(){}
|
||||
/**
|
||||
* function to be timed.
|
||||
* It will get called multiple times.
|
||||
*/
|
||||
virtual void function() = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Class for measuring time it takes to execute a function.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass TimeFunction {
|
||||
public:
|
||||
POINTER_DEFINITIONS(TimeFunction);
|
||||
/**
|
||||
* Constructor
|
||||
* @param requester The class that has a function method.
|
||||
*/
|
||||
TimeFunction(TimeFunctionRequesterPtr const & requester);
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~TimeFunction();
|
||||
/**
|
||||
* Time the function.
|
||||
* @return the time in seconds to execute the function.
|
||||
* Note that the function may be called many times.
|
||||
*/
|
||||
double timeCall();
|
||||
private:
|
||||
TimeFunctionRequesterPtr requester;
|
||||
};
|
||||
|
||||
|
||||
}}
|
||||
#endif /* TIMEFUNCTION_H */
|
||||
@@ -9,6 +9,8 @@
|
||||
#ifndef TIMER_H
|
||||
#define TIMER_H
|
||||
#include <memory>
|
||||
#include <list>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
@@ -55,34 +57,28 @@ public:
|
||||
*/
|
||||
virtual void timerStopped() = 0;
|
||||
private:
|
||||
TimerCallbackPtr next;
|
||||
TimeStamp timeToRun;
|
||||
epicsTime timeToRun;
|
||||
double period;
|
||||
bool onList;
|
||||
friend class Timer;
|
||||
struct IncreasingTime;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Support for delayed or periodic callback execution.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass Timer : public Runnable {
|
||||
class epicsShareClass Timer : private Runnable {
|
||||
public:
|
||||
POINTER_DEFINITIONS(Timer);
|
||||
/**
|
||||
* Constructor
|
||||
/** Create a new timer queue
|
||||
* @param threadName name for the timer thread.
|
||||
* @param priority thread priority
|
||||
*/
|
||||
Timer(std::string threadName, ThreadPriority priority);
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~Timer();
|
||||
/**
|
||||
* The thread run method. This is called automatically.
|
||||
*/
|
||||
virtual void run();
|
||||
//! Prevent new callbacks from being scheduled, and cancel pending callbacks
|
||||
void close();
|
||||
/**
|
||||
* schedule a callback after a delay.
|
||||
* @param timerCallback the timerCallback instance.
|
||||
@@ -104,31 +100,38 @@ public:
|
||||
/**
|
||||
* cancel a callback.
|
||||
* @param timerCallback the timerCallback to cancel.
|
||||
* @returns true if the timer was queued, and now is cancelled
|
||||
*/
|
||||
void cancel(TimerCallbackPtr const &timerCallback);
|
||||
bool 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);
|
||||
bool isScheduled(TimerCallbackPtr const &timerCallback) const;
|
||||
/**
|
||||
* show the elements in the timer queue.
|
||||
* @param o The output stream for the output
|
||||
*/
|
||||
void dump(std::ostream& o);
|
||||
void dump(std::ostream& o) const;
|
||||
|
||||
private:
|
||||
virtual void run();
|
||||
|
||||
// call with mutex held
|
||||
void addElement(TimerCallbackPtr const &timerCallback);
|
||||
TimerCallbackPtr head;
|
||||
Mutex mutex;
|
||||
|
||||
typedef std::list<TimerCallbackPtr> queue_t;
|
||||
|
||||
mutable Mutex mutex;
|
||||
queue_t queue;
|
||||
Event waitForWork;
|
||||
Event waitForDone;
|
||||
bool waiting;
|
||||
bool alive;
|
||||
Thread thread;
|
||||
};
|
||||
|
||||
epicsShareExtern std::ostream& operator<<(std::ostream& o, Timer& timer);
|
||||
epicsShareExtern std::ostream& operator<<(std::ostream& o, const Timer& timer);
|
||||
|
||||
}}
|
||||
#endif /* TIMER_H */
|
||||
|
||||
@@ -22,17 +22,29 @@ namespace epics { namespace pvData {
|
||||
namespace detail {
|
||||
// parseToPOD wraps the epicsParse*() functions in one name
|
||||
// and throws exceptions
|
||||
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);
|
||||
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); }
|
||||
|
||||
/* want to pass POD types by value,
|
||||
* and std::string by const reference
|
||||
@@ -108,13 +120,28 @@ 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
|
||||
* float, double, std::string, const char* (only FROM)
|
||||
*
|
||||
* As defined in pvType.h
|
||||
*
|
||||
@@ -181,6 +208,43 @@ 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
|
||||
|
||||
53
src/misc/pvUnitTest.cpp
Normal file
53
src/misc/pvUnitTest.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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
|
||||
282
src/misc/reftrack.cpp
Normal file
282
src/misc/reftrack.cpp
Normal file
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* 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());
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/* 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";
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -32,7 +32,7 @@ namespace epics {
|
||||
}
|
||||
|
||||
void SerializeHelper::writeSize(std::size_t s, ByteBuffer* buffer) {
|
||||
if(s==(std::size_t)-1) // null // TODO remove
|
||||
if(s==(std::size_t)-1) // null not a size, but used in PVUnion::serialize()
|
||||
buffer->putByte(-1);
|
||||
else if(s<254)
|
||||
buffer->putByte(static_cast<int8>(s));
|
||||
@@ -98,20 +98,18 @@ 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->getArray()+pos, size);
|
||||
string str(buffer->getBuffer()+pos, size);
|
||||
buffer->setPosition(pos+size);
|
||||
return str;
|
||||
}
|
||||
@@ -124,7 +122,7 @@ namespace epics {
|
||||
while(true) {
|
||||
std::size_t toRead = min(size-i, buffer->getRemaining());
|
||||
std::size_t pos = buffer->getPosition();
|
||||
str.append(buffer->getArray()+pos, toRead);
|
||||
str.append(buffer->getBuffer()+pos, toRead);
|
||||
buffer->setPosition(pos+toRead);
|
||||
i += toRead;
|
||||
if(i<size)
|
||||
@@ -139,7 +137,7 @@ namespace epics {
|
||||
}
|
||||
}
|
||||
else
|
||||
return emptyStringtring;
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user