Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8cf550ff57 | ||
|
|
c070a3485b | ||
|
|
b31d5079bf | ||
|
|
7c78b15a5c | ||
|
|
cd7d8735af | ||
|
|
94b48e4893 | ||
|
|
9d94e95521 | ||
|
|
e0d5925af3 | ||
|
|
f207e512d6 | ||
|
|
0a7d5b16aa | ||
|
|
aea8d9105e | ||
|
|
d18e0c913a | ||
|
|
e4cb8d2397 | ||
|
|
71b2a4bfe1 | ||
|
|
80703b6de1 | ||
|
|
203ee1b450 | ||
|
|
f43d50768e | ||
|
|
8ec8e47924 | ||
|
|
d374669f04 | ||
|
|
2f53d13021 | ||
|
|
00a8459944 | ||
|
|
91d0d2c315 | ||
|
|
c41f7cb3dc | ||
|
|
dbf83dd017 | ||
|
|
1b787c5149 | ||
|
|
ee3027f641 | ||
|
|
6cd0a1bdb0 | ||
|
|
0d85561481 | ||
|
|
05d54c61e1 | ||
|
|
182eee57e2 | ||
|
|
8daf322025 | ||
|
|
a68ef61a10 | ||
|
|
9dfebf1897 | ||
|
|
0cf706511e | ||
|
|
8cac3975cc | ||
|
|
219e5eaf94 | ||
|
|
f345651b67 | ||
|
|
4895107854 | ||
|
|
1c121b3f1e | ||
|
|
d493e52dda | ||
|
|
91d4d2a423 | ||
|
|
d54031a2fb | ||
|
|
1e1d5455a8 | ||
|
|
426af714a1 | ||
|
|
68bbb55370 | ||
|
|
716ec9857c | ||
|
|
e91b4b1b66 | ||
|
|
2c4e98db8c | ||
|
|
d4a13397d7 | ||
|
|
a622f66a94 | ||
|
|
81abd5f413 | ||
|
|
4df01038c3 | ||
|
|
e648cba659 | ||
|
|
42747b167c | ||
|
|
0a893a331b | ||
|
|
e95c959cfd | ||
|
|
e51c29d147 | ||
|
|
fec903fb06 | ||
|
|
ca066bc088 | ||
|
|
0481a7bf5b | ||
|
|
17f43b4452 | ||
|
|
a1514e5206 | ||
|
|
72c2489921 | ||
|
|
895698c3cd | ||
|
|
9d10b039fb | ||
|
|
bf82de407e | ||
|
|
58a603556b | ||
|
|
22e849c6f7 | ||
|
|
2a4d3a15f8 | ||
|
|
7143a8585f | ||
|
|
2d61b4c0f3 | ||
|
|
120fa22558 | ||
|
|
4ed8e6d625 | ||
|
|
4718021e39 | ||
|
|
b1822f5fbd | ||
|
|
333cd44da0 | ||
|
|
a514ab7b51 | ||
|
|
dafc37b585 | ||
|
|
2432f85784 | ||
|
|
bd1054f247 | ||
|
|
bc70b5f449 | ||
|
|
93a259cbde | ||
|
|
09423edeab | ||
|
|
ac1de6770e | ||
|
|
5427311390 | ||
|
|
b62b047f63 | ||
|
|
3bc89bfe0e | ||
|
|
cb5d9f976a |
93
.appveyor.yml
Normal file
93
.appveyor.yml
Normal file
@@ -0,0 +1,93 @@
|
||||
# .appveyor.yml for use with EPICS Base ci-scripts
|
||||
# (see: https://github.com/epics-base/ci-scripts)
|
||||
|
||||
# This is YAML - indentation levels are crucial
|
||||
|
||||
cache:
|
||||
- C:\Users\appveyor\.tools
|
||||
|
||||
#---------------------------------#
|
||||
# repository cloning #
|
||||
#---------------------------------#
|
||||
|
||||
init:
|
||||
# Set autocrlf to make batch files work
|
||||
- git config --global core.autocrlf true
|
||||
|
||||
clone_depth: 5
|
||||
|
||||
# Build Configurations: dll/static, regular/debug
|
||||
configuration:
|
||||
- dynamic
|
||||
- static
|
||||
- dynamic-debug
|
||||
- static-debug
|
||||
|
||||
# Default OS Image
|
||||
image: Visual Studio 2019
|
||||
|
||||
# Environment variables: compiler toolchain, base version, setup file, ...
|
||||
environment:
|
||||
# common / default variables for all jobs
|
||||
SETUP_PATH: .ci-local:.ci
|
||||
|
||||
matrix:
|
||||
- BASE: 3.15
|
||||
CMP: vs2019
|
||||
- BASE: 7.0
|
||||
CMP: vs2019
|
||||
- BASE: 7.0
|
||||
CMP: gcc
|
||||
- BASE: 7.0
|
||||
CMP: vs2017
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
|
||||
# Platform: processor architecture
|
||||
platform:
|
||||
- x64
|
||||
|
||||
#---------------------------------#
|
||||
# building & testing #
|
||||
#---------------------------------#
|
||||
|
||||
install:
|
||||
- cmd: git submodule update --init --recursive
|
||||
- cmd: pip install git+https://github.com/mdavidsaver/ci-core-dumper#egg=ci-core-dumper
|
||||
- cmd: python .ci/cue.py prepare
|
||||
|
||||
build_script:
|
||||
- cmd: python .ci/cue.py build
|
||||
|
||||
test_script:
|
||||
- cmd: python -m ci_core_dumper install
|
||||
- cmd: python .ci/cue.py -T 20M test
|
||||
|
||||
on_finish:
|
||||
- ps: Get-ChildItem *.tap -Recurse -Force | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
|
||||
- cmd: python .ci/cue.py -T 5M test-results
|
||||
|
||||
on_failure:
|
||||
- cmd: python -m ci_core_dumper report
|
||||
|
||||
#---------------------------------#
|
||||
# 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: Email
|
||||
to:
|
||||
- core-talk@aps.anl.gov
|
||||
on_build_success: false
|
||||
- provider: GitHubPullRequest
|
||||
1
.ci
Submodule
1
.ci
Submodule
Submodule .ci added at 130e88b709
14
.ci-local/defaults.set
Normal file
14
.ci-local/defaults.set
Normal file
@@ -0,0 +1,14 @@
|
||||
# EPICS Base
|
||||
BASE_DIRNAME=base
|
||||
BASE_REPONAME=epics-base
|
||||
BASE_REPOOWNER=epics-base
|
||||
BASE_VARNAME=EPICS_BASE
|
||||
BASE_RECURSIVE=NO
|
||||
|
||||
MODULES=PVDATA PVACCESS
|
||||
|
||||
PVDATA_REPONAME=pvDataCPP
|
||||
PVDATA_REPOOWNER=epics-base
|
||||
|
||||
PVACCESS_REPONAME=pvAccessCPP
|
||||
PVACCESS_REPOOWNER=epics-base
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e -x
|
||||
|
||||
make -j2 $EXTRA
|
||||
|
||||
if [ "$TEST" != "NO" ]
|
||||
then
|
||||
make -j2 tapfiles
|
||||
make -j2 -s test-results
|
||||
fi
|
||||
@@ -1,109 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e -x
|
||||
|
||||
CURDIR="$PWD"
|
||||
|
||||
cat << EOF > $CURDIR/configure/RELEASE.local
|
||||
EPICS_BASE=$HOME/.source/epics-base
|
||||
EOF
|
||||
|
||||
install -d "$HOME/.source"
|
||||
cd "$HOME/.source"
|
||||
|
||||
add_gh_flat() {
|
||||
MODULE=$1
|
||||
REPOOWNER=$2
|
||||
REPONAME=$3
|
||||
BRANCH=$4
|
||||
MODULE_UC=$(echo $MODULE | tr 'a-z' 'A-Z')
|
||||
( git clone --quiet --depth 5 --branch $BRANCH https://github.com/$REPOOWNER/$REPONAME.git $MODULE && \
|
||||
cd $MODULE && git log -n1 )
|
||||
cat < $CURDIR/configure/RELEASE.local > $MODULE/configure/RELEASE.local
|
||||
cat << EOF >> $CURDIR/configure/RELEASE.local
|
||||
${MODULE_UC}=$HOME/.source/$MODULE
|
||||
EOF
|
||||
}
|
||||
|
||||
# not recursive
|
||||
git clone --quiet --depth 5 --branch "$BRBASE" https://github.com/${REPOBASE:-epics-base}/epics-base.git epics-base
|
||||
(cd epics-base && git log -n1 )
|
||||
add_gh_flat pvData ${REPOPVD:-epics-base} pvDataCPP ${BRPVD:-master}
|
||||
add_gh_flat pvAccess ${REPOPVA:-epics-base} pvAccessCPP ${BRPVA:-master}
|
||||
|
||||
if [ -e $CURDIR/configure/RELEASE.local ]
|
||||
then
|
||||
cat $CURDIR/configure/RELEASE.local
|
||||
fi
|
||||
|
||||
EPICS_HOST_ARCH=`sh epics-base/startup/EpicsHostArch`
|
||||
|
||||
# requires wine and g++-mingw-w64-i686
|
||||
if [ "$WINE" = "32" ]
|
||||
then
|
||||
echo "Cross mingw32"
|
||||
sed -i -e '/CMPLR_PREFIX/d' epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw
|
||||
cat << EOF >> epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw
|
||||
CMPLR_PREFIX=i686-w64-mingw32-
|
||||
EOF
|
||||
cat << EOF >> epics-base/configure/CONFIG_SITE
|
||||
CROSS_COMPILER_TARGET_ARCHS+=win32-x86-mingw
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [ "$STATIC" = "YES" ]
|
||||
then
|
||||
echo "Build static libraries/executables"
|
||||
cat << EOF >> epics-base/configure/CONFIG_SITE
|
||||
SHARED_LIBRARIES=NO
|
||||
STATIC_BUILD=YES
|
||||
EOF
|
||||
fi
|
||||
|
||||
case "$CMPLR" in
|
||||
clang)
|
||||
echo "Host compiler is clang"
|
||||
cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.$EPICS_HOST_ARCH
|
||||
GNU = NO
|
||||
CMPLR_CLASS = clang
|
||||
CC = clang
|
||||
CCC = clang++
|
||||
EOF
|
||||
|
||||
# hack
|
||||
sed -i -e 's/CMPLR_CLASS = gcc/CMPLR_CLASS = clang/' epics-base/configure/CONFIG.gnuCommon
|
||||
|
||||
clang --version
|
||||
;;
|
||||
*)
|
||||
echo "Host compiler is default"
|
||||
gcc --version
|
||||
;;
|
||||
esac
|
||||
|
||||
cat <<EOF >> epics-base/configure/CONFIG_SITE
|
||||
USR_CPPFLAGS += $USR_CPPFLAGS
|
||||
USR_CFLAGS += $USR_CFLAGS
|
||||
USR_CXXFLAGS += $USR_CXXFLAGS
|
||||
EOF
|
||||
|
||||
# set RTEMS to eg. "4.9" or "4.10"
|
||||
# requires qemu, bison, flex, texinfo, install-info
|
||||
if [ -n "$RTEMS" ]
|
||||
then
|
||||
echo "Cross RTEMS${RTEMS} for pc386"
|
||||
curl -L "https://github.com/mdavidsaver/rsb/releases/download/20171203-${RTEMS}/i386-rtems${RTEMS}-trusty-20171203-${RTEMS}.tar.bz2" \
|
||||
| tar -C / -xmj
|
||||
|
||||
sed -i -e '/^RTEMS_VERSION/d' -e '/^RTEMS_BASE/d' epics-base/configure/os/CONFIG_SITE.Common.RTEMS
|
||||
cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.RTEMS
|
||||
RTEMS_VERSION=$RTEMS
|
||||
RTEMS_BASE=$HOME/.rtems
|
||||
EOF
|
||||
cat << EOF >> epics-base/configure/CONFIG_SITE
|
||||
CROSS_COMPILER_TARGET_ARCHS += RTEMS-pc386-qemu
|
||||
EOF
|
||||
fi
|
||||
|
||||
make -j2 -C epics-base $EXTRA
|
||||
make -j2 -C pvData $EXTRA
|
||||
make -j2 -C pvAccess $EXTRA
|
||||
163
.github/workflows/ci-scripts-build.yml
vendored
Normal file
163
.github/workflows/ci-scripts-build.yml
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
# .github/workflows/ci-scripts-build.yml for use with EPICS Base ci-scripts
|
||||
# (see: https://github.com/epics-base/ci-scripts)
|
||||
|
||||
# This is YAML - indentation levels are crucial
|
||||
|
||||
# Workflow name
|
||||
|
||||
name: Base
|
||||
|
||||
# Trigger on pushes and PRs to any branch
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'documentation/*'
|
||||
- '.*.yml'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'documentation/*'
|
||||
- '.*.yml'
|
||||
|
||||
env:
|
||||
SETUP_PATH: .ci-local:.ci
|
||||
EPICS_TEST_IMPRECISE_TIMING: YES
|
||||
|
||||
jobs:
|
||||
build-base:
|
||||
name: ${{ matrix.name }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
# Set environment variables from matrix parameters
|
||||
env:
|
||||
BASE: ${{ matrix.base }}
|
||||
CMP: ${{ matrix.cmp }}
|
||||
BCFG: ${{ matrix.configuration }}
|
||||
CI_CROSS_TARGETS: ${{ matrix.cross }}
|
||||
EXTRA: ${{ matrix.extra }}
|
||||
TEST: ${{ matrix.test }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Job names also name artifacts, character limitations apply
|
||||
include:
|
||||
- os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
base: "7.0"
|
||||
cross: "windows-x64-mingw"
|
||||
name: "7.0 Ub-20 gcc-9 + MinGW"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: static
|
||||
base: "7.0"
|
||||
cross: "windows-x64-mingw"
|
||||
name: "7.0 Ub-20 gcc-9 + MinGW, static"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: static
|
||||
base: "7.0"
|
||||
extra: "CMD_CXXFLAGS=-std=c++11"
|
||||
name: "7.0 Ub-20 gcc-9 C++11, static"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
cmp: clang
|
||||
configuration: default
|
||||
base: "7.0"
|
||||
name: "7.0 Ub-20 clang-10"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
cmp: clang
|
||||
configuration: default
|
||||
base: "7.0"
|
||||
extra: "CMD_CXXFLAGS=-std=c++11"
|
||||
name: "7.0 Ub-20 clang-10 C++11"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
base: "7.0"
|
||||
cross: "RTEMS-pc686-qemu@5"
|
||||
name: "7.0 Ub-20 gcc-9 + RT-5.1 pc686"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
base: "7.0"
|
||||
cross: "RTEMS-pc386-qemu@4.10"
|
||||
test: NO
|
||||
name: "7.0 Ub-20 gcc-9 + RT-4.10"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
base: "7.0"
|
||||
cross: "RTEMS-pc386-qemu@4.9"
|
||||
name: "7.0 Ub-20 gcc-9 + RT-4.9"
|
||||
|
||||
- os: macos-latest
|
||||
cmp: clang
|
||||
configuration: default
|
||||
base: "7.0"
|
||||
name: "7.0 MacOS clang-12"
|
||||
|
||||
- os: windows-2019
|
||||
cmp: vs2019
|
||||
configuration: default
|
||||
base: "7.0"
|
||||
name: "7.0 Win2019 MSC-19"
|
||||
extra: "CMD_CXXFLAGS=-analysis"
|
||||
|
||||
- os: windows-2019
|
||||
cmp: vs2019
|
||||
configuration: static
|
||||
base: "7.0"
|
||||
name: "7.0 Win2019 MSC-19, static"
|
||||
extra: "CMD_CXXFLAGS=-analysis"
|
||||
|
||||
- os: windows-2019
|
||||
cmp: vs2019
|
||||
configuration: debug
|
||||
base: "7.0"
|
||||
name: "7.0 Win2019 MSC-19, debug"
|
||||
|
||||
- os: windows-2019
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
base: "7.0"
|
||||
name: "7.0 Win2019 mingw"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
base: "3.15"
|
||||
wine: "64"
|
||||
name: "3.15 Ub-20 gcc-9 + MinGW"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- name: Automatic core dump analysis
|
||||
uses: mdavidsaver/ci-core-dumper@master
|
||||
- name: "apt-get install"
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install qemu-system-x86 g++-mingw-w64-x86-64 gdb
|
||||
if: runner.os == 'Linux'
|
||||
- name: Prepare and compile dependencies
|
||||
run: python .ci/cue.py prepare
|
||||
- name: Build main module
|
||||
run: python .ci/cue.py build
|
||||
- name: Run main module tests
|
||||
run: python .ci/cue.py -T 20M test
|
||||
- name: Upload tapfiles Artifact
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: tapfiles ${{ matrix.name }}
|
||||
path: '**/O.*/*.tap'
|
||||
if-no-files-found: ignore
|
||||
- name: Collect and show test results
|
||||
if: ${{ always() }}
|
||||
run: python .ci/cue.py -T 5M test-results
|
||||
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
|
||||
31
.travis.yml
31
.travis.yml
@@ -1,31 +0,0 @@
|
||||
sudo: false
|
||||
dist: trusty
|
||||
language: c++
|
||||
compiler:
|
||||
- gcc
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libreadline6-dev
|
||||
- libncurses5-dev
|
||||
- perl
|
||||
- clang
|
||||
- g++-mingw-w64-i686
|
||||
- qemu-system-x86
|
||||
install:
|
||||
- ./.ci/travis-prepare.sh
|
||||
script:
|
||||
- ./.ci/travis-build.sh
|
||||
env:
|
||||
- BRBASE=7.0
|
||||
- BRBASE=7.0 CMPLR=clang
|
||||
- BRBASE=7.0 EXTRA="CMD_CXXFLAGS=-std=c++98"
|
||||
- BRBASE=7.0 EXTRA="CMD_CXXFLAGS=-std=c++11"
|
||||
- BRBASE=7.0 CMPLR=clang EXTRA="CMD_CXXFLAGS=-std=c++11"
|
||||
- BRBASE=7.0 WINE=32 TEST=NO STATIC=YES
|
||||
- BRBASE=7.0 WINE=32 TEST=NO STATIC=NO
|
||||
- BRBASE=7.0 RTEMS=4.10 TEST=NO
|
||||
- BRBASE=7.0 RTEMS=4.9 TEST=NO
|
||||
- BRBASE=3.16
|
||||
- BRBASE=3.15
|
||||
|
||||
2
Doxyfile
2
Doxyfile
@@ -38,7 +38,7 @@ PROJECT_NAME = pvDatabaseCPP
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = 4.5.2
|
||||
PROJECT_NUMBER = 4.7.2
|
||||
|
||||
# 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Version number for the PV Database API and shared library
|
||||
|
||||
EPICS_PVDATABASE_MAJOR_VERSION = 4
|
||||
EPICS_PVDATABASE_MINOR_VERSION = 5
|
||||
EPICS_PVDATABASE_MINOR_VERSION = 7
|
||||
EPICS_PVDATABASE_MAINTENANCE_VERSION = 2
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
@@ -2,7 +2,63 @@
|
||||
|
||||
This document summarizes the changes to the module between releases.
|
||||
|
||||
## Release 4.5.2 (EPICS 7.0.3.2 May 2020)
|
||||
## Release 4.7.2 (EPICS 7.0.9, Feb 2025)
|
||||
|
||||
* Resolved issue with changed field set in the case where the top level (master)
|
||||
field ("_") is not requested by the client, but the master field callback causes
|
||||
all fields to be marked as updated, rather than only those fields that have
|
||||
actually been modified.
|
||||
|
||||
## Release 4.7.1 (EPICS 7.0.8, Dec 2023)
|
||||
|
||||
* Added data distributor plugin which can be used for distributing data between
|
||||
a group of clients. The plugin is triggered by the request string of the
|
||||
form:
|
||||
|
||||
`_[distributor=group:<group id>;set:<set_id>;trigger:<field_name>;updates:<n_updates>;mode:<update_mode>]`
|
||||
|
||||
The plugin parameters are optional and are described bellow:
|
||||
|
||||
- group: this parameter indicates a group that client application belongs to (default value: "default"); groups of clients are completely independent of each other
|
||||
|
||||
- set: this parameter designates a client set that application belongs to within its group (default value: "default")
|
||||
|
||||
- trigger: this is the PV structure field that distinguishes different channel updates (default value: "timeStamp"); for example, for area detector images one could use the "uniqueId" field of the NTND structure
|
||||
|
||||
- updates: this parameter configures how many sequential updates a client (or a set of clients) will receive before the data distributor starts updating the next one (default value: "1")
|
||||
|
||||
- mode: this parameter configures how channel updates are to be distributed between clients in a set:
|
||||
- one: update goes to one client per set
|
||||
- all: update goes to all clients in a set
|
||||
- default is "one" if client set id is not specified, and "all" if set id is specified
|
||||
|
||||
For more information and examples of usage see the [plugin documentation](dataDistributorPlugin.md).
|
||||
|
||||
## Release 4.7.0 (EPICS 7.0.7, Sep 2022)
|
||||
|
||||
* Added support for the whole structure (master field) server side plugins.
|
||||
The whole structure is identified as the `_` string, and a pvRequest string
|
||||
that applies a plugin to it takes the form:
|
||||
|
||||
`field(_[XYZ=A:3;B:uniqueId])`
|
||||
|
||||
where `XYZ` is the name of a specific filter plugin that takes parameters
|
||||
`A` and `B` with values `3` and `uniqueId` respectively.
|
||||
|
||||
## Release 4.6.0 (EPICS 7.0.6, Jul 2021)
|
||||
|
||||
* Access Security is now supported.
|
||||
* <b>special</b> has been revised and extended.
|
||||
* addRecord, removeRecord, processRecord, and traceRecord are replaced by pvdbcr versions.
|
||||
* <b>support</b> is DEPRECATED
|
||||
|
||||
## Release 4.5.3 (EPICS 7.0.5, Feb 2021)
|
||||
|
||||
* The previously deprecated destroy methods have been removed.
|
||||
Any application code that was previously calling these can just remove
|
||||
those calls.
|
||||
|
||||
## Release 4.5.2 (EPICS 7.0.3.2, May 2020)
|
||||
|
||||
* plugin support is new
|
||||
* fixed issues #53 and #52
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
TODO
|
||||
===========
|
||||
|
||||
|
||||
create more regression tests
|
||||
----------------
|
||||
|
||||
Currently only some simple tests exist. Most of the testing has been via the examples
|
||||
233
documentation/dataDistributorPlugin.md
Normal file
233
documentation/dataDistributorPlugin.md
Normal file
@@ -0,0 +1,233 @@
|
||||
# Data Distributor Plugin
|
||||
|
||||
The data distributor plugin enables distribution of channel data between
|
||||
multiple client applications. The plugin considers two basic use cases
|
||||
for a group of clients:
|
||||
|
||||
- For simple parallel processing where client applications do not need
|
||||
to share data all clients in a group receive n sequential updates
|
||||
in a round-robin fashion: client \#1 sees the first n updates, client \#2 the
|
||||
second n updates, and so on.
|
||||
|
||||
- For data analysis where several cooperating client applications must all
|
||||
see the same data in order to process it the applications are grouped
|
||||
into sets, and each set of clients receives the same number of sequential
|
||||
updates. The first n updates are sent to all members of client set #1, the second n updates are sent to all members of client set #2, and so on.
|
||||
|
||||
## Requirements
|
||||
|
||||
This plugin relies on the pvDatabase plugin framework and requires
|
||||
epics base version > 7.0.7
|
||||
|
||||
## Usage
|
||||
|
||||
The PV request object which triggers plugin instantiation is defined below:
|
||||
|
||||
```
|
||||
"_[distributor=group:<group id>;set:<set_id>;trigger:<field_name>;updates:<n_updates>;mode:<update_mode>]"
|
||||
```
|
||||
|
||||
The underscore character at the begining of the PV request object
|
||||
indicates that the data distributor will be targeting entire PV structure.
|
||||
The same PV request object format should work regardless of the language
|
||||
in which a particular client application is written.
|
||||
|
||||
The plugin parameters are the following:
|
||||
|
||||
- `group:<group_id>`: specifying a `group_id` names a group the client application belongs to (default value: `default`); clients with different group names are
|
||||
completely independent of each other
|
||||
|
||||
- `set:<set_id>`: this parameter designates a client set that application belongs to within its group (default value: `default`)
|
||||
|
||||
- `trigger:<field_name>`: this is the PV structure field that distinguishes
|
||||
different channel updates (default value: `timeStamp`); for example,
|
||||
for area detector images one could use the `uniqueId` field of the NTND
|
||||
structure
|
||||
|
||||
- `updates:<n_updates>`: this parameter must be an integer and configures how many sequential updates a client (or a set of clients) will receive before the data distributor starts updating the next one (default value: `1`)
|
||||
|
||||
- `mode:<update_mode>`: this parameter configures how channel updates are to be
|
||||
distributed between clients in a set:
|
||||
- `one`: update goes to one client per set
|
||||
- `all`: update goes to all clients in a set
|
||||
- default is `one` if client set id is not specified, and `all` if set
|
||||
id is specified
|
||||
|
||||
The plugin obeys the following rules:
|
||||
|
||||
- Parameter names are case insensitive, but the string values
|
||||
are not. For example, "group=abc" and "group=ABC" would indicate two
|
||||
different groups of clients. String values allow alphanumeric characters,
|
||||
as well as dashes and underscores.
|
||||
|
||||
- Updates for a set of clients are configured when the first client in
|
||||
the set requests data. Configuration values (i.e., "trigger",
|
||||
"updates", and "mode"), passed in the PV request by the subsequent
|
||||
clients are ignored.
|
||||
|
||||
- A set is removed from the group once the last client in that
|
||||
set disconnects.
|
||||
|
||||
- A group is removed from the distributor plugin once all of its
|
||||
clients have disconnected.
|
||||
|
||||
- Different client groups are completely independent of each other.
|
||||
In other words, channel updates sent to clients belonging to
|
||||
group A do not interfere with updates sent to clients
|
||||
belonging to group B.
|
||||
|
||||
- The order in which clients and groups receive data is on a
|
||||
"first connected, first served basis".
|
||||
|
||||
- The current channel PV object is always distributed to a client on an
|
||||
initial connect.
|
||||
|
||||
- Data distribution is dynamic with respect to the number of clients.
|
||||
As clients connect and disconnect, the data distribution in a group adjusts
|
||||
accordingly. For example, with a group of clients configured to
|
||||
distribute one sequential update to each client, three clients would each be
|
||||
receiving every third update; after client number four connects, all
|
||||
clients would start receiving every fourth update; if one of those then
|
||||
disconnects, remaining three clients would again be receiving every third
|
||||
update.
|
||||
|
||||
## Examples
|
||||
|
||||
For all examples below we assume that PVDatabase server is serving
|
||||
area detector images on the channel 'image'. All clients are started before
|
||||
the server itself, and the initial (empty) object has unique id of 0.
|
||||
|
||||
### Example 1
|
||||
|
||||
This example show behavior of three clients that belong to the same (default)
|
||||
group. Each client receives one sequential update in a round-robin fashion.
|
||||
Note that all clients received current object on initial connection,
|
||||
and every third object afterward:
|
||||
|
||||
Client 1:
|
||||
```
|
||||
$ pvget -m -r _[distributor=trigger:uniqueId] image | grep uniqueId
|
||||
int uniqueId 0
|
||||
int uniqueId 1
|
||||
int uniqueId 4
|
||||
int uniqueId 7
|
||||
int uniqueId 10
|
||||
```
|
||||
|
||||
Client 2:
|
||||
```
|
||||
$ pvget -m -r _[distributor=trigger:uniqueId] image | grep uniqueId
|
||||
int uniqueId 0
|
||||
int uniqueId 2
|
||||
int uniqueId 5
|
||||
int uniqueId 8
|
||||
int uniqueId 11
|
||||
```
|
||||
|
||||
Client 3:
|
||||
```
|
||||
$ pvget -m -r _[distributor=trigger:uniqueId] image | grep uniqueId
|
||||
int uniqueId 0
|
||||
int uniqueId 3
|
||||
int uniqueId 6
|
||||
int uniqueId 9
|
||||
int uniqueId 12
|
||||
```
|
||||
|
||||
### Example 2
|
||||
|
||||
In this example we have two sets of two clients, each client set receiving
|
||||
three sequential updates. Both clients from client set \#1 receive updates
|
||||
(1,2,3), both clients from client set \#2 receive updates (4,5,6),
|
||||
client set \#1 receives updates (7,8,9), and so on.
|
||||
|
||||
Client 1 and Client 2/Set 1:
|
||||
```
|
||||
$ pvget -m -r "_[distributor=set:S1;trigger:uniqueId;updates:3]" image | grep uniqueId
|
||||
int uniqueId 0
|
||||
int uniqueId 1
|
||||
int uniqueId 2
|
||||
int uniqueId 3
|
||||
int uniqueId 7
|
||||
int uniqueId 8
|
||||
int uniqueId 9
|
||||
int uniqueId 13
|
||||
int uniqueId 14
|
||||
int uniqueId 15
|
||||
```
|
||||
|
||||
Client 3 and Client 4/Set 2:
|
||||
```
|
||||
$ pvget -m -r "_[distributor=set:S2;trigger:uniqueId;updates:3]" image | grep uniqueId
|
||||
int uniqueId 0
|
||||
int uniqueId 4
|
||||
int uniqueId 5
|
||||
int uniqueId 6
|
||||
int uniqueId 10
|
||||
int uniqueId 11
|
||||
int uniqueId 12
|
||||
int uniqueId 16
|
||||
int uniqueId 17
|
||||
int uniqueId 18
|
||||
```
|
||||
|
||||
### Example 3
|
||||
|
||||
This example illustrates what happens when multiple independent groups of
|
||||
clients connect to the same channel. Group G1 has two clients belonging
|
||||
to the same default set, and requesting one sequential update per client, while
|
||||
Group G2 has two clients in the default set requesting three
|
||||
sequential updates per client.
|
||||
|
||||
In this case the first client in group G1 receives updates
|
||||
(1,3,5,...), while the second one receives updates (2,4,6,...). On the
|
||||
other hand, the first client in group G2 receives updates
|
||||
(1,2,3,7,8,9,...), while the second one receives updates (4,5,6,10,11,12,...).
|
||||
|
||||
Client 1/Group G1:
|
||||
```
|
||||
$ pvget -m -r "_[distributor=group:G1;trigger:uniqueId]" image | grep uniqueId
|
||||
int uniqueId 0
|
||||
int uniqueId 1
|
||||
int uniqueId 3
|
||||
int uniqueId 5
|
||||
int uniqueId 7
|
||||
int uniqueId 9
|
||||
```
|
||||
|
||||
Client 2/Group G1:
|
||||
```
|
||||
pvget -m -r "_[distributor=group:G1;trigger:uniqueId]" image | grep uniqueId
|
||||
int uniqueId 0
|
||||
int uniqueId 2
|
||||
int uniqueId 4
|
||||
int uniqueId 6
|
||||
int uniqueId 8
|
||||
```
|
||||
|
||||
Client 1/Group G2:
|
||||
```
|
||||
$ pvget -m -r "_[distributor=group:G2;trigger:uniqueId;updates:3]" image | grep uniqueId
|
||||
int uniqueId 0
|
||||
int uniqueId 1
|
||||
int uniqueId 2
|
||||
int uniqueId 3
|
||||
int uniqueId 7
|
||||
int uniqueId 8
|
||||
int uniqueId 9
|
||||
```
|
||||
|
||||
Client 2/Group G2:
|
||||
```
|
||||
$ pvget -m -r "_[distributor=group:G2;trigger:uniqueId;updates:3]" image | grep uniqueId
|
||||
int uniqueId 0
|
||||
int uniqueId 4
|
||||
int uniqueId 5
|
||||
int uniqueId 6
|
||||
int uniqueId 10
|
||||
int uniqueId 11
|
||||
int uniqueId 12
|
||||
```
|
||||
|
||||
The above shows that the two client groups do not interfere with each other.
|
||||
|
||||
@@ -4,72 +4,66 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
|
||||
<title>pvDatabaseCPP</title>
|
||||
<title>EPICS pvDatabaseCPP</title>
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="http://epics-pvdata.sourceforge.net/base.css" />
|
||||
href="https://mrkraimer.github.io/website/css/base.css" />
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="http://epics-pvdata.sourceforge.net/epicsv4.css" />
|
||||
href="https://mrkraimer.github.io/website/css/epicsv4.css" />
|
||||
<style type="text/css">
|
||||
/*<![CDATA[*/
|
||||
/*<![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>
|
||||
body { margin-right: 10% }
|
||||
/*]]>*/</style>
|
||||
|
||||
<!-- Script that generates the Table of Contents -->
|
||||
<script type="text/javascript" src="https://mrkraimer.github.io/website/css/tocgen.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="head">
|
||||
<h1>pvDatabaseCPP</h1>
|
||||
<h2 class="nocount">Release ? - TBD</h2>
|
||||
Latest update 2019.09.11.
|
||||
|
||||
<h1>EPICS pvDatabaseCPP</h1>
|
||||
<h2 class="nocount">Release 4.6.0 - March 2021</h2>
|
||||
|
||||
<h2 class="nocount">Abstract</h2>
|
||||
|
||||
<p>This document describes pvDatabaseCPP,
|
||||
which is a framework for implementing a network accessible database of smart memory resident
|
||||
<p><b>pvDatabase</b> is a framework for implementing a network accessible database of smart memory resident
|
||||
records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by
|
||||
pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess.
|
||||
The framework can be extended in order to create record instances that implements services.
|
||||
The minimum that an extension must provide is a top level PVStructure and a process method.
|
||||
</p>
|
||||
|
||||
<!-- last para of Abstract is boilerplate reference to EPICS -->
|
||||
<p>For more information about EPICS generally, please refer to the home page of the <a
|
||||
href="http://www.aps.anl.gov/epics/">Experimental Physics and Industrial
|
||||
Control System</a>.</p>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div id="toc">
|
||||
<h2 class="nocount">Table of Contents</h2>
|
||||
</div>
|
||||
</div> <!-- head -->
|
||||
|
||||
<div id="contents" class="contents">
|
||||
<hr />
|
||||
|
||||
<h2>Overview</h2>
|
||||
<p>
|
||||
Documentation for pvDatabaseCPP is available at:
|
||||
<a
|
||||
href="https://mrkraimer.github.io/website/developerGuide/pvDatabase/pvDatabaseCPP.html">
|
||||
pvDatabase
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
pvDatabaseCPP is one of the components of
|
||||
EPICS Version 7
|
||||
<a href="https://epics-controls.org/resources-and-support/base/epics-7/">
|
||||
EPICS-7
|
||||
</a>
|
||||
</p>
|
||||
<p>This document is only a guide to help locate code and documentation related to pvDatabaseCPP
|
||||
</p>
|
||||
<p>
|
||||
It is intended for developers that want to use pvDatabaseCPP.
|
||||
</p>
|
||||
<h2>Developer Guide</h2>
|
||||
@@ -79,204 +73,28 @@ href="https://mrkraimer.github.io/website/developerGuide/developerGuide.html">
|
||||
developerGuide
|
||||
</a>
|
||||
</p>
|
||||
<p>This guide discusses all the components that are part of an <b>EPICS V4</b> release.
|
||||
<p>This guide provides an overview of the components that are part of an <b>EPICS V4</b> release.
|
||||
Some understanding of the components and how they are related is necessary in order to
|
||||
develop code that uses pvDatabaseCPP.
|
||||
In particular read everything related to pvDatabase.
|
||||
</p>
|
||||
<p>pvDatabase has plugin support, which is implemented in <b>pvCopy</b>.
|
||||
<b>pvCopy</b> was originally implemented in <b>pvDataCPP</b>,
|
||||
but pvDatabaseCPP now implements its own version and adds plugin support.
|
||||
</p>
|
||||
<p>
|
||||
See
|
||||
<a
|
||||
href="https://mrkraimer.github.io/website/pvRequest/pvRequest.html">
|
||||
pvRequest
|
||||
</a>
|
||||
for details.
|
||||
</p>
|
||||
<p>The developerGuide discusses code in a way that applies to both CPP and C++.
|
||||
For the descriptions of the CPP specific code consult the following sections.
|
||||
In particular read everything related to pvaClient.
|
||||
</p>
|
||||
|
||||
<h2>doxygen</h2>
|
||||
<p>doxygen documentation is available at
|
||||
<a
|
||||
href="./html/index.html">doxgen</a>
|
||||
href="./html/index.html">doxygen</a>
|
||||
</p>
|
||||
|
||||
<h2>pvDatabaseCPP</h2>
|
||||
<h3>include/pv</h3>
|
||||
<p>The header files that describe the various components implemented by pvDatabase.
|
||||
</p>
|
||||
<dl>
|
||||
<dt>pvDatabase.h</dt>
|
||||
<dd>
|
||||
This describes PVRecord and PVDatabase.
|
||||
</dd>
|
||||
<dt>channelProviderLocal.h </dt>
|
||||
<dd>
|
||||
This describes a channel provider for PVDatabase
|
||||
</dd>
|
||||
<dt>pvSupport.h</dt>
|
||||
<dd>
|
||||
This is the base class for support attached to a field of a record.
|
||||
</dd>
|
||||
<dt>controlSupport.h</dt>
|
||||
<dd>
|
||||
This is support that implements control limits.
|
||||
</dd>
|
||||
<dt>scalarAlarmSupport.h</dt>
|
||||
<dd>
|
||||
This is support for a alarm limits for a scalar numeric field.
|
||||
</dd>
|
||||
<dt>processRecord.h</dt>
|
||||
<dd>
|
||||
This is a PVRecord that periodical processes a set of PVRecords in the local PVDatabase.
|
||||
</dd>
|
||||
<dt>addRecord.h</dt>
|
||||
<dd>
|
||||
This is a PVRecord that adds a new PVRecord to the local PVDatabase.
|
||||
</dd>
|
||||
<dt>removeRecord.h</dt>
|
||||
<dd>
|
||||
This is a PVRecord that removes a PVRecord in the local PVDatabase.
|
||||
</dd>
|
||||
<dt>traceRecord.h</dt>
|
||||
<dd>
|
||||
This is a PVRecord that sets the trace value for another PVRecord in the local PVDatabase.
|
||||
</dd>
|
||||
<dt>pvStructureCopy.h</dt>
|
||||
<dd>
|
||||
This is a facility that allows a client to access a subfield of the fields in a PVRecord.
|
||||
It also provides record and field options an plugin support.
|
||||
</dd>
|
||||
<dt>pvPlugin.h</dt>
|
||||
<dd>
|
||||
This is the base class for a plugin attached to a record or field of PVRecord.
|
||||
</dd>
|
||||
<dt>pvArrayPlugin.h</dt>
|
||||
<dd>
|
||||
A plugin for accessing a subset of the elements in an array field.
|
||||
</dd>
|
||||
<dt>pvDeadbandPlugin.h</dt>
|
||||
<dd>
|
||||
A deadband plugin for monitors.
|
||||
</dd>
|
||||
<dt>pvTimestampPlugin.h</dt>
|
||||
<dd>
|
||||
A plugin for timeStamp.
|
||||
</dd>
|
||||
</dl>
|
||||
<h3>src/database</h3>
|
||||
<p>This has the code that implements pvDatabase and pvRecord.</p>
|
||||
<h3>src/pvAccess</h3>
|
||||
<p>This has the code for the channel provider for pvDatabase.
|
||||
</p>
|
||||
<h3>src/support</h3>
|
||||
<p>This has the pvSupport code.</p>
|
||||
<h3>src/special</h3>
|
||||
<p>
|
||||
This has the code for processRecord, addRecord, removeRecord, and traceRecord.
|
||||
</p>
|
||||
<h3>src/copy</h3>
|
||||
<p>This has the code for pvStructureCopy and all the plugin support.
|
||||
</p>
|
||||
<h2>exampleCPP</h2>
|
||||
<p>Example code is available as part of this release.
|
||||
<p>Example code is available at
|
||||
<a
|
||||
href="https://github.com/epics-base/exampleCPP">
|
||||
exampleCPP
|
||||
</a>
|
||||
</p>
|
||||
<p>In particular look at the example code mentioned in the following sub-sections.
|
||||
<p>In particular look at database, exampleLink, and helloPutGet.
|
||||
</p>
|
||||
|
||||
<h3>database</h3>
|
||||
<p>This has many examples of how to create both soft records and records that implement
|
||||
other functionality.</p>
|
||||
<dl>
|
||||
<dt>exampleDatabase.cpp</dt>
|
||||
<dd>
|
||||
This shows how to create soft records of each pvData type.<br />
|
||||
In addition shows how to create instances of the following two records.
|
||||
</dd>
|
||||
<dt>exampleHelloRecord.cpp</dt>
|
||||
<dd>
|
||||
This is a simple "hello world" that is intentended to be used via a channelPutGet request.
|
||||
</dd>
|
||||
<dt>exampleHelloRPC.cpp</dt>
|
||||
<dd>
|
||||
This is a simple "hello world" that is intentended to be used via a channelRPC request.
|
||||
</dd>
|
||||
<dt>exampleDatabaseMain.cpp</dt>
|
||||
<dd>
|
||||
This shows how to create a standalone IOC.
|
||||
</dd>
|
||||
<dt>ioc and iocBoot</dt>
|
||||
<dd>
|
||||
This has code and examples to create a V3 IOC which also has a PVDatabase.
|
||||
</dd>
|
||||
</dl>
|
||||
<h3>exampleLink</h3>
|
||||
<p>This shows how to implement a record that has a link to another record</p>
|
||||
<dl>
|
||||
<dt>exampleMonitorLinkRecord</dt>
|
||||
<dd>
|
||||
This creates a monitor link to another record.
|
||||
</dd>
|
||||
<dt>exampleGetLinkRecord</dt>
|
||||
<dd>
|
||||
This creates a get link to another record.
|
||||
</dd>
|
||||
<dt>examplePutLinkRecord</dt>
|
||||
<dd>
|
||||
This creates a put link to another record.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<h3>support</h3>
|
||||
<p>This creates records that have the following features:</p>
|
||||
<dl>
|
||||
<dt>value</dt>
|
||||
<dd>
|
||||
Each record has a value field the is a numeric scalar field.
|
||||
In addition each has the following fields:
|
||||
alarm,timeStamp,control,scalarAlarm, and display.
|
||||
</dd>
|
||||
<dt>support</dt>
|
||||
<dd>
|
||||
Each record uses the control and scalarAlarm support provided by pvDatabaseCPP.
|
||||
</dd>
|
||||
</dl>
|
||||
<p>
|
||||
It also creates records that can be used by clients to show example of the plugin support.
|
||||
</p>
|
||||
|
||||
<h2>iocshell commands</h2>
|
||||
<p>Shell commands are made available via the standard DBD include mechanism
|
||||
provided by iocCore.
|
||||
The following provide EPICS V4 shell commands:</p>
|
||||
<pre>
|
||||
pvAccessCPP
|
||||
qsrv
|
||||
pvDatabaseCPP
|
||||
</pre>
|
||||
|
||||
<p>pvDatabaseCPP provides the following iocshell command.</p>
|
||||
<dl>
|
||||
<dt>registerChannelProviderLocal</dt>
|
||||
<dd>Including <b>registerChannelProviderLocal.dbd</b> as a dbd file automatically starts provider local
|
||||
and also creates the pvdbl shell command.
|
||||
</dd>
|
||||
<dt>pvdbl</dt>
|
||||
<dd>Provides a list of all the pvRecords in database <b>master</b>
|
||||
</dd>
|
||||
</dl>
|
||||
<p>In addition any code that implements a PVRecord must implement an ioc command.
|
||||
Look at the examples in <b>exampleCPP/support</b> to see how to implement shell commands.</p>
|
||||
|
||||
</div>
|
||||
</div> <!-- class="contents" -->
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
# pvDatabase C++ implementation
|
||||
# Jenkins @ Cloudbees build script
|
||||
#
|
||||
# Jenkins invokes scripts with the "-ex" option. So the build is considered a failure
|
||||
# if any of the commands exits with a non-zero exit code.
|
||||
#
|
||||
# Author: Ralph Lange <ralph.lange@gmx.de>
|
||||
# Copyright (C) 2014 Helmholtz-Zentrum Berlin für Materialien und Energie GmbH
|
||||
# Copyright (C) 2014-2016 ITER Organization.
|
||||
# All rights reserved. Use is subject to license terms.
|
||||
|
||||
installTool () {
|
||||
local module=$1
|
||||
local version=$2
|
||||
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/${module}-${version}_Build/lastSuccessfulBuild/artifact/${module,,}-${version}.CB-dist.tar.gz
|
||||
tar -xzf ${module,,}-${version}.CB-dist.tar.gz
|
||||
}
|
||||
|
||||
installE4 () {
|
||||
local module=$1
|
||||
local branch=$2
|
||||
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE}/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
|
||||
tar -xzf ${module}.CB-dist.tar.gz
|
||||
}
|
||||
|
||||
###########################################
|
||||
# Defaults for EPICS Base
|
||||
|
||||
DEFAULT_BASE=3.15.4
|
||||
BASE=${BASE:-${DEFAULT_BASE}}
|
||||
|
||||
###########################################
|
||||
# Dependent module branches
|
||||
|
||||
PVDATA_BRANCH="master"
|
||||
PVACCESS_BRANCH="master"
|
||||
|
||||
###########################################
|
||||
# Fetch and unpack dependencies
|
||||
|
||||
export STUFF=/tmp/stuff
|
||||
|
||||
rm -fr ${STUFF}
|
||||
mkdir -p ${STUFF}
|
||||
cd ${STUFF}
|
||||
|
||||
installTool Boost 1.61.0
|
||||
installTool Base ${BASE}
|
||||
|
||||
installE4 pvData ${PVDATA_BRANCH}
|
||||
installE4 pvAccess ${PVACCESS_BRANCH}
|
||||
|
||||
###########################################
|
||||
# Build
|
||||
|
||||
cd ${WORKSPACE}
|
||||
|
||||
export EPICS_BASE=${STUFF}
|
||||
export EPICS_HOST_ARCH=$(${EPICS_BASE}/startup/EpicsHostArch)
|
||||
export LD_LIBRARY_PATH=${EPICS_BASE}/lib/${EPICS_HOST_ARCH}
|
||||
export PATH=${STUFF}/bin:${PATH}
|
||||
|
||||
cat > configure/RELEASE.local << EOF
|
||||
EPICS_BASE=${EPICS_BASE}
|
||||
EOF
|
||||
|
||||
make distclean all
|
||||
|
||||
###########################################
|
||||
# Test
|
||||
|
||||
make runtests
|
||||
|
||||
###########################################
|
||||
# Create cache
|
||||
|
||||
tar -czf pvDatabase.CB-dist.tar.gz lib include dbd LICENSE
|
||||
@@ -1,66 +0,0 @@
|
||||
# pvDatabase C++ implementation
|
||||
# Jenkins @ Cloudbees documentation generation and deployment
|
||||
#
|
||||
# Jenkins invokes scripts with the "-ex" option. So the build is considered a failure
|
||||
# if any of the commands exits with a non-zero exit code.
|
||||
#
|
||||
# Author: Ralph Lange <ralph.lange@gmx.de>
|
||||
# Copyright (C) 2014 Helmholtz-Zentrum Berlin für Materialien und Energie GmbH
|
||||
# Copyright (C) 2014-2016 ITER Organization.
|
||||
# All rights reserved. Use is subject to license terms.
|
||||
|
||||
installTool () {
|
||||
local module=$1
|
||||
local version=$2
|
||||
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/${module}-${version}_Build/lastSuccessfulBuild/artifact/${module,,}-${version}.CB-dist.tar.gz
|
||||
tar -xzf ${module,,}-${version}.CB-dist.tar.gz
|
||||
}
|
||||
|
||||
installE4 () {
|
||||
local module=$1
|
||||
local branch=$2
|
||||
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE}/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
|
||||
tar -xzf ${module}.CB-dist.tar.gz
|
||||
}
|
||||
|
||||
###########################################
|
||||
# Defaults for EPICS Base and parameters
|
||||
|
||||
BASE=3.15.4
|
||||
PUBLISH=${PUBLISH:-NO}
|
||||
BRANCH=${BRANCH:-master}
|
||||
|
||||
###########################################
|
||||
# Fetch and unpack dependencies
|
||||
|
||||
export STUFF=/tmp/stuff
|
||||
|
||||
rm -fr ${STUFF}
|
||||
mkdir -p ${STUFF}
|
||||
cd ${STUFF}
|
||||
|
||||
installTool Doxygen 1.8.11
|
||||
|
||||
###########################################
|
||||
# Generate
|
||||
|
||||
cd ${WORKSPACE}
|
||||
|
||||
installE4 pvDatabase ${BRANCH}
|
||||
|
||||
export PATH=${STUFF}/bin:${PATH}
|
||||
|
||||
doxygen
|
||||
|
||||
###########################################
|
||||
# Publish
|
||||
|
||||
if [ "${PUBLISH}" != "DONT" ]; then
|
||||
# Upload explicit dummy to ensure target directory exists
|
||||
echo "Created by CloudBees Jenkins upload job. Should be deleted as part of the job." > DUMMY
|
||||
rsync -q -e ssh DUMMY epics-jenkins@web.sourceforge.net:/home/project-web/epics-pvdata/htdocs/docbuild/pvDatabaseCPP/${PUBLISH}/
|
||||
|
||||
rsync -aqP --delete -e ssh documentation epics-jenkins@web.sourceforge.net:/home/project-web/epics-pvdata/htdocs/docbuild/pvDatabaseCPP/${PUBLISH}/
|
||||
fi
|
||||
32
src/Makefile
32
src/Makefile
@@ -3,15 +3,8 @@
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
# needed for Windows
|
||||
LIB_SYS_LIBS_WIN32 += netapi32 ws2_32
|
||||
|
||||
PVDATABASE_SRC = $(TOP)/src
|
||||
|
||||
LIBRARY += pvDatabase
|
||||
|
||||
# shared library ABI version.
|
||||
SHRLIB_VERSION ?= $(EPICS_PVDATABASE_MAJOR_VERSION).$(EPICS_PVDATABASE_MINOR_VERSION).$(EPICS_PVDATABASE_MAINTENANCE_VERSION)
|
||||
|
||||
INC += pv/pvPlugin.h
|
||||
INC += pv/pvStructureCopy.h
|
||||
@@ -23,23 +16,32 @@ INC += pv/pvDatabase.h
|
||||
|
||||
INC += pv/channelProviderLocal.h
|
||||
|
||||
INC += pv/traceRecord.h
|
||||
INC += pv/removeRecord.h
|
||||
INC += pv/addRecord.h
|
||||
INC += pv/processRecord.h
|
||||
|
||||
INC += pv/pvSupport.h
|
||||
INC += pv/controlSupport.h
|
||||
INC += pv/scalarAlarmSupport.h
|
||||
|
||||
INC += pv/pvdbcrScalarRecord.h
|
||||
INC += pv/pvdbcrScalarArrayRecord.h
|
||||
INC += pv/pvdbcrAddRecord.h
|
||||
INC += pv/pvdbcrRemoveRecord.h
|
||||
INC += pv/pvdbcrProcessRecord.h
|
||||
INC += pv/pvdbcrTraceRecord.h
|
||||
|
||||
include $(PVDATABASE_SRC)/copy/Makefile
|
||||
include $(PVDATABASE_SRC)/database/Makefile
|
||||
include $(PVDATABASE_SRC)/pvAccess/Makefile
|
||||
include $(PVDATABASE_SRC)/special/Makefile
|
||||
include $(PVDATABASE_SRC)/support/Makefile
|
||||
include $(PVDATABASE_SRC)/special/Makefile
|
||||
|
||||
pvDatabase_LIBS += $(EPICS_BASE_PVA_CORE_LIBS)
|
||||
pvDatabase_LIBS += $(EPICS_BASE_IOC_LIBS)
|
||||
LIBRARY += pvDatabase
|
||||
pvDatabase_LIBS += pvData
|
||||
pvDatabase_LIBS += pvAccess
|
||||
LIB_LIBS += Com
|
||||
|
||||
# shared library ABI version.
|
||||
SHRLIB_VERSION ?= $(EPICS_PVDATABASE_MAJOR_VERSION).$(EPICS_PVDATABASE_MINOR_VERSION).$(EPICS_PVDATABASE_MAINTENANCE_VERSION)
|
||||
|
||||
# needed for Windows
|
||||
LIB_SYS_LIBS_WIN32 += netapi32 ws2_32
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
@@ -8,5 +8,6 @@ LIBSRCS += pvCopy.cpp
|
||||
LIBSRCS += pvArrayPlugin.cpp
|
||||
LIBSRCS += pvDeadbandPlugin.cpp
|
||||
LIBSRCS += pvTimestampPlugin.cpp
|
||||
LIBSRCS += dataDistributorPlugin.cpp
|
||||
|
||||
|
||||
|
||||
428
src/copy/dataDistributorPlugin.cpp
Normal file
428
src/copy/dataDistributorPlugin.cpp
Normal file
@@ -0,0 +1,428 @@
|
||||
// Copyright information and license terms for this software can be
|
||||
// found in the file LICENSE that is included with the distribution
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "pv/dataDistributorPlugin.h"
|
||||
|
||||
using std::string;
|
||||
using std::size_t;
|
||||
using std::endl;
|
||||
using std::tr1::static_pointer_cast;
|
||||
using std::vector;
|
||||
using namespace epics::pvData;
|
||||
namespace epvd = epics::pvData;
|
||||
|
||||
namespace epics { namespace pvCopy {
|
||||
|
||||
// Utilities for manipulating strings
|
||||
static std::string leftTrim(const std::string& s)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int n = (unsigned int)s.length();
|
||||
for (i = 0; i < n; i++) {
|
||||
if (!isspace(s[i])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return s.substr(i,n-i);
|
||||
}
|
||||
|
||||
static std::string rightTrim(const std::string& s)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int n = (unsigned int)s.length();
|
||||
for (i = n; i > 0; i--) {
|
||||
if (!isspace(s[i-1])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return s.substr(0,i);
|
||||
}
|
||||
|
||||
static std::string trim(const std::string& s)
|
||||
{
|
||||
return rightTrim(leftTrim(s));
|
||||
}
|
||||
|
||||
static std::vector<std::string>& split(const std::string& s, char delimiter, std::vector<std::string>& elements)
|
||||
{
|
||||
std::stringstream ss(s);
|
||||
std::string item;
|
||||
while (std::getline(ss, item, delimiter)) {
|
||||
elements.push_back(trim(item));
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
static std::vector<std::string> split(const std::string& s, char delimiter)
|
||||
{
|
||||
std::vector<std::string> elements;
|
||||
split(s, delimiter, elements);
|
||||
return elements;
|
||||
}
|
||||
|
||||
static std::string toLowerCase(const std::string& input)
|
||||
{
|
||||
std::stringstream ss;
|
||||
for (unsigned int i = 0; i < input.size(); i++) {
|
||||
char c = std::tolower(input.at(i));
|
||||
ss << c;
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// Data distributor class
|
||||
|
||||
static std::string name("distributor");
|
||||
bool DataDistributorPlugin::initialized(false);
|
||||
|
||||
std::map<std::string, DataDistributorPtr> DataDistributor::dataDistributorMap;
|
||||
epics::pvData::Mutex DataDistributor::dataDistributorMapMutex;
|
||||
|
||||
DataDistributorPtr DataDistributor::getInstance(const std::string& groupId)
|
||||
{
|
||||
epvd::Lock lock(dataDistributorMapMutex);
|
||||
std::map<std::string,DataDistributorPtr>::iterator ddit = dataDistributorMap.find(groupId);
|
||||
if (ddit != dataDistributorMap.end()) {
|
||||
DataDistributorPtr ddPtr = ddit->second;
|
||||
return ddPtr;
|
||||
}
|
||||
else {
|
||||
DataDistributorPtr ddPtr(new DataDistributor(groupId));
|
||||
dataDistributorMap[groupId] = ddPtr;
|
||||
return ddPtr;
|
||||
}
|
||||
}
|
||||
|
||||
void DataDistributor::removeUnusedInstance(DataDistributorPtr dataDistributorPtr)
|
||||
{
|
||||
epvd::Lock lock(dataDistributorMapMutex);
|
||||
std::string groupId = dataDistributorPtr->getGroupId();
|
||||
std::map<std::string,DataDistributorPtr>::iterator ddit = dataDistributorMap.find(groupId);
|
||||
if (ddit != dataDistributorMap.end()) {
|
||||
DataDistributorPtr ddPtr = ddit->second;
|
||||
size_t nSets = ddPtr->clientSetMap.size();
|
||||
if (nSets == 0) {
|
||||
dataDistributorMap.erase(ddit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DataDistributor::DataDistributor(const std::string& groupId_)
|
||||
: groupId(groupId_)
|
||||
, mutex()
|
||||
, clientSetMap()
|
||||
, clientSetIdList()
|
||||
, currentSetIdIter(clientSetIdList.end())
|
||||
, lastUpdateValue()
|
||||
{
|
||||
}
|
||||
|
||||
DataDistributor::~DataDistributor()
|
||||
{
|
||||
epvd::Lock lock(mutex);
|
||||
clientSetMap.clear();
|
||||
clientSetIdList.clear();
|
||||
}
|
||||
|
||||
std::string DataDistributor::addClient(int clientId, const std::string& setId, const std::string& triggerField, int nUpdatesPerClient, int updateMode)
|
||||
{
|
||||
epvd::Lock lock(mutex);
|
||||
std::map<std::string,ClientSetPtr>::iterator git = clientSetMap.find(setId);
|
||||
if (git != clientSetMap.end()) {
|
||||
ClientSetPtr setPtr = git->second;
|
||||
setPtr->clientIdList.push_back(clientId);
|
||||
return setPtr->triggerField;
|
||||
}
|
||||
else {
|
||||
ClientSetPtr setPtr(new ClientSet(setId, triggerField, nUpdatesPerClient, updateMode));
|
||||
setPtr->clientIdList.push_back(clientId);
|
||||
clientSetMap[setId] = setPtr;
|
||||
clientSetIdList.push_back(setId);
|
||||
return triggerField;
|
||||
}
|
||||
}
|
||||
|
||||
void DataDistributor::removeClient(int clientId, const std::string& setId)
|
||||
{
|
||||
epvd::Lock lock(mutex);
|
||||
std::map<std::string,ClientSetPtr>::iterator git = clientSetMap.find(setId);
|
||||
if (git != clientSetMap.end()) {
|
||||
ClientSetPtr setPtr = git->second;
|
||||
std::list<int>::iterator cit = std::find(setPtr->clientIdList.begin(), setPtr->clientIdList.end(), clientId);
|
||||
if (cit != setPtr->clientIdList.end()) {
|
||||
// If we are removing current client id, advance iterator
|
||||
if (cit == setPtr->currentClientIdIter) {
|
||||
setPtr->currentClientIdIter++;
|
||||
}
|
||||
|
||||
// Find current client id
|
||||
int currentClientId = -1;
|
||||
if (setPtr->currentClientIdIter != setPtr->clientIdList.end()) {
|
||||
currentClientId = *(setPtr->currentClientIdIter);
|
||||
}
|
||||
|
||||
// Remove client id from the list
|
||||
setPtr->clientIdList.erase(cit);
|
||||
|
||||
// Reset current client id iterator
|
||||
setPtr->currentClientIdIter = setPtr->clientIdList.end();
|
||||
if (currentClientId >= 0) {
|
||||
std::list<int>::iterator cit2 = std::find(setPtr->clientIdList.begin(), setPtr->clientIdList.end(), currentClientId);
|
||||
if (cit2 != setPtr->clientIdList.end()) {
|
||||
setPtr->currentClientIdIter = cit2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (setPtr->clientIdList.size() == 0) {
|
||||
clientSetMap.erase(git);
|
||||
std::list<std::string>::iterator git2 = std::find(clientSetIdList.begin(), clientSetIdList.end(), setId);
|
||||
if (git2 == currentSetIdIter) {
|
||||
currentSetIdIter++;
|
||||
}
|
||||
if (git2 != clientSetIdList.end()) {
|
||||
clientSetIdList.erase(git2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DataDistributor::updateClient(int clientId, const std::string& setId, const std::string& triggerFieldValue)
|
||||
{
|
||||
epvd::Lock lock(mutex);
|
||||
bool proceedWithUpdate = false;
|
||||
if (currentSetIdIter == clientSetIdList.end()) {
|
||||
currentSetIdIter = clientSetIdList.begin();
|
||||
}
|
||||
std::string currentSetId = *currentSetIdIter;
|
||||
if (setId != currentSetId) {
|
||||
// We are not distributing data to this set at the moment
|
||||
return proceedWithUpdate;
|
||||
}
|
||||
ClientSetPtr setPtr = clientSetMap[currentSetId];
|
||||
if (setPtr->currentClientIdIter == setPtr->clientIdList.end()) {
|
||||
// Move current client iterator to the beginning of the list
|
||||
setPtr->currentClientIdIter = setPtr->clientIdList.begin();
|
||||
}
|
||||
if (lastUpdateValue == triggerFieldValue) {
|
||||
// This update was already distributed.
|
||||
return proceedWithUpdate;
|
||||
}
|
||||
switch (setPtr->updateMode) {
|
||||
case(DD_UPDATE_ONE_PER_GROUP): {
|
||||
if (clientId != *(setPtr->currentClientIdIter)) {
|
||||
// Not this client's turn.
|
||||
return proceedWithUpdate;
|
||||
}
|
||||
proceedWithUpdate = true;
|
||||
lastUpdateValue = triggerFieldValue;
|
||||
setPtr->lastUpdateValue = triggerFieldValue;
|
||||
setPtr->updateCounter++;
|
||||
if (setPtr->updateCounter >= setPtr->nUpdatesPerClient) {
|
||||
// This client and set are done.
|
||||
setPtr->currentClientIdIter++;
|
||||
setPtr->updateCounter = 0;
|
||||
currentSetIdIter++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(DD_UPDATE_ALL_IN_GROUP): {
|
||||
proceedWithUpdate = true;
|
||||
static unsigned int nClientsUpdated = 0;
|
||||
if (setPtr->lastUpdateValue != triggerFieldValue) {
|
||||
setPtr->lastUpdateValue = triggerFieldValue;
|
||||
setPtr->updateCounter++;
|
||||
nClientsUpdated = 0;
|
||||
}
|
||||
nClientsUpdated++;
|
||||
if (nClientsUpdated == setPtr->clientIdList.size() && setPtr->updateCounter >= setPtr->nUpdatesPerClient) {
|
||||
// This set is done.
|
||||
lastUpdateValue = triggerFieldValue;
|
||||
setPtr->updateCounter = 0;
|
||||
currentSetIdIter++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
proceedWithUpdate = true;
|
||||
}
|
||||
}
|
||||
return proceedWithUpdate;
|
||||
}
|
||||
|
||||
DataDistributorPlugin::DataDistributorPlugin()
|
||||
{
|
||||
}
|
||||
|
||||
DataDistributorPlugin::~DataDistributorPlugin()
|
||||
{
|
||||
}
|
||||
|
||||
void DataDistributorPlugin::create()
|
||||
{
|
||||
initialize();
|
||||
}
|
||||
|
||||
bool DataDistributorPlugin::initialize()
|
||||
{
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
DataDistributorPluginPtr pvPlugin = DataDistributorPluginPtr(new DataDistributorPlugin());
|
||||
PVPluginRegistry::registerPlugin(name,pvPlugin);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
PVFilterPtr DataDistributorPlugin::create(
|
||||
const std::string& requestValue,
|
||||
const PVCopyPtr& pvCopy,
|
||||
const PVFieldPtr& master)
|
||||
{
|
||||
return DataDistributorFilter::create(requestValue,pvCopy,master);
|
||||
}
|
||||
|
||||
DataDistributorFilter::~DataDistributorFilter()
|
||||
{
|
||||
dataDistributorPtr->removeClient(clientId, setId);
|
||||
DataDistributor::removeUnusedInstance(dataDistributorPtr);
|
||||
}
|
||||
|
||||
DataDistributorFilterPtr DataDistributorFilter::create(
|
||||
const std::string& requestValue,
|
||||
const PVCopyPtr& pvCopy,
|
||||
const PVFieldPtr& master)
|
||||
{
|
||||
static int clientId = 0;
|
||||
clientId++;
|
||||
|
||||
std::vector<std::string> configItems = split(requestValue, ';');
|
||||
// Use lowercase keys if possible.
|
||||
std::string requestValue2 = toLowerCase(requestValue);
|
||||
std::vector<std::string> configItems2 = split(requestValue2, ';');
|
||||
int nUpdatesPerClient = 1;
|
||||
int updateMode = DataDistributor::DD_UPDATE_ONE_PER_GROUP;
|
||||
std::string groupId = "default";
|
||||
std::string setId = "default";
|
||||
std::string triggerField = "timeStamp";
|
||||
bool hasUpdateMode = false;
|
||||
bool hasSetId = false;
|
||||
for(unsigned int i = 0; i < configItems2.size(); i++) {
|
||||
std::string configItem2 = configItems2[i];
|
||||
size_t ind = configItem2.find(':');
|
||||
if (ind == string::npos) {
|
||||
continue;
|
||||
}
|
||||
if(configItem2.find("updates") == 0) {
|
||||
std::string svalue = configItem2.substr(ind+1);
|
||||
nUpdatesPerClient = atoi(svalue.c_str());
|
||||
}
|
||||
else if(configItem2.find("group") == 0) {
|
||||
std::string configItem = configItems[i];
|
||||
groupId = configItem.substr(ind+1);
|
||||
}
|
||||
else if(configItem2.find("set") == 0) {
|
||||
std::string configItem = configItems[i];
|
||||
setId = configItem.substr(ind+1);
|
||||
hasSetId = true;
|
||||
}
|
||||
else if(configItem2.find("mode") == 0) {
|
||||
std::string svalue = toLowerCase(configItem2.substr(ind+1));
|
||||
if (svalue == "one") {
|
||||
updateMode = DataDistributor::DD_UPDATE_ONE_PER_GROUP;
|
||||
hasUpdateMode = true;
|
||||
}
|
||||
else if (svalue == "all") {
|
||||
updateMode = DataDistributor::DD_UPDATE_ALL_IN_GROUP;
|
||||
hasUpdateMode = true;
|
||||
}
|
||||
}
|
||||
else if(configItem2.find("trigger") == 0) {
|
||||
std::string configItem = configItems[i];
|
||||
triggerField = configItem.substr(ind+1);
|
||||
}
|
||||
}
|
||||
// If request does not have update mode specified, but has set id
|
||||
// then use a different update mode
|
||||
if(!hasUpdateMode && hasSetId) {
|
||||
updateMode = DataDistributor::DD_UPDATE_ALL_IN_GROUP;
|
||||
}
|
||||
|
||||
// Make sure request is valid
|
||||
if(nUpdatesPerClient <= 0) {
|
||||
return DataDistributorFilterPtr();
|
||||
}
|
||||
DataDistributorFilterPtr filter =
|
||||
DataDistributorFilterPtr(new DataDistributorFilter(groupId, clientId, setId, triggerField, nUpdatesPerClient, updateMode, pvCopy, master));
|
||||
return filter;
|
||||
}
|
||||
|
||||
DataDistributorFilter::DataDistributorFilter(const std::string& groupId_, int clientId_, const std::string& setId_, const std::string& triggerField_, int nUpdatesPerClient, int updateMode, const PVCopyPtr& copyPtr_, const epvd::PVFieldPtr& masterFieldPtr_)
|
||||
: dataDistributorPtr(DataDistributor::getInstance(groupId_))
|
||||
, clientId(clientId_)
|
||||
, setId(setId_)
|
||||
, triggerField(triggerField_)
|
||||
, masterFieldPtr(masterFieldPtr_)
|
||||
, triggerFieldPtr()
|
||||
, firstUpdate(true)
|
||||
{
|
||||
triggerField = dataDistributorPtr->addClient(clientId, setId, triggerField, nUpdatesPerClient, updateMode);
|
||||
if(masterFieldPtr->getField()->getType() == epvd::structure) {
|
||||
epvd::PVStructurePtr pvStructurePtr = static_pointer_cast<epvd::PVStructure>(masterFieldPtr);
|
||||
if(pvStructurePtr) {
|
||||
triggerFieldPtr = pvStructurePtr->getSubField(triggerField);
|
||||
}
|
||||
}
|
||||
if(!triggerFieldPtr) {
|
||||
triggerFieldPtr = masterFieldPtr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool DataDistributorFilter::filter(const PVFieldPtr& pvCopy, const BitSetPtr& bitSet, bool toCopy)
|
||||
{
|
||||
if(!toCopy) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool proceedWithUpdate = false;
|
||||
if(firstUpdate) {
|
||||
// Always send first update
|
||||
firstUpdate = false;
|
||||
proceedWithUpdate = true;
|
||||
}
|
||||
else {
|
||||
std::stringstream ss;
|
||||
ss << triggerFieldPtr;
|
||||
std::string triggerFieldValue = ss.str();
|
||||
proceedWithUpdate = dataDistributorPtr->updateClient(clientId, setId, triggerFieldValue);
|
||||
}
|
||||
|
||||
if(proceedWithUpdate) {
|
||||
pvCopy->copyUnchecked(*masterFieldPtr);
|
||||
bitSet->set((unsigned int)pvCopy->getFieldOffset());
|
||||
}
|
||||
else {
|
||||
// Clear all bits
|
||||
//bitSet->clear(pvCopy->getFieldOffset());
|
||||
bitSet->clear();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
string DataDistributorFilter::getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -88,7 +88,6 @@ PVCopyPtr PVCopy::create(
|
||||
bool result = pvCopy->init(pvStructure);
|
||||
if(!result) return PVCopyPtr();
|
||||
pvCopy->traverseMasterInitPlugin();
|
||||
//cout << pvCopy->dump() << endl;
|
||||
return pvCopy;
|
||||
}
|
||||
|
||||
@@ -428,20 +427,28 @@ PVCopy::PVCopy(
|
||||
{
|
||||
}
|
||||
|
||||
void PVCopy::destroy()
|
||||
{
|
||||
headNode.reset();
|
||||
}
|
||||
|
||||
bool PVCopy::init(epics::pvData::PVStructurePtr const &pvRequest)
|
||||
{
|
||||
PVStructurePtr pvMasterStructure = pvMaster;
|
||||
size_t len = pvRequest->getPVFields().size();
|
||||
bool entireMaster = false;
|
||||
if(len==0) entireMaster = true;
|
||||
requestHasMasterField = false;
|
||||
PVStructurePtr pvOptions;
|
||||
if(len==1) {
|
||||
pvOptions = pvRequest->getSubField<PVStructure>("_options");
|
||||
if(len==0) {
|
||||
entireMaster = true;
|
||||
}
|
||||
else {
|
||||
// If "_" is in the request, but not in the master structure,
|
||||
// then assume the top level PV structure is requested
|
||||
PVStructurePtr masterFieldPtr = pvMaster->getSubField<PVStructure>("_");
|
||||
PVStructurePtr requestFieldPtr = pvRequest->getSubField<PVStructure>("_");
|
||||
if (requestFieldPtr) {
|
||||
requestHasMasterField = true;
|
||||
}
|
||||
if (!masterFieldPtr && requestFieldPtr) {
|
||||
entireMaster = true;
|
||||
pvOptions = requestFieldPtr->getSubField<PVStructure>("_options");
|
||||
}
|
||||
}
|
||||
if(entireMaster) {
|
||||
structure = pvMasterStructure->getStructure();
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "pv/pvArrayPlugin.h"
|
||||
#include "pv/pvTimestampPlugin.h"
|
||||
#include "pv/pvDeadbandPlugin.h"
|
||||
#include "pv/dataDistributorPlugin.h"
|
||||
|
||||
using std::tr1::static_pointer_cast;
|
||||
using namespace epics::pvData;
|
||||
@@ -44,6 +45,7 @@ PVDatabasePtr PVDatabase::getMaster()
|
||||
PVArrayPlugin::create();
|
||||
PVTimestampPlugin::create();
|
||||
PVDeadbandPlugin::create();
|
||||
DataDistributorPlugin::create();
|
||||
}
|
||||
return pvDatabaseMaster;
|
||||
}
|
||||
|
||||
@@ -37,9 +37,11 @@ namespace epics { namespace pvDatabase {
|
||||
|
||||
PVRecordPtr PVRecord::create(
|
||||
string const &recordName,
|
||||
PVStructurePtr const & pvStructure)
|
||||
PVStructurePtr const & pvStructure,
|
||||
int asLevel,
|
||||
const std::string& asGroup)
|
||||
{
|
||||
PVRecordPtr pvRecord(new PVRecord(recordName,pvStructure));
|
||||
PVRecordPtr pvRecord(new PVRecord(recordName,pvStructure,asLevel,asGroup));
|
||||
if(!pvRecord->init()) {
|
||||
pvRecord.reset();
|
||||
}
|
||||
@@ -49,12 +51,16 @@ PVRecordPtr PVRecord::create(
|
||||
|
||||
PVRecord::PVRecord(
|
||||
string const & recordName,
|
||||
PVStructurePtr const & pvStructure)
|
||||
PVStructurePtr const & pvStructure,
|
||||
int asLevel_,
|
||||
const std::string& asGroup_)
|
||||
: recordName(recordName),
|
||||
pvStructure(pvStructure),
|
||||
depthGroupPut(0),
|
||||
traceLevel(0),
|
||||
isAddListener(false)
|
||||
isAddListener(false),
|
||||
asLevel(asLevel_),
|
||||
asGroup(asGroup_)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -326,6 +332,7 @@ PVRecordField::PVRecordField(
|
||||
PVRecordPtr const & pvRecord)
|
||||
: pvField(pvField),
|
||||
isStructure(pvField->getField()->getType()==structure ? true : false),
|
||||
master(),
|
||||
parent(parent),
|
||||
pvRecord(pvRecord)
|
||||
{
|
||||
@@ -412,11 +419,18 @@ void PVRecordField::postParent(PVRecordFieldPtr const & subField)
|
||||
listener->dataPut(pvrs,subField);
|
||||
}
|
||||
PVRecordStructurePtr parent(this->parent.lock());
|
||||
if(parent) parent->postParent(subField);
|
||||
if(parent) {
|
||||
parent->postParent(subField);
|
||||
}
|
||||
}
|
||||
|
||||
void PVRecordField::postSubField()
|
||||
{
|
||||
// Master field pointer will be set in only one subfield
|
||||
PVRecordStructurePtr master(this->master.lock());
|
||||
if(master) {
|
||||
master->callListener();
|
||||
}
|
||||
callListener();
|
||||
if(isStructure) {
|
||||
PVRecordStructurePtr pvrs =
|
||||
@@ -459,19 +473,38 @@ void PVRecordStructure::init()
|
||||
PVRecordStructurePtr self =
|
||||
static_pointer_cast<PVRecordStructure>(shared_from_this());
|
||||
PVRecordPtr pvRecord = getPVRecord();
|
||||
static bool masterFieldCallbackSet = false;
|
||||
bool isMasterField = (!getFullFieldName().size());
|
||||
if (isMasterField) {
|
||||
masterFieldCallbackSet = false;
|
||||
}
|
||||
for(size_t i=0; i<numFields; i++) {
|
||||
PVFieldPtr pvField = pvFields[i];
|
||||
if(pvField->getField()->getType()==structure) {
|
||||
PVStructurePtr xxx = static_pointer_cast<PVStructure>(pvField);
|
||||
PVRecordStructurePtr pvRecordStructure(
|
||||
PVStructurePtr xxx = static_pointer_cast<PVStructure>(pvField);
|
||||
PVRecordStructurePtr pvRecordStructure(
|
||||
new PVRecordStructure(xxx,self,pvRecord));
|
||||
pvRecordFields->push_back(pvRecordStructure);
|
||||
pvRecordStructure->init();
|
||||
pvRecordFields->push_back(pvRecordStructure);
|
||||
pvRecordStructure->init();
|
||||
} else {
|
||||
PVRecordFieldPtr pvRecordField(
|
||||
PVRecordFieldPtr pvRecordField(
|
||||
new PVRecordField(pvField,self,pvRecord));
|
||||
pvRecordFields->push_back(pvRecordField);
|
||||
pvRecordField->init();
|
||||
pvRecordFields->push_back(pvRecordField);
|
||||
pvRecordField->init();
|
||||
// Master field listeners will be called before
|
||||
// calling listeners for the first subfield
|
||||
if (!masterFieldCallbackSet) {
|
||||
masterFieldCallbackSet = true;
|
||||
// Find master field
|
||||
PVRecordStructurePtr p = pvRecordField->parent.lock();
|
||||
while (p) {
|
||||
PVRecordStructurePtr p2 = p->parent.lock();
|
||||
if (!p2) {
|
||||
pvRecordField->master = p;
|
||||
}
|
||||
p = p2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/* addRecord.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2013.04.18
|
||||
*/
|
||||
#ifndef ADDRECORD_H
|
||||
#define ADDRECORD_H
|
||||
|
||||
#include <pv/channelProviderLocal.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
|
||||
class AddRecord;
|
||||
typedef std::tr1::shared_ptr<AddRecord> AddRecordPtr;
|
||||
|
||||
/**
|
||||
* @brief Add another record in the same database.
|
||||
*
|
||||
* A record to add another record
|
||||
* It is meant to be used via a channelPutGet request.
|
||||
* The argument has one field: recordName.
|
||||
* The result has a field named status.
|
||||
*/
|
||||
class epicsShareClass AddRecord :
|
||||
public PVRecord
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(AddRecord);
|
||||
/**
|
||||
* Factory methods to create AddRecord.
|
||||
* @param recordName The name for the AddRecord.
|
||||
* @return A shared pointer to AddRecord..
|
||||
*/
|
||||
static AddRecordPtr create(
|
||||
std::string const & recordName);
|
||||
/**
|
||||
* standard init method required by PVRecord
|
||||
* @return true unless record name already exists.
|
||||
*/
|
||||
virtual bool init();
|
||||
/**
|
||||
* @brief Add the record specified by recordName.
|
||||
*/
|
||||
virtual void process();
|
||||
private:
|
||||
AddRecord(
|
||||
std::string const & recordName,
|
||||
epics::pvData::PVStructurePtr const & pvStructure);
|
||||
epics::pvData::PVStringPtr pvRecordName;
|
||||
epics::pvData::PVStringPtr pvResult;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* ADDRECORD_H */
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <pv/pvDatabase.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
#include <asLib.h>
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
@@ -58,6 +59,19 @@ class epicsShareClass ChannelProviderLocal :
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelProviderLocal);
|
||||
/**
|
||||
* @brief Initialize access security configuration
|
||||
* @param filePath AS definition file path
|
||||
* @param substitutions macro substitutions
|
||||
* @throws std::runtime_error in case of configuration problem
|
||||
*/
|
||||
static void initAs(const std::string& filePath, const std::string& substitutions="");
|
||||
/**
|
||||
* @brief Is access security active?
|
||||
* @return true is AS is active
|
||||
*/
|
||||
static bool isAsActive();
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
@@ -66,11 +80,6 @@ public:
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~ChannelProviderLocal();
|
||||
/**
|
||||
* @brief DEPRECATED
|
||||
*
|
||||
*/
|
||||
virtual void destroy(){};
|
||||
/**
|
||||
* @brief Returns the channel provider name.
|
||||
*
|
||||
@@ -163,6 +172,7 @@ private:
|
||||
friend class ChannelProviderLocalRun;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Channel for accessing a PVRecord.
|
||||
*
|
||||
@@ -190,11 +200,6 @@ public:
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~ChannelLocal();
|
||||
/**
|
||||
* @brief DEPRECATED
|
||||
*
|
||||
*/
|
||||
virtual void destroy() {};
|
||||
/**
|
||||
* @brief Detach from the record.
|
||||
*
|
||||
@@ -351,6 +356,18 @@ public:
|
||||
* @param out the stream on which the message is displayed.
|
||||
*/
|
||||
virtual void printInfo(std::ostream& out);
|
||||
/**
|
||||
* @brief determines if client can write
|
||||
*
|
||||
* @return true if client can write
|
||||
*/
|
||||
virtual bool canWrite();
|
||||
/**
|
||||
* @brief determines if client can read
|
||||
*
|
||||
* @return true if client can read
|
||||
*/
|
||||
virtual bool canRead();
|
||||
protected:
|
||||
shared_pointer getPtrSelf()
|
||||
{
|
||||
@@ -361,6 +378,19 @@ private:
|
||||
ChannelProviderLocalWPtr provider;
|
||||
PVRecordWPtr pvRecord;
|
||||
epics::pvData::Mutex mutex;
|
||||
|
||||
// AS-specific variables/methods
|
||||
std::vector<char> toCharArray(const std::string& s);
|
||||
std::vector<char> getAsGroup(const PVRecordPtr& pvRecord);
|
||||
std::vector<char> getAsUser(const epics::pvAccess::ChannelRequester::shared_pointer& requester);
|
||||
std::vector<char> getAsHost(const epics::pvAccess::ChannelRequester::shared_pointer& requester);
|
||||
|
||||
int asLevel;
|
||||
std::vector<char> asGroup;
|
||||
std::vector<char> asUser;
|
||||
std::vector<char> asHost;
|
||||
ASMEMBERPVT asMemberPvt;
|
||||
ASCLIENTPVT asClientPvt;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
167
src/pv/dataDistributorPlugin.h
Normal file
167
src/pv/dataDistributorPlugin.h
Normal file
@@ -0,0 +1,167 @@
|
||||
// Copyright information and license terms for this software can be
|
||||
// found in the file LICENSE that is included with the distribution
|
||||
|
||||
#ifndef DATA_DISTRIBUTOR_PLUGIN_H
|
||||
#define DATA_DISTRIBUTOR_PLUGIN_H
|
||||
|
||||
// The data distributor plugin enables distribution of channel data between
|
||||
// multiple client applications.
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/pvPlugin.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvCopy {
|
||||
|
||||
class DataDistributorPlugin;
|
||||
class DataDistributorFilter;
|
||||
class DataDistributor;
|
||||
|
||||
typedef std::tr1::shared_ptr<DataDistributorPlugin> DataDistributorPluginPtr;
|
||||
typedef std::tr1::shared_ptr<DataDistributorFilter> DataDistributorFilterPtr;
|
||||
typedef std::tr1::shared_ptr<DataDistributor> DataDistributorPtr;
|
||||
|
||||
struct ClientSet;
|
||||
typedef std::tr1::shared_ptr<ClientSet> ClientSetPtr;
|
||||
typedef std::tr1::shared_ptr<const ClientSet> ClientSetConstPtr;
|
||||
|
||||
struct ClientSet
|
||||
{
|
||||
POINTER_DEFINITIONS(ClientSet);
|
||||
|
||||
ClientSet(const std::string& setId_, const std::string triggerField_, int nUpdatesPerClient_, int updateMode_)
|
||||
: setId(setId_)
|
||||
, triggerField(triggerField_)
|
||||
, nUpdatesPerClient(nUpdatesPerClient_)
|
||||
, updateMode(updateMode_)
|
||||
, clientIdList()
|
||||
, lastUpdateValue()
|
||||
, updateCounter(0)
|
||||
, currentClientIdIter(clientIdList.end())
|
||||
{}
|
||||
~ClientSet() {}
|
||||
std::string setId;
|
||||
std::string triggerField;
|
||||
int nUpdatesPerClient;
|
||||
int updateMode;
|
||||
std::list<int> clientIdList;
|
||||
std::string lastUpdateValue;
|
||||
int updateCounter;
|
||||
std::list<int>::iterator currentClientIdIter;
|
||||
};
|
||||
|
||||
class DataDistributor
|
||||
{
|
||||
public:
|
||||
enum ClientUpdateMode {
|
||||
DD_UPDATE_ONE_PER_GROUP = 0, // Update goes to one client per set
|
||||
DD_UPDATE_ALL_IN_GROUP = 1, // Update goes to all clients in set
|
||||
DD_N_UPDATE_MODES = 2 // Number of valid update modes
|
||||
};
|
||||
|
||||
static DataDistributorPtr getInstance(const std::string& groupId);
|
||||
static void removeUnusedInstance(DataDistributorPtr dataDistributorPtr);
|
||||
|
||||
virtual ~DataDistributor();
|
||||
std::string getGroupId() const { return groupId; }
|
||||
std::string addClient(int clientId, const std::string& setId, const std::string& triggerField, int nUpdatesPerClient, int updateMode);
|
||||
void removeClient(int clientId, const std::string& setId);
|
||||
bool updateClient(int clientId, const std::string& setId, const std::string& triggerFieldValue);
|
||||
|
||||
private:
|
||||
DataDistributor(const std::string& id);
|
||||
DataDistributor(const DataDistributor& distributor);
|
||||
DataDistributor& operator=(const DataDistributor& distributor);
|
||||
|
||||
static std::map<std::string, DataDistributorPtr> dataDistributorMap;
|
||||
static epics::pvData::Mutex dataDistributorMapMutex;
|
||||
|
||||
std::string groupId;
|
||||
epics::pvData::Mutex mutex;
|
||||
std::map<std::string, ClientSetPtr> clientSetMap;
|
||||
std::list<std::string> clientSetIdList;
|
||||
std::list<std::string>::iterator currentSetIdIter;
|
||||
std::string lastUpdateValue;
|
||||
};
|
||||
|
||||
class epicsShareClass DataDistributorPlugin : public PVPlugin
|
||||
{
|
||||
private:
|
||||
DataDistributorPlugin();
|
||||
public:
|
||||
POINTER_DEFINITIONS(DataDistributorPlugin);
|
||||
virtual ~DataDistributorPlugin();
|
||||
/**
|
||||
* Factory
|
||||
*/
|
||||
static void create();
|
||||
/**
|
||||
* Create a PVFilter.
|
||||
* @param requestValue The value part of a name=value request option.
|
||||
* @param pvCopy The PVCopy to which the PVFilter will be attached.
|
||||
* @param master The field in the master PVStructure to which the PVFilter will be attached
|
||||
* @return The PVFilter.
|
||||
* Null is returned if master or requestValue is not appropriate for the plugin.
|
||||
*/
|
||||
virtual PVFilterPtr create(
|
||||
const std::string& requestValue,
|
||||
const PVCopyPtr& pvCopy,
|
||||
const epics::pvData::PVFieldPtr& master);
|
||||
private:
|
||||
static bool initialize();
|
||||
static bool initialized;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A Plugin for a filter that gets a sub array from a PVScalarDeadband.
|
||||
*/
|
||||
class epicsShareClass DataDistributorFilter : public PVFilter
|
||||
{
|
||||
private:
|
||||
DataDistributorPtr dataDistributorPtr;
|
||||
int clientId;
|
||||
std::string setId;
|
||||
std::string triggerField;
|
||||
epics::pvData::PVFieldPtr masterFieldPtr;
|
||||
epics::pvData::PVFieldPtr triggerFieldPtr;
|
||||
bool firstUpdate;
|
||||
|
||||
DataDistributorFilter(const std::string& groupId, int clientId, const std::string& setId, const std::string& triggerField, int nUpdatesPerClient, int updateMode, const epics::pvCopy::PVCopyPtr& copyPtr, const epics::pvData::PVFieldPtr& masterFieldPtr);
|
||||
|
||||
public:
|
||||
POINTER_DEFINITIONS(DataDistributorFilter);
|
||||
virtual ~DataDistributorFilter();
|
||||
/**
|
||||
* Create a DataDistributorFilter.
|
||||
* @param requestValue The value part of a name=value request option.
|
||||
* @param master The field in the master PVStructure to which the PVFilter will be attached.
|
||||
* @return The PVFilter.
|
||||
* A null is returned if master or requestValue is not appropriate for the plugin.
|
||||
*/
|
||||
static DataDistributorFilterPtr create(
|
||||
const std::string& requestValue,
|
||||
const PVCopyPtr& pvCopy,
|
||||
const epics::pvData::PVFieldPtr & master);
|
||||
/**
|
||||
* Perform a filter operation
|
||||
* @param pvCopy The field in the copy PVStructure.
|
||||
* @param bitSet A bitSet for copyPVStructure.
|
||||
* @param toCopy (true,false) means copy (from master to copy,from copy to master)
|
||||
* @return if filter (modified, did not modify) destination.
|
||||
* Null is returned if master or requestValue is not appropriate for the plugin.
|
||||
*/
|
||||
bool filter(const epics::pvData::PVFieldPtr & pvCopy,const epics::pvData::BitSetPtr & bitSet,bool toCopy);
|
||||
/**
|
||||
* Get the filter name.
|
||||
* @return The name.
|
||||
*/
|
||||
std::string getName();
|
||||
};
|
||||
|
||||
}}
|
||||
#endif
|
||||
@@ -1,89 +0,0 @@
|
||||
/* processRecord.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2019.06.07
|
||||
*/
|
||||
#ifndef PROCESSRECORD_H
|
||||
#define PROCESSRECORD_H
|
||||
|
||||
#include <map>
|
||||
#include <epicsThread.h>
|
||||
#include <pv/event.h>
|
||||
#include <pv/channelProviderLocal.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
typedef std::tr1::shared_ptr<epicsThread> EpicsThreadPtr;
|
||||
|
||||
class ProcessRecord;
|
||||
typedef std::tr1::shared_ptr<ProcessRecord> ProcessRecordPtr;
|
||||
|
||||
/**
|
||||
* @brief Process another record in the same database.
|
||||
*
|
||||
* A record to process another record
|
||||
* It is meant to be used via a channelPutGet request.
|
||||
* The argument has one field: recordName.
|
||||
* The result has a field named status.
|
||||
*/
|
||||
class epicsShareClass ProcessRecord :
|
||||
public PVRecord,
|
||||
public epicsThreadRunable
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ProcessRecord);
|
||||
/**
|
||||
* Factory methods to create ProcessRecord.
|
||||
* @param recordName The name for the ProcessRecord.
|
||||
* @param delay Delay time to wait between process requests.
|
||||
* @return A shared pointer to ProcessRecord.
|
||||
*/
|
||||
static ProcessRecordPtr create(
|
||||
std::string const & recordName,double delay);
|
||||
/**
|
||||
* standard init method required by PVRecord
|
||||
* @return true unless record name already exists.
|
||||
*/
|
||||
virtual bool init();
|
||||
/**
|
||||
* @brief Process the record specified by recordName.
|
||||
*/
|
||||
virtual void process();
|
||||
/**
|
||||
* @brief The run method for the thread.
|
||||
*/
|
||||
virtual void run();
|
||||
/**
|
||||
* @brief Start the thread
|
||||
*/
|
||||
void startThread();
|
||||
/**
|
||||
* @brief Stop the thread
|
||||
*/
|
||||
void stop();
|
||||
private:
|
||||
ProcessRecord(
|
||||
std::string const & recordName,
|
||||
epics::pvData::PVStructurePtr const & pvStructure,double delay);
|
||||
double delay;
|
||||
EpicsThreadPtr thread;
|
||||
epics::pvData::Event runStop;
|
||||
epics::pvData::Event runReturn;
|
||||
PVDatabasePtr pvDatabase;
|
||||
PVRecordMap pvRecordMap;
|
||||
epics::pvData::PVStringPtr pvCommand;
|
||||
epics::pvData::PVStringPtr pvRecordName;
|
||||
epics::pvData::PVStringPtr pvResult;
|
||||
epics::pvData::Mutex mutex;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* PROCESSRECORD_H */
|
||||
@@ -59,6 +59,7 @@ class epicsShareClass PVRecord :
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(PVRecord);
|
||||
|
||||
/**
|
||||
* The Destructor.
|
||||
*/
|
||||
@@ -86,10 +87,6 @@ public:
|
||||
* the base class sets the timeStamp to the current time.
|
||||
*/
|
||||
virtual void process();
|
||||
/**
|
||||
* @brief DEPRECATED
|
||||
*/
|
||||
virtual void destroy() {}
|
||||
/**
|
||||
* @brief remove record from database.
|
||||
*
|
||||
@@ -116,11 +113,14 @@ public:
|
||||
*
|
||||
* @param recordName The name of the record, which is also the channelName.
|
||||
* @param pvStructure The top level structure.
|
||||
* @param asLevel AS level (default: ASL0)
|
||||
* @param asGroup AS group (default: DEFAULT)
|
||||
* @return A shared pointer to the newly created record.
|
||||
*/
|
||||
static PVRecordPtr create(
|
||||
std::string const & recordName,
|
||||
epics::pvData::PVStructurePtr const & pvStructure);
|
||||
epics::pvData::PVStructurePtr const & pvStructure,
|
||||
int asLevel = 0, const std::string& asGroup = "DEFAULT");
|
||||
/**
|
||||
* @brief Get the name of the record.
|
||||
*
|
||||
@@ -236,15 +236,40 @@ public:
|
||||
* @param level The level
|
||||
*/
|
||||
void setTraceLevel(int level) {traceLevel = level;}
|
||||
/**
|
||||
* @brief Get the ASlevel
|
||||
*
|
||||
* @return The level.
|
||||
*/
|
||||
int getAsLevel() const {return asLevel;}
|
||||
/**
|
||||
* @brief Get the AS group name
|
||||
*
|
||||
* @return The name.
|
||||
*/
|
||||
std::string getAsGroup() const {return asGroup;}
|
||||
/**
|
||||
* @brief set access security level.
|
||||
* @param level The level
|
||||
*/
|
||||
void setAsLevel(int level) {asLevel=level;}
|
||||
/**
|
||||
* @brief set access security group
|
||||
* @param group The group name
|
||||
*/
|
||||
void setAsGroup(const std::string& group) {asGroup = group;}
|
||||
protected:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param recordName The name of the record
|
||||
* @param pvStructure The top level PVStructutre
|
||||
* @param asLevel AS level (default: ASL0)
|
||||
* @param asGroup AS group (default: DEFAULT)
|
||||
*/
|
||||
PVRecord(
|
||||
std::string const & recordName,
|
||||
epics::pvData::PVStructurePtr const & pvStructure);
|
||||
epics::pvData::PVStructurePtr const & pvStructure,
|
||||
int asLevel = 0, const std::string& asGroup = "DEFAULT");
|
||||
/**
|
||||
* @brief Initializes the base class.
|
||||
*
|
||||
@@ -273,6 +298,9 @@ private:
|
||||
|
||||
epics::pvData::PVTimeStamp pvTimeStamp;
|
||||
epics::pvData::TimeStamp timeStamp;
|
||||
|
||||
int asLevel;
|
||||
std::string asGroup;
|
||||
};
|
||||
|
||||
epicsShareFunc std::ostream& operator<<(std::ostream& o, const PVRecord& record);
|
||||
@@ -348,6 +376,7 @@ private:
|
||||
std::list<PVListenerWPtr> pvListenerList;
|
||||
epics::pvData::PVField::weak_pointer pvField;
|
||||
bool isStructure;
|
||||
PVRecordStructureWPtr master;
|
||||
PVRecordStructureWPtr parent;
|
||||
PVRecordWPtr pvRecord;
|
||||
std::string fullName;
|
||||
|
||||
@@ -79,7 +79,6 @@ public:
|
||||
epics::pvData::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.
|
||||
@@ -168,6 +167,10 @@ public:
|
||||
* name is the subField name and value is the subField value.
|
||||
*/
|
||||
epics::pvData::PVStructurePtr getOptions(std::size_t fieldOffset);
|
||||
/**
|
||||
* Is master field requested?
|
||||
*/
|
||||
bool isMasterFieldRequested() const {return requestHasMasterField;}
|
||||
/**
|
||||
* For debugging.
|
||||
*/
|
||||
@@ -184,6 +187,7 @@ private:
|
||||
CopyNodePtr headNode;
|
||||
epics::pvData::PVStructurePtr cacheInitStructure;
|
||||
epics::pvData::BitSetPtr ignorechangeBitSet;
|
||||
bool requestHasMasterField;
|
||||
|
||||
void traverseMaster(
|
||||
CopyNodePtr const &node,
|
||||
|
||||
67
src/pv/pvdbcrAddRecord.h
Normal file
67
src/pv/pvdbcrAddRecord.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2021.04.11
|
||||
*/
|
||||
#ifndef PVDBCRADDARRAY_H
|
||||
#define PVDBCRADDARRAY_H
|
||||
|
||||
#include <pv/pvDatabase.h>
|
||||
#include <pv/pvSupport.h>
|
||||
#include <pv/pvStructureCopy.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
class PvdbcrAddRecord;
|
||||
typedef std::tr1::shared_ptr<PvdbcrAddRecord> PvdbcrAddRecordPtr;
|
||||
|
||||
/**
|
||||
* @brief PvdbcrAddRecord A record that adds a record to the master database.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass PvdbcrAddRecord :
|
||||
public PVRecord
|
||||
{
|
||||
private:
|
||||
PvdbcrAddRecord(
|
||||
std::string const & recordName,epics::pvData::PVStructurePtr const & pvStructure,
|
||||
int asLevel,std::string const & asGroup);
|
||||
epics::pvData::PVStringPtr pvRecordName;
|
||||
epics::pvData::PVStringPtr pvResult;
|
||||
public:
|
||||
POINTER_DEFINITIONS(PvdbcrAddRecord);
|
||||
/**
|
||||
* The Destructor.
|
||||
*/
|
||||
virtual ~PvdbcrAddRecord() {}
|
||||
/**
|
||||
* @brief Create a record.
|
||||
*
|
||||
* @param recordName The record name.
|
||||
* @param asLevel The access security level.
|
||||
* @param asGroup The access security group.
|
||||
* @return The PVRecord
|
||||
*/
|
||||
static PvdbcrAddRecordPtr create(
|
||||
std::string const & recordName,
|
||||
int asLevel=0,std::string const & asGroup = std::string("DEFAULT"));
|
||||
/**
|
||||
* @brief a PVRecord method
|
||||
* @return success or failure
|
||||
*/
|
||||
virtual bool init();
|
||||
/**
|
||||
* @brief process method that adds a record to the master database.
|
||||
*/
|
||||
virtual void process();
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* PVDBCRADDARRAY_H */
|
||||
105
src/pv/pvdbcrProcessRecord.h
Normal file
105
src/pv/pvdbcrProcessRecord.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2021.04.11
|
||||
*/
|
||||
#ifndef PVDBCRPROCESSARRAY_H
|
||||
#define PVDBCRPROCESSARRAY_H
|
||||
#include <epicsThread.h>
|
||||
#include <epicsGuard.h>
|
||||
#include <pv/event.h>
|
||||
#include <pv/pvDatabase.h>
|
||||
#include <pv/pvSupport.h>
|
||||
#include <pv/pvStructureCopy.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
typedef std::tr1::shared_ptr<epicsThread> EpicsThreadPtr;
|
||||
class PvdbcrProcessRecord;
|
||||
typedef std::tr1::shared_ptr<PvdbcrProcessRecord> PvdbcrProcessRecordPtr;
|
||||
|
||||
/**
|
||||
* @brief PvdbcrProcessRecord A record that processes other records in the master database.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass PvdbcrProcessRecord :
|
||||
public PVRecord,
|
||||
public epicsThreadRunable
|
||||
{
|
||||
private:
|
||||
PvdbcrProcessRecord(
|
||||
std::string const & recordName,epics::pvData::PVStructurePtr const & pvStructure,
|
||||
double delay,
|
||||
int asLevel,std::string const & asGroup);
|
||||
double delay;
|
||||
EpicsThreadPtr thread;
|
||||
epics::pvData::Event runStop;
|
||||
epics::pvData::Event runReturn;
|
||||
PVDatabasePtr pvDatabase;
|
||||
PVRecordMap pvRecordMap;
|
||||
epics::pvData::PVStringPtr pvCommand;
|
||||
epics::pvData::PVStringPtr pvRecordName;
|
||||
epics::pvData::PVStringPtr pvResult;
|
||||
epics::pvData::Mutex mutex;
|
||||
public:
|
||||
POINTER_DEFINITIONS(PvdbcrProcessRecord);
|
||||
/**
|
||||
* The Destructor.
|
||||
*/
|
||||
virtual ~PvdbcrProcessRecord() {}
|
||||
/**
|
||||
* @brief Create a record.
|
||||
*
|
||||
* @param recordName The record name.
|
||||
* @param asLevel The access security level.
|
||||
* @param asGroup The access security group.
|
||||
* @return The PVRecord
|
||||
*/
|
||||
static PvdbcrProcessRecordPtr create(
|
||||
std::string const & recordName,
|
||||
double delay= 1.0,
|
||||
int asLevel=0,std::string const & asGroup = std::string("DEFAULT"));
|
||||
/**
|
||||
* @brief set the delay between prcocessing.
|
||||
*
|
||||
* @param delay in seconds
|
||||
*/
|
||||
void setDelay(double delay);
|
||||
/**
|
||||
* @brief get the delay between prcocessing.
|
||||
*
|
||||
* @return delay in seconds
|
||||
*/
|
||||
double getDelay();
|
||||
/**
|
||||
* @brief a PVRecord method
|
||||
* @return success or failure
|
||||
*/
|
||||
virtual bool init();
|
||||
/**
|
||||
* @brief method that processes other records in the master database.
|
||||
*/
|
||||
virtual void process();
|
||||
/**
|
||||
* @brief thread method
|
||||
*/
|
||||
virtual void run();
|
||||
/**
|
||||
* @brief thread method
|
||||
*/
|
||||
void startThread();
|
||||
/**
|
||||
* @brief thread method
|
||||
*/
|
||||
void stop();
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* PVDBCRPROCESSARRAY_H */
|
||||
67
src/pv/pvdbcrRemoveRecord.h
Normal file
67
src/pv/pvdbcrRemoveRecord.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2021.04.11
|
||||
*/
|
||||
#ifndef PVDBCRREMOVEARRAY_H
|
||||
#define PVDBCRREMOVEARRAY_H
|
||||
|
||||
#include <pv/pvDatabase.h>
|
||||
#include <pv/pvSupport.h>
|
||||
#include <pv/pvStructureCopy.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
class PvdbcrRemoveRecord;
|
||||
typedef std::tr1::shared_ptr<PvdbcrRemoveRecord> PvdbcrRemoveRecordPtr;
|
||||
|
||||
/**
|
||||
* @brief PvdbcrRemoveRecord A record that removes a record from the master database.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass PvdbcrRemoveRecord :
|
||||
public PVRecord
|
||||
{
|
||||
private:
|
||||
PvdbcrRemoveRecord(
|
||||
std::string const & recordName,epics::pvData::PVStructurePtr const & pvStructure,
|
||||
int asLevel,std::string const & asGroup);
|
||||
epics::pvData::PVStringPtr pvRecordName;
|
||||
epics::pvData::PVStringPtr pvResult;
|
||||
public:
|
||||
POINTER_DEFINITIONS(PvdbcrRemoveRecord);
|
||||
/**
|
||||
* The Destructor.
|
||||
*/
|
||||
virtual ~PvdbcrRemoveRecord() {}
|
||||
/**
|
||||
* @brief Create a record.
|
||||
*
|
||||
* @param recordName The record name.
|
||||
* @param asLevel The access security level.
|
||||
* @param asGroup The access security group.
|
||||
* @return The PVRecord
|
||||
*/
|
||||
static PvdbcrRemoveRecordPtr create(
|
||||
std::string const & recordName,
|
||||
int asLevel=0,std::string const & asGroup = std::string("DEFAULT"));
|
||||
/**
|
||||
* @brief a PVRecord method
|
||||
* @return success or failure
|
||||
*/
|
||||
virtual bool init();
|
||||
/**
|
||||
* @brief process method that removes a record from the master database.
|
||||
*/
|
||||
virtual void process();
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* PVDBCRREMOVEARRAY_H */
|
||||
57
src/pv/pvdbcrScalarArrayRecord.h
Normal file
57
src/pv/pvdbcrScalarArrayRecord.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2021.04.11
|
||||
*/
|
||||
#ifndef PVDBCRSCALARARRAYRECORD_H
|
||||
#define PVDBCRSCALARARRAYRECORD_H
|
||||
|
||||
#include <pv/pvDatabase.h>
|
||||
#include <pv/pvSupport.h>
|
||||
#include <pv/pvStructureCopy.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
class PvdbcrScalarArrayRecord;
|
||||
typedef std::tr1::shared_ptr<PvdbcrScalarArrayRecord> PvdbcrScalarArrayRecordPtr;
|
||||
|
||||
/**
|
||||
* @brief PvdbcrScalarArrayRecord creates a record with a scalar array value, alarm, and timeStamp.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass PvdbcrScalarArrayRecord :
|
||||
public PVRecord
|
||||
{
|
||||
private:
|
||||
PvdbcrScalarArrayRecord(
|
||||
std::string const & recordName,epics::pvData::PVStructurePtr const & pvStructure,
|
||||
int asLevel,std::string const & asGroup);
|
||||
public:
|
||||
POINTER_DEFINITIONS(PvdbcrScalarArrayRecord);
|
||||
/**
|
||||
* The Destructor.
|
||||
*/
|
||||
virtual ~PvdbcrScalarArrayRecord() {}
|
||||
/**
|
||||
* @brief Create a record.
|
||||
*
|
||||
* @param recordName The record name.
|
||||
* @param scalarType The type for the value field
|
||||
* @param asLevel The access security level.
|
||||
* @param asGroup The access security group.
|
||||
* @return The PVRecord
|
||||
*/
|
||||
static PvdbcrScalarArrayRecordPtr create(
|
||||
std::string const & recordName,std::string const & scalarType,
|
||||
int asLevel=0,std::string const & asGroup = std::string("DEFAULT"));
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* PVDBCRSCALARARRAYRECORD_H */
|
||||
57
src/pv/pvdbcrScalarRecord.h
Normal file
57
src/pv/pvdbcrScalarRecord.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2021.04.11
|
||||
*/
|
||||
#ifndef PVDBCRSCALARRECORD_H
|
||||
#define PVDBCRSCALARRECORD_H
|
||||
|
||||
#include <pv/pvDatabase.h>
|
||||
#include <pv/pvSupport.h>
|
||||
#include <pv/pvStructureCopy.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
class PvdbcrScalarRecord;
|
||||
typedef std::tr1::shared_ptr<PvdbcrScalarRecord> PvdbcrScalarRecordPtr;
|
||||
|
||||
/**
|
||||
* @brief PvdbcrScalarRecord creates a record with a scalar value, alarm, and timeStamp.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass PvdbcrScalarRecord :
|
||||
public PVRecord
|
||||
{
|
||||
private:
|
||||
PvdbcrScalarRecord(
|
||||
std::string const & recordName,epics::pvData::PVStructurePtr const & pvStructure,
|
||||
int asLevel,std::string const & asGroup);
|
||||
public:
|
||||
POINTER_DEFINITIONS(PvdbcrScalarRecord);
|
||||
/**
|
||||
* The Destructor.
|
||||
*/
|
||||
virtual ~PvdbcrScalarRecord() {}
|
||||
/**
|
||||
* @brief Create a record.
|
||||
*
|
||||
* @param recordName The record name.
|
||||
* @param scalarType The type for the value field
|
||||
* @param asLevel The access security level.
|
||||
* @param asGroup The access security group.
|
||||
* @return The PVRecord
|
||||
*/
|
||||
static PvdbcrScalarRecordPtr create(
|
||||
std::string const & recordName,std::string const & scalarType,
|
||||
int asLevel=0,std::string const & asGroup = std::string("DEFAULT"));
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* PVDBCRSCALARRECORD_H */
|
||||
68
src/pv/pvdbcrTraceRecord.h
Normal file
68
src/pv/pvdbcrTraceRecord.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2021.04.11
|
||||
*/
|
||||
#ifndef PVDBCRTRACEARRAY_H
|
||||
#define PVDBCRTRACEARRAY_H
|
||||
|
||||
#include <pv/pvDatabase.h>
|
||||
#include <pv/pvSupport.h>
|
||||
#include <pv/pvStructureCopy.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
class PvdbcrTraceRecord;
|
||||
typedef std::tr1::shared_ptr<PvdbcrTraceRecord> PvdbcrTraceRecordPtr;
|
||||
|
||||
/**
|
||||
* @brief PvdbcrTraceRecord A record sets trace level for a record in the master database.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass PvdbcrTraceRecord :
|
||||
public PVRecord
|
||||
{
|
||||
private:
|
||||
PvdbcrTraceRecord(
|
||||
std::string const & recordName,epics::pvData::PVStructurePtr const & pvStructure,
|
||||
int asLevel,std::string const & asGroup);
|
||||
epics::pvData::PVStringPtr pvRecordName;
|
||||
epics::pvData::PVIntPtr pvLevel;
|
||||
epics::pvData::PVStringPtr pvResult;
|
||||
public:
|
||||
POINTER_DEFINITIONS(PvdbcrTraceRecord);
|
||||
/**
|
||||
* The Destructor.
|
||||
*/
|
||||
virtual ~PvdbcrTraceRecord() {}
|
||||
/**
|
||||
* @brief Create a record.
|
||||
*
|
||||
* @param recordName The record name.
|
||||
* @param asLevel The access security level.
|
||||
* @param asGroup The access security group.
|
||||
* @return The PVRecord
|
||||
*/
|
||||
static PvdbcrTraceRecordPtr create(
|
||||
std::string const & recordName,
|
||||
int asLevel=0,std::string const & asGroup = std::string("DEFAULT"));
|
||||
/**
|
||||
* @brief a PVRecord method
|
||||
* @return success or failure
|
||||
*/
|
||||
virtual bool init();
|
||||
/**
|
||||
* @brief process method that sets trace level for a record in the master database.
|
||||
*/
|
||||
virtual void process();
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* PVDBCRTRACEARRAY_H */
|
||||
@@ -1,63 +0,0 @@
|
||||
/* removeRecord.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2013.04.18
|
||||
*/
|
||||
#ifndef REMOVERECORD_H
|
||||
#define REMOVERECORD_H
|
||||
|
||||
#include <pv/channelProviderLocal.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
|
||||
class RemoveRecord;
|
||||
typedef std::tr1::shared_ptr<RemoveRecord> RemoveRecordPtr;
|
||||
|
||||
/**
|
||||
* @brief Remove another record in the same database.
|
||||
*
|
||||
* A record to remove another record
|
||||
* It is meant to be used via a channelPutGet request.
|
||||
* The argument has one field: recordName.
|
||||
* The result has a field named status.
|
||||
*/
|
||||
class epicsShareClass RemoveRecord :
|
||||
public PVRecord
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(RemoveRecord);
|
||||
/**
|
||||
* Factory methods to create RemoveRecord.
|
||||
* @param recordName The name for the RemoveRecord.
|
||||
* @return A shared pointer to RemoveRecord..
|
||||
*/
|
||||
static RemoveRecordPtr create(
|
||||
std::string const & recordName);
|
||||
/**
|
||||
* standard init method required by PVRecord
|
||||
* @return true unless record name already exists.
|
||||
*/
|
||||
virtual bool init();
|
||||
/**
|
||||
* @brief Remove the record specified by recordName.
|
||||
*/
|
||||
virtual void process();
|
||||
private:
|
||||
RemoveRecord(
|
||||
std::string const & recordName,
|
||||
epics::pvData::PVStructurePtr const & pvStructure);
|
||||
epics::pvData::PVStringPtr pvRecordName;
|
||||
epics::pvData::PVStringPtr pvResult;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* REMOVERECORD_H */
|
||||
@@ -1,66 +0,0 @@
|
||||
/* traceRecord.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2013.04.18
|
||||
*/
|
||||
#ifndef TRACERECORD_H
|
||||
#define TRACERECORD_H
|
||||
|
||||
#include <pv/channelProviderLocal.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
|
||||
class TraceRecord;
|
||||
typedef std::tr1::shared_ptr<TraceRecord> TraceRecordPtr;
|
||||
|
||||
/**
|
||||
* @brief Trace activity of PVRecord.
|
||||
*
|
||||
* A record to set the trace value for another record
|
||||
* It is meant to be used via a channelPutGet request.
|
||||
* The argument has two fields: recordName and level.
|
||||
* The result has a field named status.
|
||||
*/
|
||||
class epicsShareClass TraceRecord :
|
||||
public PVRecord
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(TraceRecord);
|
||||
/**
|
||||
* @brief Factory method to create TraceRecord.
|
||||
*
|
||||
* @param recordName The name for the TraceRecord.
|
||||
* @return A shared pointer to TraceRecord..
|
||||
*/
|
||||
static TraceRecordPtr create(
|
||||
std::string const & recordName);
|
||||
/**
|
||||
* standard init method required by PVRecord
|
||||
* @return true unless record name already exists.
|
||||
*/
|
||||
virtual bool init();
|
||||
/**
|
||||
* @brief Set the trace level for record specified by recordName.
|
||||
*/
|
||||
virtual void process();
|
||||
private:
|
||||
TraceRecord(
|
||||
std::string const & recordName,
|
||||
epics::pvData::PVStructurePtr const & pvStructure);
|
||||
epics::pvData::PVStringPtr pvRecordName;
|
||||
epics::pvData::PVIntPtr pvLevel;
|
||||
epics::pvData::PVStringPtr pvResult;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* TRACERECORD_H */
|
||||
@@ -10,7 +10,9 @@
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <asLib.h>
|
||||
#include <epicsGuard.h>
|
||||
#include <epicsThread.h>
|
||||
#include <pv/pvData.h>
|
||||
@@ -23,6 +25,7 @@
|
||||
#include <pv/pvaVersionNum.h>
|
||||
#include <pv/serverContext.h>
|
||||
#include <pv/pvSubArrayCopy.h>
|
||||
#include <pv/security.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "pv/pvStructureCopy.h"
|
||||
@@ -74,13 +77,12 @@ static bool getProcess(PVStructurePtr pvRequest,bool processDefault)
|
||||
}
|
||||
|
||||
class ChannelProcessLocal :
|
||||
public ChannelProcess,
|
||||
public epics::pvAccess::ChannelProcess,
|
||||
public std::tr1::enable_shared_from_this<ChannelProcessLocal>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelProcessLocal);
|
||||
virtual ~ChannelProcessLocal();
|
||||
virtual void destroy() {} // DEPRECATED
|
||||
static ChannelProcessLocalPtr create(
|
||||
ChannelLocalPtr const &channelLocal,
|
||||
ChannelProcessRequester::shared_pointer const & channelProcessRequester,
|
||||
@@ -204,13 +206,12 @@ void ChannelProcessLocal::process()
|
||||
}
|
||||
|
||||
class ChannelGetLocal :
|
||||
public ChannelGet,
|
||||
public epics::pvAccess::ChannelGet,
|
||||
public std::tr1::enable_shared_from_this<ChannelGetLocal>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelGetLocal);
|
||||
virtual ~ChannelGetLocal();
|
||||
virtual void destroy() {} // DEPRECATED
|
||||
static ChannelGetLocalPtr create(
|
||||
ChannelLocalPtr const &channelLocal,
|
||||
ChannelGetRequester::shared_pointer const & channelGetRequester,
|
||||
@@ -328,6 +329,13 @@ void ChannelGetLocal::get()
|
||||
{
|
||||
ChannelGetRequester::shared_pointer requester = channelGetRequester.lock();
|
||||
if(!requester) return;
|
||||
ChannelLocalPtr channel(channelLocal.lock());
|
||||
if(!channel) throw std::logic_error("channel is deleted");
|
||||
if(!channel->canRead()) {
|
||||
Status status = Status::error("ChannelGet::get is not allowed");
|
||||
requester->getDone(status,getPtrSelf(),PVStructurePtr(),BitSetPtr());
|
||||
return;
|
||||
}
|
||||
PVRecordPtr pvr(pvRecord.lock());
|
||||
if(!pvr) throw std::logic_error("pvRecord is deleted");
|
||||
try {
|
||||
@@ -375,13 +383,12 @@ void ChannelGetLocal::get()
|
||||
}
|
||||
|
||||
class ChannelPutLocal :
|
||||
public ChannelPut,
|
||||
public epics::pvAccess::ChannelPut,
|
||||
public std::tr1::enable_shared_from_this<ChannelPutLocal>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelPutLocal);
|
||||
virtual ~ChannelPutLocal();
|
||||
virtual void destroy() {} // DEPRECATED
|
||||
static ChannelPutLocalPtr create(
|
||||
ChannelLocalPtr const &channelLocal,
|
||||
ChannelPutRequester::shared_pointer const & channelPutRequester,
|
||||
@@ -490,6 +497,13 @@ void ChannelPutLocal::get()
|
||||
{
|
||||
ChannelPutRequester::shared_pointer requester = channelPutRequester.lock();
|
||||
if(!requester) return;
|
||||
ChannelLocalPtr channel(channelLocal.lock());
|
||||
if(!channel) throw std::logic_error("channel is deleted");
|
||||
if(!channel->canRead()) {
|
||||
Status status = Status::error("ChannelPut::get is not allowed");
|
||||
requester->getDone(status,getPtrSelf(),PVStructurePtr(),BitSetPtr());
|
||||
return;
|
||||
}
|
||||
PVRecordPtr pvr(pvRecord.lock());
|
||||
if(!pvr) throw std::logic_error("pvRecord is deleted");
|
||||
try {
|
||||
@@ -520,6 +534,14 @@ void ChannelPutLocal::put(
|
||||
{
|
||||
ChannelPutRequester::shared_pointer requester = channelPutRequester.lock();
|
||||
if(!requester) return;
|
||||
ChannelLocalPtr channel(channelLocal.lock());
|
||||
if(!channel) throw std::logic_error("channel is deleted");
|
||||
if(!channel->canWrite()) {
|
||||
Status status = Status::error("ChannelPut::put is not allowed");
|
||||
requester->putDone(status,getPtrSelf());
|
||||
return;
|
||||
}
|
||||
|
||||
PVRecordPtr pvr(pvRecord.lock());
|
||||
if(!pvr) throw std::logic_error("pvRecord is deleted");
|
||||
try {
|
||||
@@ -545,13 +567,12 @@ void ChannelPutLocal::put(
|
||||
|
||||
|
||||
class ChannelPutGetLocal :
|
||||
public ChannelPutGet,
|
||||
public epics::pvAccess::ChannelPutGet,
|
||||
public std::tr1::enable_shared_from_this<ChannelPutGetLocal>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelPutGetLocal);
|
||||
virtual ~ChannelPutGetLocal();
|
||||
virtual void destroy() {} // DEPRECATED
|
||||
static ChannelPutGetLocalPtr create(
|
||||
ChannelLocalPtr const &channelLocal,
|
||||
ChannelPutGetRequester::shared_pointer const & channelPutGetRequester,
|
||||
@@ -681,6 +702,13 @@ void ChannelPutGetLocal::putGet(
|
||||
{
|
||||
ChannelPutGetRequester::shared_pointer requester = channelPutGetRequester.lock();
|
||||
if(!requester) return;
|
||||
ChannelLocalPtr channel(channelLocal.lock());
|
||||
if(!channel) throw std::logic_error("channel is deleted");
|
||||
if(!channel->canWrite()||!channel->canRead() ) {
|
||||
Status status = Status::error("ChannelPutGet::putGet is not allowed");
|
||||
requester->putGetDone(status,getPtrSelf(),PVStructurePtr(),BitSetPtr());
|
||||
return;
|
||||
}
|
||||
PVRecordPtr pvr(pvRecord.lock());
|
||||
if(!pvr) throw std::logic_error("pvRecord is deleted");
|
||||
try {
|
||||
@@ -709,6 +737,13 @@ void ChannelPutGetLocal::getPut()
|
||||
{
|
||||
ChannelPutGetRequester::shared_pointer requester = channelPutGetRequester.lock();
|
||||
if(!requester) return;
|
||||
ChannelLocalPtr channel(channelLocal.lock());
|
||||
if(!channel) throw std::logic_error("channel is deleted");
|
||||
if(!channel->canRead()) {
|
||||
Status status = Status::error("ChannelPutGet::getPut is not allowed");
|
||||
requester->getPutDone(status,getPtrSelf(),PVStructurePtr(),BitSetPtr());
|
||||
return;
|
||||
}
|
||||
PVRecordPtr pvr(pvRecord.lock());
|
||||
if(!pvr) throw std::logic_error("pvRecord is deleted");
|
||||
try {
|
||||
@@ -736,6 +771,13 @@ void ChannelPutGetLocal::getGet()
|
||||
{
|
||||
ChannelPutGetRequester::shared_pointer requester = channelPutGetRequester.lock();
|
||||
if(!requester) return;
|
||||
ChannelLocalPtr channel(channelLocal.lock());
|
||||
if(!channel) throw std::logic_error("channel is deleted");
|
||||
if(!channel->canRead()) {
|
||||
Status status = Status::error("ChannelPutGet::getGet is not allowed");
|
||||
requester->getPutDone(status,getPtrSelf(),PVStructurePtr(),BitSetPtr());
|
||||
return;
|
||||
}
|
||||
PVRecordPtr pvr(pvRecord.lock());
|
||||
if(!pvr) throw std::logic_error("pvRecord is deleted");
|
||||
try {
|
||||
@@ -760,13 +802,12 @@ void ChannelPutGetLocal::getGet()
|
||||
|
||||
|
||||
class ChannelRPCLocal :
|
||||
public ChannelRPC,
|
||||
public RPCResponseCallback,
|
||||
public epics::pvAccess::ChannelRPC,
|
||||
public epics::pvAccess::RPCResponseCallback,
|
||||
public std::tr1::enable_shared_from_this<ChannelRPCLocal>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelRPCLocal);
|
||||
virtual void destroy() {} // DEPRECATED
|
||||
static ChannelRPCLocalPtr create(
|
||||
ChannelLocalPtr const & channelLocal,
|
||||
ChannelRPCRequester::shared_pointer const & channelRPCRequester,
|
||||
@@ -871,7 +912,7 @@ void ChannelRPCLocal::processRequest(
|
||||
{
|
||||
result = service->request(pvArgument);
|
||||
}
|
||||
catch (RPCRequestException& rre)
|
||||
catch (epics::pvAccess::RPCRequestException& rre)
|
||||
{
|
||||
status = Status(rre.getStatus(), rre.what());
|
||||
ok = false;
|
||||
@@ -952,13 +993,12 @@ void ChannelRPCLocal::request(PVStructurePtr const & pvArgument)
|
||||
typedef std::tr1::shared_ptr<PVArray> PVArrayPtr;
|
||||
|
||||
class ChannelArrayLocal :
|
||||
public ChannelArray,
|
||||
public epics::pvAccess::ChannelArray,
|
||||
public std::tr1::enable_shared_from_this<ChannelArrayLocal>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelArrayLocal);
|
||||
virtual ~ChannelArrayLocal();
|
||||
virtual void destroy() {} // DEPRECATED
|
||||
static ChannelArrayLocalPtr create(
|
||||
ChannelLocalPtr const &channelLocal,
|
||||
ChannelArrayRequester::shared_pointer const & channelArrayRequester,
|
||||
@@ -1231,7 +1271,13 @@ ChannelLocal::ChannelLocal(
|
||||
:
|
||||
requester(requester),
|
||||
provider(provider),
|
||||
pvRecord(pvRecord)
|
||||
pvRecord(pvRecord),
|
||||
asLevel(pvRecord->getAsLevel()),
|
||||
asGroup(getAsGroup(pvRecord)),
|
||||
asUser(getAsUser(requester)),
|
||||
asHost(getAsHost(requester)),
|
||||
asMemberPvt(0),
|
||||
asClientPvt(0)
|
||||
{
|
||||
if(pvRecord->getTraceLevel()>0) {
|
||||
cout << "ChannelLocal::ChannelLocal()"
|
||||
@@ -1239,11 +1285,92 @@ ChannelLocal::ChannelLocal(
|
||||
<< " requester exists " << (requester ? "true" : "false")
|
||||
<< endl;
|
||||
}
|
||||
if (pvRecord->getAsGroup().empty() || asAddMember(&asMemberPvt, &asGroup[0]) != 0) {
|
||||
asMemberPvt = 0;
|
||||
}
|
||||
if (asMemberPvt) {
|
||||
asAddClient(&asClientPvt, asMemberPvt, asLevel, &asUser[0], &asHost[0]);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<char> ChannelLocal::toCharArray(const std::string& s)
|
||||
{
|
||||
std::vector<char> v(s.begin(), s.end());
|
||||
v.push_back('\0');
|
||||
return v;
|
||||
}
|
||||
|
||||
std::vector<char> ChannelLocal::getAsGroup(const PVRecordPtr& pvRecord)
|
||||
{
|
||||
return toCharArray(pvRecord->getAsGroup());
|
||||
}
|
||||
|
||||
std::vector<char> ChannelLocal::getAsUser(const ChannelRequester::shared_pointer& requester)
|
||||
{
|
||||
PeerInfo::const_shared_pointer info(requester->getPeerInfo());
|
||||
std::string user;
|
||||
if(info && info->identified) {
|
||||
if(info->authority=="ca") {
|
||||
user = info->account;
|
||||
size_t first = user.find_last_of('/');
|
||||
if(first != std::string::npos) {
|
||||
// prevent CA accounts like "<authority>/<user>"
|
||||
user = user.substr(first+1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
user = info->authority + "/" + info->account;
|
||||
}
|
||||
}
|
||||
return toCharArray(user);
|
||||
}
|
||||
|
||||
std::vector<char> ChannelLocal::getAsHost(const epics::pvAccess::ChannelRequester::shared_pointer& requester)
|
||||
{
|
||||
PeerInfo::const_shared_pointer info(requester->getPeerInfo());
|
||||
std::string host;
|
||||
if(info && info->identified) {
|
||||
host= info->peer;
|
||||
}
|
||||
else {
|
||||
// anonymous
|
||||
host = requester->getRequesterName();
|
||||
}
|
||||
|
||||
// handle form "ip:port"
|
||||
size_t last = host.find_first_of(':');
|
||||
if(last == std::string::npos) {
|
||||
last = host.size();
|
||||
}
|
||||
host.resize(last);
|
||||
return toCharArray(host);
|
||||
}
|
||||
|
||||
bool ChannelLocal::canWrite()
|
||||
{
|
||||
if(!asActive || (asClientPvt && asCheckPut(asClientPvt))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ChannelLocal::canRead()
|
||||
{
|
||||
if(!asActive || (asClientPvt && asCheckGet(asClientPvt))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
ChannelLocal::~ChannelLocal()
|
||||
{
|
||||
// cout << "~ChannelLocal()" << endl;
|
||||
if(asMemberPvt) {
|
||||
asRemoveMember(&asMemberPvt);
|
||||
asMemberPvt = 0;
|
||||
}
|
||||
if(asClientPvt) {
|
||||
asRemoveClient(&asClientPvt);
|
||||
asClientPvt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ChannelProvider::shared_pointer ChannelLocal::getProvider()
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
*/
|
||||
|
||||
#include <epicsThread.h>
|
||||
#include <asLib.h>
|
||||
#include <pv/serverContext.h>
|
||||
#include <pv/syncChannelFind.h>
|
||||
#include <pv/pvTimeStamp.h>
|
||||
@@ -176,4 +177,18 @@ Channel::shared_pointer ChannelProviderLocal::createChannel(
|
||||
return createChannel(channelName, channelRequester, priority);
|
||||
}
|
||||
|
||||
void ChannelProviderLocal::initAs(const std::string& filePath, const std::string& substitutions)
|
||||
{
|
||||
int status = asInitFile(filePath.c_str(), substitutions.c_str());
|
||||
if(status) {
|
||||
throw std::runtime_error("Invalid AS configuration.");
|
||||
}
|
||||
}
|
||||
|
||||
bool ChannelProviderLocal::isAsActive()
|
||||
{
|
||||
return asActive;
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
|
||||
@@ -141,7 +141,6 @@ class MonitorLocal :
|
||||
public:
|
||||
POINTER_DEFINITIONS(MonitorLocal);
|
||||
virtual ~MonitorLocal();
|
||||
virtual void destroy() {} // DEPRECATED
|
||||
virtual Status start();
|
||||
virtual Status stop();
|
||||
virtual MonitorElementPtr poll();
|
||||
@@ -293,6 +292,12 @@ void MonitorLocal::dataPut(PVRecordFieldPtr const & pvRecordField)
|
||||
{
|
||||
cout << "MonitorLocal::dataPut(pvRecordField)" << endl;
|
||||
}
|
||||
// If this record field is the master field, and the master field was not
|
||||
// requested, we do not proceed with copy
|
||||
bool isMasterField = pvRecordField->getPVRecord()->getPVStructure()->getFieldOffset()==0;
|
||||
if (isMasterField && !pvCopy->isMasterFieldRequested()) {
|
||||
return;
|
||||
}
|
||||
if(state!=active) return;
|
||||
{
|
||||
Lock xx(mutex);
|
||||
|
||||
@@ -46,7 +46,6 @@ extern "C" void pvdbl(const iocshArgBuf *args)
|
||||
static void registerChannelProviderLocal(void)
|
||||
{
|
||||
static int firstTime = 1;
|
||||
cout << "registerChannelProviderLocal firstTime " << (firstTime ? "true" : "false") << endl;
|
||||
if (firstTime) {
|
||||
firstTime = 0;
|
||||
iocshRegister(&pvdblFuncDef, pvdbl);
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
|
||||
SRC_DIRS += $(PVDATABASE_SRC)/special
|
||||
|
||||
LIBSRCS += traceRecord.cpp
|
||||
LIBSRCS += removeRecord.cpp
|
||||
LIBSRCS += addRecord.cpp
|
||||
LIBSRCS += processRecord.cpp
|
||||
DBD += pvdbcrScalarRecord.dbd
|
||||
DBD += pvdbcrScalarArrayRecord.dbd
|
||||
DBD += pvdbcrAddRecord.dbd
|
||||
DBD += pvdbcrRemoveRecord.dbd
|
||||
DBD += pvdbcrProcessRecord.dbd
|
||||
DBD += pvdbcrTraceRecord.dbd
|
||||
DBD += pvdbcrAllRecords.dbd
|
||||
|
||||
DBD += traceRecordRegister.dbd
|
||||
DBD += removeRecordRegister.dbd
|
||||
DBD += addRecordRegister.dbd
|
||||
DBD += processRecordRegister.dbd
|
||||
|
||||
LIBSRCS += traceRecordRegister.cpp
|
||||
LIBSRCS += removeRecordRegister.cpp
|
||||
LIBSRCS += addRecordRegister.cpp
|
||||
LIBSRCS += processRecordRegister.cpp
|
||||
LIBSRCS += pvdbcrScalarRecord.cpp
|
||||
LIBSRCS += pvdbcrScalarArrayRecord.cpp
|
||||
LIBSRCS += pvdbcrAddRecord.cpp
|
||||
LIBSRCS += pvdbcrRemoveRecord.cpp
|
||||
LIBSRCS += pvdbcrProcessRecord.cpp
|
||||
LIBSRCS += pvdbcrTraceRecord.cpp
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2013.07.24
|
||||
*/
|
||||
|
||||
|
||||
/* Author: Marty Kraimer */
|
||||
#include <epicsThread.h>
|
||||
#include <iocsh.h>
|
||||
#include <pv/event.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/serverContext.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/pvTimeStamp.h>
|
||||
#include <pv/rpcService.h>
|
||||
|
||||
// The following must be the last include for code pvDatabase uses
|
||||
#include <epicsExport.h>
|
||||
#define epicsExportSharedSymbols
|
||||
#include "pv/pvStructureCopy.h"
|
||||
#include "pv/pvDatabase.h"
|
||||
#include "pv/addRecord.h"
|
||||
|
||||
using namespace epics::pvData;
|
||||
using namespace epics::pvAccess;
|
||||
using namespace epics::pvDatabase;
|
||||
using namespace std;
|
||||
|
||||
static const iocshArg testArg0 = { "recordName", iocshArgString };
|
||||
static const iocshArg *testArgs[] = {
|
||||
&testArg0};
|
||||
|
||||
static const iocshFuncDef addRecordFuncDef = {"addRecordCreate", 1,testArgs};
|
||||
|
||||
static void addRecordCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
char *recordName = args[0].sval;
|
||||
if(!recordName) {
|
||||
throw std::runtime_error("addRecordCreate invalid number of arguments");
|
||||
}
|
||||
AddRecordPtr record = AddRecord::create(recordName);
|
||||
bool result = PVDatabase::getMaster()->addRecord(record);
|
||||
if(!result) cout << "recordname" << " not added" << endl;
|
||||
}
|
||||
|
||||
static void addRecordRegister(void)
|
||||
{
|
||||
static int firstTime = 1;
|
||||
if (firstTime) {
|
||||
firstTime = 0;
|
||||
iocshRegister(&addRecordFuncDef, addRecordCallFunc);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
epicsExportRegistrar(addRecordRegister);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
registrar("addRecordRegister")
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2013.07.24
|
||||
*/
|
||||
|
||||
|
||||
/* Author: Marty Kraimer */
|
||||
|
||||
#include <epicsThread.h>
|
||||
#include <iocsh.h>
|
||||
#include <pv/event.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/serverContext.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/pvTimeStamp.h>
|
||||
#include <pv/rpcService.h>
|
||||
|
||||
// The following must be the last include for code pvDatabase uses
|
||||
#include <epicsExport.h>
|
||||
#define epicsExportSharedSymbols
|
||||
#include "pv/pvStructureCopy.h"
|
||||
#include "pv/pvDatabase.h"
|
||||
#include "pv/processRecord.h"
|
||||
|
||||
|
||||
using namespace epics::pvData;
|
||||
using namespace epics::pvAccess;
|
||||
using namespace epics::pvDatabase;
|
||||
using namespace std;
|
||||
|
||||
static const iocshArg testArg0 = { "recordName", iocshArgString };
|
||||
static const iocshArg testArg1 = { "delay", iocshArgDouble };
|
||||
static const iocshArg *testArgs[] = {
|
||||
&testArg0,&testArg1};
|
||||
|
||||
static const iocshFuncDef processRecordFuncDef = {"processRecordCreate", 2,testArgs};
|
||||
|
||||
static void processRecordCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
char *recordName = args[0].sval;
|
||||
if(!recordName) {
|
||||
throw std::runtime_error("processRecordCreate invalid number of arguments");
|
||||
}
|
||||
double delay = args[1].dval;
|
||||
if(delay<0.0) delay = 1.0;
|
||||
ProcessRecordPtr record = ProcessRecord::create(recordName,delay);
|
||||
bool result = PVDatabase::getMaster()->addRecord(record);
|
||||
if(!result) cout << "recordname" << " not added" << endl;
|
||||
}
|
||||
|
||||
static void processRecordRegister(void)
|
||||
{
|
||||
static int firstTime = 1;
|
||||
if (firstTime) {
|
||||
firstTime = 0;
|
||||
iocshRegister(&processRecordFuncDef, processRecordCallFunc);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
epicsExportRegistrar(processRecordRegister);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
registrar("processRecordRegister")
|
||||
@@ -1,46 +1,36 @@
|
||||
/* addRecord.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2013.04.18
|
||||
* @date 2021.04.07
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <iocsh.h>
|
||||
#include <pv/standardField.h>
|
||||
#include <pv/pvTimeStamp.h>
|
||||
#include <pv/standardPVField.h>
|
||||
#include <pv/timeStamp.h>
|
||||
#include <pv/rpcService.h>
|
||||
#include <pv/pvTimeStamp.h>
|
||||
#include <pv/alarm.h>
|
||||
#include <pv/pvAlarm.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/status.h>
|
||||
#include <pv/serverContext.h>
|
||||
#include <pv/rpcService.h>
|
||||
|
||||
|
||||
// The following must be the last include for code exampleLink uses
|
||||
#include <epicsExport.h>
|
||||
#define epicsExportSharedSymbols
|
||||
#include "pv/pvStructureCopy.h"
|
||||
#include "pv/pvDatabase.h"
|
||||
#include "pv/addRecord.h"
|
||||
|
||||
using std::tr1::static_pointer_cast;
|
||||
#include "pv/pvdbcrAddRecord.h"
|
||||
using namespace epics::pvData;
|
||||
using namespace epics::pvAccess;
|
||||
using namespace std;
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
AddRecordPtr AddRecord::create(
|
||||
std::string const & recordName)
|
||||
PvdbcrAddRecordPtr PvdbcrAddRecord::create(
|
||||
std::string const & recordName,
|
||||
int asLevel,std::string const & asGroup)
|
||||
{
|
||||
FieldCreatePtr fieldCreate = getFieldCreate();
|
||||
PVDataCreatePtr pvDataCreate = getPVDataCreate();
|
||||
@@ -55,20 +45,21 @@ AddRecordPtr AddRecord::create(
|
||||
endNested()->
|
||||
createStructure();
|
||||
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure);
|
||||
AddRecordPtr pvRecord(
|
||||
new AddRecord(recordName,pvStructure));
|
||||
PvdbcrAddRecordPtr pvRecord(
|
||||
new PvdbcrAddRecord(recordName,pvStructure,asLevel,asGroup));
|
||||
if(!pvRecord->init()) pvRecord.reset();
|
||||
return pvRecord;
|
||||
}
|
||||
|
||||
AddRecord::AddRecord(
|
||||
PvdbcrAddRecord::PvdbcrAddRecord(
|
||||
std::string const & recordName,
|
||||
epics::pvData::PVStructurePtr const & pvStructure)
|
||||
: PVRecord(recordName,pvStructure)
|
||||
PVStructurePtr const & pvStructure,
|
||||
int asLevel,std::string const & asGroup)
|
||||
: PVRecord(recordName,pvStructure,asLevel,asGroup)
|
||||
{
|
||||
}
|
||||
|
||||
bool AddRecord::init()
|
||||
bool PvdbcrAddRecord::init()
|
||||
{
|
||||
initPVRecord();
|
||||
PVStructurePtr pvStructure = getPVStructure();
|
||||
@@ -79,7 +70,7 @@ bool AddRecord::init()
|
||||
return true;
|
||||
}
|
||||
|
||||
void AddRecord::process()
|
||||
void PvdbcrAddRecord::process()
|
||||
{
|
||||
PVDataCreatePtr pvDataCreate = getPVDataCreate();
|
||||
string name = pvRecordName->get();
|
||||
@@ -102,7 +93,7 @@ void AddRecord::process()
|
||||
pvResult->put(name + " union most be a structure");
|
||||
return;
|
||||
}
|
||||
StructureConstPtr st = static_pointer_cast<const Structure>(pvField->getField());
|
||||
StructureConstPtr st = std::tr1::static_pointer_cast<const Structure>(pvField->getField());
|
||||
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(st);
|
||||
PVRecordPtr pvRec = PVRecord::create(name,pvStructure);
|
||||
bool result = PVDatabase::getMaster()->addRecord(pvRec);
|
||||
@@ -112,6 +103,45 @@ void AddRecord::process()
|
||||
pvResult->put("failure");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
|
||||
static const iocshArg arg0 = { "recordName", iocshArgString };
|
||||
static const iocshArg arg1 = { "asLevel", iocshArgInt };
|
||||
static const iocshArg arg2 = { "asGroup", iocshArgString };
|
||||
static const iocshArg *args[] = {&arg0,&arg1,&arg2};
|
||||
|
||||
static const iocshFuncDef pvdbcrAddRecordFuncDef = {"pvdbcrAddRecord", 3,args};
|
||||
|
||||
static void pvdbcrAddRecordCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
char *sval = args[0].sval;
|
||||
if(!sval) {
|
||||
throw std::runtime_error("pvdbcrAddRecord recordName not specified");
|
||||
}
|
||||
string recordName = string(sval);
|
||||
int asLevel = args[1].ival;
|
||||
string asGroup("DEFAULT");
|
||||
sval = args[2].sval;
|
||||
if(sval) {
|
||||
asGroup = string(sval);
|
||||
}
|
||||
epics::pvDatabase::PvdbcrAddRecordPtr record = epics::pvDatabase::PvdbcrAddRecord::create(recordName);
|
||||
record->setAsLevel(asLevel);
|
||||
record->setAsGroup(asGroup);
|
||||
epics::pvDatabase::PVDatabasePtr master = epics::pvDatabase::PVDatabase::getMaster();
|
||||
bool result = master->addRecord(record);
|
||||
if(!result) cout << "recordname " << recordName << " not added" << endl;
|
||||
}
|
||||
|
||||
static void pvdbcrAddRecord(void)
|
||||
{
|
||||
static int firstTime = 1;
|
||||
if (firstTime) {
|
||||
firstTime = 0;
|
||||
iocshRegister(&pvdbcrAddRecordFuncDef, pvdbcrAddRecordCallFunc);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
epicsExportRegistrar(pvdbcrAddRecord);
|
||||
}
|
||||
1
src/special/pvdbcrAddRecord.dbd
Normal file
1
src/special/pvdbcrAddRecord.dbd
Normal file
@@ -0,0 +1 @@
|
||||
registrar("pvdbcrAddRecord")
|
||||
6
src/special/pvdbcrAllRecords.dbd
Normal file
6
src/special/pvdbcrAllRecords.dbd
Normal file
@@ -0,0 +1,6 @@
|
||||
include "pvdbcrAddRecord.dbd"
|
||||
include "pvdbcrRemoveRecord.dbd"
|
||||
include "pvdbcrProcessRecord.dbd"
|
||||
include "pvdbcrTraceRecord.dbd"
|
||||
include "pvdbcrScalarRecord.dbd"
|
||||
include "pvdbcrScalarArrayRecord.dbd"
|
||||
@@ -1,47 +1,39 @@
|
||||
/* processRecord.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2013.04.18
|
||||
* @date 2021.04.07
|
||||
*/
|
||||
#include <map>
|
||||
#include <epicsThread.h>
|
||||
#include <epicsGuard.h>
|
||||
#include <pv/event.h>
|
||||
#include <shareLib.h>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/pvTimeStamp.h>
|
||||
#include <iocsh.h>
|
||||
#include <pv/standardField.h>
|
||||
#include <pv/standardPVField.h>
|
||||
#include <pv/timeStamp.h>
|
||||
#include <pv/rpcService.h>
|
||||
#include <pv/pvTimeStamp.h>
|
||||
#include <pv/alarm.h>
|
||||
#include <pv/pvAlarm.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/status.h>
|
||||
#include <pv/serverContext.h>
|
||||
#include <pv/rpcService.h>
|
||||
|
||||
#include <epicsExport.h>
|
||||
#define epicsExportSharedSymbols
|
||||
#include "pv/pvStructureCopy.h"
|
||||
#include "pv/pvDatabase.h"
|
||||
#include "pv/processRecord.h"
|
||||
|
||||
using std::tr1::static_pointer_cast;
|
||||
#include "pv/pvdbcrProcessRecord.h"
|
||||
using namespace epics::pvData;
|
||||
using namespace epics::pvAccess;
|
||||
using namespace std;
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
ProcessRecordPtr ProcessRecord::create(
|
||||
std::string const & recordName,double delay)
|
||||
PvdbcrProcessRecordPtr PvdbcrProcessRecord::create(
|
||||
std::string const & recordName,double delay,
|
||||
int asLevel,std::string const & asGroup)
|
||||
{
|
||||
FieldCreatePtr fieldCreate = getFieldCreate();
|
||||
PVDataCreatePtr pvDataCreate = getPVDataCreate();
|
||||
@@ -55,39 +47,24 @@ ProcessRecordPtr ProcessRecord::create(
|
||||
endNested()->
|
||||
createStructure();
|
||||
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure);
|
||||
ProcessRecordPtr pvRecord(
|
||||
new ProcessRecord(recordName,pvStructure,delay));
|
||||
PvdbcrProcessRecordPtr pvRecord(
|
||||
new PvdbcrProcessRecord(recordName,pvStructure,delay,asLevel,asGroup));
|
||||
if(!pvRecord->init()) pvRecord.reset();
|
||||
return pvRecord;
|
||||
}
|
||||
|
||||
void ProcessRecord::startThread()
|
||||
{
|
||||
thread = EpicsThreadPtr(new epicsThread(
|
||||
*this,
|
||||
"processRecord",
|
||||
epicsThreadGetStackSize(epicsThreadStackSmall),
|
||||
epicsThreadPriorityLow));
|
||||
thread->start();
|
||||
}
|
||||
|
||||
void ProcessRecord::stop()
|
||||
{
|
||||
runStop.signal();
|
||||
runReturn.wait();
|
||||
}
|
||||
|
||||
|
||||
ProcessRecord::ProcessRecord(
|
||||
PvdbcrProcessRecord::PvdbcrProcessRecord(
|
||||
std::string const & recordName,
|
||||
epics::pvData::PVStructurePtr const & pvStructure,double delay)
|
||||
: PVRecord(recordName,pvStructure),
|
||||
epics::pvData::PVStructurePtr const & pvStructure,double delay,
|
||||
int asLevel,std::string const & asGroup)
|
||||
: PVRecord(recordName,pvStructure,asLevel,asGroup),
|
||||
delay(delay),
|
||||
pvDatabase(PVDatabase::getMaster())
|
||||
{
|
||||
}
|
||||
|
||||
bool ProcessRecord::init()
|
||||
bool PvdbcrProcessRecord::init()
|
||||
{
|
||||
initPVRecord();
|
||||
PVStructurePtr pvStructure = getPVStructure();
|
||||
@@ -100,7 +77,27 @@ bool ProcessRecord::init()
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProcessRecord::process()
|
||||
void PvdbcrProcessRecord::setDelay(double delay) {this->delay = delay;}
|
||||
|
||||
double PvdbcrProcessRecord::getDelay() {return delay;}
|
||||
|
||||
void PvdbcrProcessRecord::startThread()
|
||||
{
|
||||
thread = EpicsThreadPtr(new epicsThread(
|
||||
*this,
|
||||
"processRecord",
|
||||
epicsThreadGetStackSize(epicsThreadStackSmall),
|
||||
epicsThreadPriorityLow));
|
||||
thread->start();
|
||||
}
|
||||
|
||||
void PvdbcrProcessRecord::stop()
|
||||
{
|
||||
runStop.signal();
|
||||
runReturn.wait();
|
||||
}
|
||||
|
||||
void PvdbcrProcessRecord::process()
|
||||
{
|
||||
string recordName = pvRecordName->get();
|
||||
string command = pvCommand->get();
|
||||
@@ -135,7 +132,7 @@ void ProcessRecord::process()
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessRecord::run()
|
||||
void PvdbcrProcessRecord::run()
|
||||
{
|
||||
while(true) {
|
||||
if(runStop.tryWait()) {
|
||||
@@ -161,6 +158,49 @@ void ProcessRecord::run()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
|
||||
static const iocshArg arg0 = { "recordName", iocshArgString };
|
||||
static const iocshArg arg1 = { "delay", iocshArgDouble };
|
||||
static const iocshArg arg2 = { "asLevel", iocshArgInt };
|
||||
static const iocshArg arg3 = { "asGroup", iocshArgString };
|
||||
static const iocshArg *args[] = {&arg0,&arg1,&arg2,&arg3};
|
||||
|
||||
static const iocshFuncDef pvdbcrProcessRecordFuncDef = {"pvdbcrProcessRecord", 4,args};
|
||||
|
||||
static void pvdbcrProcessRecordCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
char *sval = args[0].sval;
|
||||
if(!sval) {
|
||||
throw std::runtime_error("pvdbcrProcessRecord recordName not specified");
|
||||
}
|
||||
string recordName = string(sval);
|
||||
double delay = args[1].dval;
|
||||
if(delay<0.0) delay = 1.0;
|
||||
int asLevel = args[2].ival;
|
||||
string asGroup("DEFAULT");
|
||||
sval = args[3].sval;
|
||||
if(sval) {
|
||||
asGroup = string(sval);
|
||||
}
|
||||
epics::pvDatabase::PvdbcrProcessRecordPtr record
|
||||
= epics::pvDatabase::PvdbcrProcessRecord::create(recordName,delay);
|
||||
record->setAsLevel(asLevel);
|
||||
record->setAsGroup(asGroup);
|
||||
epics::pvDatabase::PVDatabasePtr master = epics::pvDatabase::PVDatabase::getMaster();
|
||||
bool result = master->addRecord(record);
|
||||
if(!result) cout << "recordname " << recordName << " not added" << endl;
|
||||
}
|
||||
|
||||
static void pvdbcrProcessRecord(void)
|
||||
{
|
||||
static int firstTime = 1;
|
||||
if (firstTime) {
|
||||
firstTime = 0;
|
||||
iocshRegister(&pvdbcrProcessRecordFuncDef, pvdbcrProcessRecordCallFunc);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
epicsExportRegistrar(pvdbcrProcessRecord);
|
||||
}
|
||||
1
src/special/pvdbcrProcessRecord.dbd
Normal file
1
src/special/pvdbcrProcessRecord.dbd
Normal file
@@ -0,0 +1 @@
|
||||
registrar("pvdbcrProcessRecord")
|
||||
123
src/special/pvdbcrRemoveRecord.cpp
Normal file
123
src/special/pvdbcrRemoveRecord.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2021.04.07
|
||||
*/
|
||||
#include <iocsh.h>
|
||||
#include <pv/standardField.h>
|
||||
#include <pv/standardPVField.h>
|
||||
#include <pv/timeStamp.h>
|
||||
#include <pv/pvTimeStamp.h>
|
||||
#include <pv/alarm.h>
|
||||
#include <pv/pvAlarm.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/serverContext.h>
|
||||
#include <pv/rpcService.h>
|
||||
|
||||
#include <epicsExport.h>
|
||||
#define epicsExportSharedSymbols
|
||||
#include "pv/pvDatabase.h"
|
||||
#include "pv/pvdbcrRemoveRecord.h"
|
||||
using namespace epics::pvData;
|
||||
using namespace std;
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
PvdbcrRemoveRecordPtr PvdbcrRemoveRecord::create(
|
||||
std::string const & recordName,
|
||||
int asLevel,std::string const & asGroup)
|
||||
{
|
||||
FieldCreatePtr fieldCreate = getFieldCreate();
|
||||
PVDataCreatePtr pvDataCreate = getPVDataCreate();
|
||||
StructureConstPtr topStructure = fieldCreate->createFieldBuilder()->
|
||||
addNestedStructure("argument")->
|
||||
add("recordName",pvString)->
|
||||
endNested()->
|
||||
addNestedStructure("result") ->
|
||||
add("status",pvString) ->
|
||||
endNested()->
|
||||
createStructure();
|
||||
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure);
|
||||
PvdbcrRemoveRecordPtr pvRecord(
|
||||
new PvdbcrRemoveRecord(recordName,pvStructure,
|
||||
asLevel,asGroup));
|
||||
if(!pvRecord->init()) pvRecord.reset();
|
||||
return pvRecord;
|
||||
}
|
||||
|
||||
PvdbcrRemoveRecord::PvdbcrRemoveRecord(
|
||||
std::string const & recordName,
|
||||
epics::pvData::PVStructurePtr const & pvStructure,
|
||||
int asLevel,std::string const & asGroup)
|
||||
: PVRecord(recordName,pvStructure,asLevel,asGroup)
|
||||
{
|
||||
}
|
||||
|
||||
bool PvdbcrRemoveRecord::init()
|
||||
{
|
||||
initPVRecord();
|
||||
PVStructurePtr pvStructure = getPVStructure();
|
||||
pvRecordName = pvStructure->getSubField<PVString>("argument.recordName");
|
||||
if(!pvRecordName) return false;
|
||||
pvResult = pvStructure->getSubField<PVString>("result.status");
|
||||
if(!pvResult) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PvdbcrRemoveRecord::process()
|
||||
{
|
||||
string name = pvRecordName->get();
|
||||
PVRecordPtr pvRecord = PVDatabase::getMaster()->findRecord(name);
|
||||
if(!pvRecord) {
|
||||
pvResult->put(name + " not found");
|
||||
return;
|
||||
}
|
||||
pvRecord->remove();
|
||||
pvResult->put("success");
|
||||
}
|
||||
}}
|
||||
|
||||
static const iocshArg arg0 = { "recordName", iocshArgString };
|
||||
static const iocshArg arg1 = { "asLevel", iocshArgInt };
|
||||
static const iocshArg arg2 = { "asGroup", iocshArgString };
|
||||
static const iocshArg *args[] = {&arg0,&arg1,&arg2};
|
||||
|
||||
static const iocshFuncDef pvdbcrRemoveRecordFuncDef = {"pvdbcrRemoveRecord", 3,args};
|
||||
|
||||
static void pvdbcrRemoveRecordCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
char *sval = args[0].sval;
|
||||
if(!sval) {
|
||||
throw std::runtime_error("pvdbcrRemoveRecord recordName not specified");
|
||||
}
|
||||
string recordName = string(sval);
|
||||
int asLevel = args[1].ival;
|
||||
string asGroup("DEFAULT");
|
||||
sval = args[2].sval;
|
||||
if(sval) {
|
||||
asGroup = string(sval);
|
||||
}
|
||||
epics::pvDatabase::PvdbcrRemoveRecordPtr record = epics::pvDatabase::PvdbcrRemoveRecord::create(recordName);
|
||||
record->setAsLevel(asLevel);
|
||||
record->setAsGroup(asGroup);
|
||||
epics::pvDatabase::PVDatabasePtr master = epics::pvDatabase::PVDatabase::getMaster();
|
||||
bool result = master->addRecord(record);
|
||||
if(!result) cout << "recordname " << recordName << " not added" << endl;
|
||||
}
|
||||
|
||||
static void pvdbcrRemoveRecord(void)
|
||||
{
|
||||
static int firstTime = 1;
|
||||
if (firstTime) {
|
||||
firstTime = 0;
|
||||
iocshRegister(&pvdbcrRemoveRecordFuncDef, pvdbcrRemoveRecordCallFunc);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
epicsExportRegistrar(pvdbcrRemoveRecord);
|
||||
}
|
||||
1
src/special/pvdbcrRemoveRecord.dbd
Normal file
1
src/special/pvdbcrRemoveRecord.dbd
Normal file
@@ -0,0 +1 @@
|
||||
registrar("pvdbcrRemoveRecord")
|
||||
103
src/special/pvdbcrScalarArrayRecord.cpp
Normal file
103
src/special/pvdbcrScalarArrayRecord.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2021.04.07
|
||||
*/
|
||||
#include <iocsh.h>
|
||||
#include <pv/standardField.h>
|
||||
#include <pv/standardPVField.h>
|
||||
#include <pv/timeStamp.h>
|
||||
#include <pv/pvTimeStamp.h>
|
||||
#include <pv/alarm.h>
|
||||
#include <pv/pvAlarm.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/serverContext.h>
|
||||
#include <pv/rpcService.h>
|
||||
|
||||
// The following must be the last include for code pvDatabase implements
|
||||
#include <epicsExport.h>
|
||||
#define epicsExportSharedSymbols
|
||||
#include "pv/pvDatabase.h"
|
||||
#include "pv/pvdbcrScalarArrayRecord.h"
|
||||
using namespace epics::pvData;
|
||||
using namespace std;
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
PvdbcrScalarArrayRecord::PvdbcrScalarArrayRecord(
|
||||
std::string const & recordName,epics::pvData::PVStructurePtr const & pvStructure,
|
||||
int asLevel,std::string const & asGroup)
|
||||
: PVRecord(recordName,pvStructure,asLevel,asGroup)
|
||||
{}
|
||||
|
||||
PvdbcrScalarArrayRecordPtr PvdbcrScalarArrayRecord::create(
|
||||
std::string const & recordName,std::string const & scalarType,
|
||||
int asLevel,std::string const & asGroup)
|
||||
{
|
||||
ScalarType st = epics::pvData::ScalarTypeFunc::getScalarType(scalarType);
|
||||
FieldCreatePtr fieldCreate = getFieldCreate();
|
||||
StandardFieldPtr standardField = getStandardField();
|
||||
PVDataCreatePtr pvDataCreate = getPVDataCreate();
|
||||
StructureConstPtr top = fieldCreate->createFieldBuilder()->
|
||||
addArray("value",st) ->
|
||||
add("timeStamp",standardField->timeStamp()) ->
|
||||
add("alarm",standardField->alarm()) ->
|
||||
createStructure();
|
||||
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(top);
|
||||
PvdbcrScalarArrayRecordPtr pvRecord(new PvdbcrScalarArrayRecord(recordName,pvStructure,asLevel,asGroup));
|
||||
pvRecord->initPVRecord();
|
||||
return pvRecord;
|
||||
};
|
||||
}}
|
||||
|
||||
static const iocshArg arg0 = { "recordName", iocshArgString };
|
||||
static const iocshArg arg1 = { "scalarType", iocshArgString };
|
||||
static const iocshArg arg2 = { "asLevel", iocshArgInt };
|
||||
static const iocshArg arg3 = { "asGroup", iocshArgString };
|
||||
static const iocshArg *args[] = {&arg0,&arg1,&arg2,&arg3};
|
||||
|
||||
static const iocshFuncDef pvdbcrScalarArrayFuncDef = {"pvdbcrScalarArrayRecord", 4,args};
|
||||
|
||||
static void pvdbcrScalarArrayCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
char *sval = args[0].sval;
|
||||
if(!sval) {
|
||||
throw std::runtime_error("pvdbcrScalarArrayRecord recordName not specified");
|
||||
}
|
||||
string recordName = string(sval);
|
||||
sval = args[1].sval;
|
||||
if(!sval) {
|
||||
throw std::runtime_error("pvdbcrScalarArrayRecord scalarType not specified");
|
||||
}
|
||||
string scalarType = string(sval);
|
||||
int asLevel = args[2].ival;
|
||||
string asGroup("DEFAULT");
|
||||
sval = args[3].sval;
|
||||
if(sval) {
|
||||
asGroup = string(sval);
|
||||
}
|
||||
epics::pvDatabase::PvdbcrScalarArrayRecordPtr record
|
||||
= epics::pvDatabase::PvdbcrScalarArrayRecord::create(recordName,scalarType);
|
||||
epics::pvDatabase::PVDatabasePtr master = epics::pvDatabase::PVDatabase::getMaster();
|
||||
record->setAsLevel(asLevel);
|
||||
record->setAsGroup(asGroup);
|
||||
bool result = master->addRecord(record);
|
||||
if(!result) cout << "recordname " << recordName << " not added" << endl;
|
||||
}
|
||||
|
||||
static void pvdbcrScalarArrayRecord(void)
|
||||
{
|
||||
static int firstTime = 1;
|
||||
if (firstTime) {
|
||||
firstTime = 0;
|
||||
iocshRegister(&pvdbcrScalarArrayFuncDef, pvdbcrScalarArrayCallFunc);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
epicsExportRegistrar(pvdbcrScalarArrayRecord);
|
||||
}
|
||||
1
src/special/pvdbcrScalarArrayRecord.dbd
Normal file
1
src/special/pvdbcrScalarArrayRecord.dbd
Normal file
@@ -0,0 +1 @@
|
||||
registrar("pvdbcrScalarArrayRecord")
|
||||
103
src/special/pvdbcrScalarRecord.cpp
Normal file
103
src/special/pvdbcrScalarRecord.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2021.04.07
|
||||
*/
|
||||
#include <iocsh.h>
|
||||
#include <pv/standardField.h>
|
||||
#include <pv/standardPVField.h>
|
||||
#include <pv/timeStamp.h>
|
||||
#include <pv/pvTimeStamp.h>
|
||||
#include <pv/alarm.h>
|
||||
#include <pv/pvAlarm.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/serverContext.h>
|
||||
#include <pv/rpcService.h>
|
||||
|
||||
// The following must be the last include for code pvDatabase implements
|
||||
#include <epicsExport.h>
|
||||
#define epicsExportSharedSymbols
|
||||
#include "pv/pvDatabase.h"
|
||||
#include "pv/pvdbcrScalarRecord.h"
|
||||
using namespace epics::pvData;
|
||||
using namespace std;
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
PvdbcrScalarRecord::PvdbcrScalarRecord(
|
||||
std::string const & recordName,epics::pvData::PVStructurePtr const & pvStructure,
|
||||
int asLevel,std::string const & asGroup)
|
||||
: PVRecord(recordName,pvStructure,asLevel,asGroup)
|
||||
{}
|
||||
|
||||
PvdbcrScalarRecordPtr PvdbcrScalarRecord::create(
|
||||
std::string const & recordName,std::string const & scalarType,
|
||||
int asLevel,std::string const & asGroup)
|
||||
{
|
||||
ScalarType st = epics::pvData::ScalarTypeFunc::getScalarType(scalarType);
|
||||
FieldCreatePtr fieldCreate = getFieldCreate();
|
||||
StandardFieldPtr standardField = getStandardField();
|
||||
PVDataCreatePtr pvDataCreate = getPVDataCreate();
|
||||
StructureConstPtr top = fieldCreate->createFieldBuilder()->
|
||||
add("value",st) ->
|
||||
add("timeStamp",standardField->timeStamp()) ->
|
||||
add("alarm",standardField->alarm()) ->
|
||||
createStructure();
|
||||
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(top);
|
||||
PvdbcrScalarRecordPtr pvRecord(new PvdbcrScalarRecord(recordName,pvStructure,asLevel,asGroup));
|
||||
pvRecord->initPVRecord();
|
||||
return pvRecord;
|
||||
};
|
||||
}}
|
||||
|
||||
static const iocshArg arg0 = { "recordName", iocshArgString };
|
||||
static const iocshArg arg1 = { "scalarType", iocshArgString };
|
||||
static const iocshArg arg2 = { "asLevel", iocshArgInt };
|
||||
static const iocshArg arg3 = { "asGroup", iocshArgString };
|
||||
static const iocshArg *args[] = {&arg0,&arg1,&arg2,&arg3};
|
||||
|
||||
static const iocshFuncDef pvdbcrScalarFuncDef = {"pvdbcrScalarRecord", 4,args};
|
||||
|
||||
static void pvdbcrScalarCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
char *sval = args[0].sval;
|
||||
if(!sval) {
|
||||
throw std::runtime_error("pvdbcrScalarRecord recordName not specified");
|
||||
}
|
||||
string recordName = string(sval);
|
||||
sval = args[1].sval;
|
||||
if(!sval) {
|
||||
throw std::runtime_error("pvdbcrScalarRecord scalarType not specified");
|
||||
}
|
||||
string scalarType = string(sval);
|
||||
int asLevel = args[2].ival;
|
||||
string asGroup("DEFAULT");
|
||||
sval = args[3].sval;
|
||||
if(sval) {
|
||||
asGroup = string(sval);
|
||||
}
|
||||
epics::pvDatabase::PvdbcrScalarRecordPtr record
|
||||
= epics::pvDatabase::PvdbcrScalarRecord::create(recordName,scalarType);
|
||||
epics::pvDatabase::PVDatabasePtr master = epics::pvDatabase::PVDatabase::getMaster();
|
||||
record->setAsLevel(asLevel);
|
||||
record->setAsGroup(asGroup);
|
||||
bool result = master->addRecord(record);
|
||||
if(!result) cout << "recordname " << recordName << " not added" << endl;
|
||||
}
|
||||
|
||||
static void pvdbcrScalarRecord(void)
|
||||
{
|
||||
static int firstTime = 1;
|
||||
if (firstTime) {
|
||||
firstTime = 0;
|
||||
iocshRegister(&pvdbcrScalarFuncDef, pvdbcrScalarCallFunc);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
epicsExportRegistrar(pvdbcrScalarRecord);
|
||||
}
|
||||
1
src/special/pvdbcrScalarRecord.dbd
Normal file
1
src/special/pvdbcrScalarRecord.dbd
Normal file
@@ -0,0 +1 @@
|
||||
registrar("pvdbcrScalarRecord")
|
||||
128
src/special/pvdbcrTraceRecord.cpp
Normal file
128
src/special/pvdbcrTraceRecord.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2021.04.07
|
||||
*/
|
||||
#include <iocsh.h>
|
||||
#include <pv/standardField.h>
|
||||
#include <pv/standardPVField.h>
|
||||
#include <pv/timeStamp.h>
|
||||
#include <pv/pvTimeStamp.h>
|
||||
#include <pv/alarm.h>
|
||||
#include <pv/pvAlarm.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/serverContext.h>
|
||||
#include <pv/rpcService.h>
|
||||
|
||||
// The following must be the last include
|
||||
#include <epicsExport.h>
|
||||
#define epicsExportSharedSymbols
|
||||
#include "pv/pvDatabase.h"
|
||||
#include "pv/pvdbcrTraceRecord.h"
|
||||
using namespace epics::pvData;
|
||||
using namespace std;
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
PvdbcrTraceRecordPtr PvdbcrTraceRecord::create(
|
||||
std::string const & recordName,
|
||||
int asLevel,std::string const & asGroup)
|
||||
{
|
||||
FieldCreatePtr fieldCreate = getFieldCreate();
|
||||
PVDataCreatePtr pvDataCreate = getPVDataCreate();
|
||||
StructureConstPtr topStructure = fieldCreate->createFieldBuilder()->
|
||||
addNestedStructure("argument")->
|
||||
add("recordName",pvString)->
|
||||
add("level",pvInt)->
|
||||
endNested()->
|
||||
addNestedStructure("result") ->
|
||||
add("status",pvString) ->
|
||||
endNested()->
|
||||
createStructure();
|
||||
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure);
|
||||
PvdbcrTraceRecordPtr pvRecord(
|
||||
new PvdbcrTraceRecord(recordName,pvStructure,asLevel,asGroup));
|
||||
if(!pvRecord->init()) pvRecord.reset();
|
||||
return pvRecord;
|
||||
}
|
||||
|
||||
PvdbcrTraceRecord::PvdbcrTraceRecord(
|
||||
std::string const & recordName,
|
||||
epics::pvData::PVStructurePtr const & pvStructure,
|
||||
int asLevel,std::string const & asGroup)
|
||||
: PVRecord(recordName,pvStructure,asLevel,asGroup)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool PvdbcrTraceRecord::init()
|
||||
{
|
||||
initPVRecord();
|
||||
PVStructurePtr pvStructure = getPVStructure();
|
||||
pvRecordName = pvStructure->getSubField<PVString>("argument.recordName");
|
||||
if(!pvRecordName) return false;
|
||||
pvLevel = pvStructure->getSubField<PVInt>("argument.level");
|
||||
if(!pvLevel) return false;
|
||||
pvResult = pvStructure->getSubField<PVString>("result.status");
|
||||
if(!pvResult) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PvdbcrTraceRecord::process()
|
||||
{
|
||||
string name = pvRecordName->get();
|
||||
PVRecordPtr pvRecord = PVDatabase::getMaster()->findRecord(name);
|
||||
if(!pvRecord) {
|
||||
pvResult->put(name + " not found");
|
||||
return;
|
||||
}
|
||||
pvRecord->setTraceLevel(pvLevel->get());
|
||||
pvResult->put("success");
|
||||
}
|
||||
}}
|
||||
|
||||
static const iocshArg arg0 = { "recordName", iocshArgString };
|
||||
static const iocshArg arg1 = { "asLevel", iocshArgInt };
|
||||
static const iocshArg arg2 = { "asGroup", iocshArgString };
|
||||
static const iocshArg *args[] = {&arg0,&arg1,&arg2};
|
||||
|
||||
static const iocshFuncDef pvdbcrTraceRecordFuncDef = {"pvdbcrTraceRecord", 3,args};
|
||||
|
||||
static void pvdbcrTraceRecordCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
char *sval = args[0].sval;
|
||||
if(!sval) {
|
||||
throw std::runtime_error("pvdbcrTraceRecord recordName not specified");
|
||||
}
|
||||
string recordName = string(sval);
|
||||
int asLevel = args[1].ival;
|
||||
string asGroup("DEFAULT");
|
||||
sval = args[2].sval;
|
||||
if(sval) {
|
||||
asGroup = string(sval);
|
||||
}
|
||||
epics::pvDatabase::PvdbcrTraceRecordPtr record
|
||||
= epics::pvDatabase::PvdbcrTraceRecord::create(recordName);
|
||||
record->setAsLevel(asLevel);
|
||||
record->setAsGroup(asGroup);
|
||||
epics::pvDatabase::PVDatabasePtr master = epics::pvDatabase::PVDatabase::getMaster();
|
||||
bool result = master->addRecord(record);
|
||||
if(!result) cout << "recordname " << recordName << " not added" << endl;
|
||||
}
|
||||
|
||||
static void pvdbcrTraceRecord(void)
|
||||
{
|
||||
static int firstTime = 1;
|
||||
if (firstTime) {
|
||||
firstTime = 0;
|
||||
iocshRegister(&pvdbcrTraceRecordFuncDef, pvdbcrTraceRecordCallFunc);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
epicsExportRegistrar(pvdbcrTraceRecord);
|
||||
}
|
||||
1
src/special/pvdbcrTraceRecord.dbd
Normal file
1
src/special/pvdbcrTraceRecord.dbd
Normal file
@@ -0,0 +1 @@
|
||||
registrar("pvdbcrTraceRecord")
|
||||
@@ -1,92 +0,0 @@
|
||||
/* removeRecord.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2013.04.18
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/pvTimeStamp.h>
|
||||
#include <pv/timeStamp.h>
|
||||
#include <pv/rpcService.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/status.h>
|
||||
#include <pv/serverContext.h>
|
||||
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "pv/pvStructureCopy.h"
|
||||
#include "pv/pvDatabase.h"
|
||||
#include "pv/removeRecord.h"
|
||||
|
||||
using std::tr1::static_pointer_cast;
|
||||
using namespace epics::pvData;
|
||||
using namespace epics::pvAccess;
|
||||
using namespace std;
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
RemoveRecordPtr RemoveRecord::create(
|
||||
std::string const & recordName)
|
||||
{
|
||||
FieldCreatePtr fieldCreate = getFieldCreate();
|
||||
PVDataCreatePtr pvDataCreate = getPVDataCreate();
|
||||
StructureConstPtr topStructure = fieldCreate->createFieldBuilder()->
|
||||
addNestedStructure("argument")->
|
||||
add("recordName",pvString)->
|
||||
endNested()->
|
||||
addNestedStructure("result") ->
|
||||
add("status",pvString) ->
|
||||
endNested()->
|
||||
createStructure();
|
||||
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure);
|
||||
RemoveRecordPtr pvRecord(
|
||||
new RemoveRecord(recordName,pvStructure));
|
||||
if(!pvRecord->init()) pvRecord.reset();
|
||||
return pvRecord;
|
||||
}
|
||||
|
||||
RemoveRecord::RemoveRecord(
|
||||
std::string const & recordName,
|
||||
epics::pvData::PVStructurePtr const & pvStructure)
|
||||
: PVRecord(recordName,pvStructure)
|
||||
{
|
||||
}
|
||||
|
||||
bool RemoveRecord::init()
|
||||
{
|
||||
initPVRecord();
|
||||
PVStructurePtr pvStructure = getPVStructure();
|
||||
pvRecordName = pvStructure->getSubField<PVString>("argument.recordName");
|
||||
if(!pvRecordName) return false;
|
||||
pvResult = pvStructure->getSubField<PVString>("result.status");
|
||||
if(!pvResult) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void RemoveRecord::process()
|
||||
{
|
||||
string name = pvRecordName->get();
|
||||
PVRecordPtr pvRecord = PVDatabase::getMaster()->findRecord(name);
|
||||
if(!pvRecord) {
|
||||
pvResult->put(name + " not found");
|
||||
return;
|
||||
}
|
||||
pvRecord->remove();
|
||||
pvResult->put("success");
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2013.07.24
|
||||
*/
|
||||
|
||||
|
||||
/* Author: Marty Kraimer */
|
||||
#include <epicsThread.h>
|
||||
#include <iocsh.h>
|
||||
#include <pv/event.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/serverContext.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/pvTimeStamp.h>
|
||||
#include <pv/rpcService.h>
|
||||
|
||||
// The following must be the last include for code pvDatabase uses
|
||||
#include <epicsExport.h>
|
||||
#define epicsExportSharedSymbols
|
||||
#include "pv/pvStructureCopy.h"
|
||||
#include "pv/pvDatabase.h"
|
||||
#include "pv/removeRecord.h"
|
||||
|
||||
using namespace epics::pvData;
|
||||
using namespace epics::pvAccess;
|
||||
using namespace epics::pvDatabase;
|
||||
using namespace std;
|
||||
|
||||
static const iocshArg testArg0 = { "recordName", iocshArgString };
|
||||
static const iocshArg *testArgs[] = {
|
||||
&testArg0};
|
||||
|
||||
static const iocshFuncDef removeRecordFuncDef = {"removeRecordCreate", 1,testArgs};
|
||||
|
||||
static void removeRecordCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
char *recordName = args[0].sval;
|
||||
if(!recordName) {
|
||||
throw std::runtime_error("removeRecordCreate invalid number of arguments");
|
||||
}
|
||||
RemoveRecordPtr record = RemoveRecord::create(recordName);
|
||||
bool result = PVDatabase::getMaster()->addRecord(record);
|
||||
if(!result) cout << "recordname" << " not added" << endl;
|
||||
}
|
||||
|
||||
static void removeRecordRegister(void)
|
||||
{
|
||||
static int firstTime = 1;
|
||||
if (firstTime) {
|
||||
firstTime = 0;
|
||||
iocshRegister(&removeRecordFuncDef, removeRecordCallFunc);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
epicsExportRegistrar(removeRecordRegister);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
registrar("removeRecordRegister")
|
||||
@@ -1,95 +0,0 @@
|
||||
/* traceRecord.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2013.04.18
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/pvTimeStamp.h>
|
||||
#include <pv/timeStamp.h>
|
||||
#include <pv/rpcService.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/status.h>
|
||||
#include <pv/serverContext.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "pv/pvStructureCopy.h"
|
||||
#include "pv/channelProviderLocal.h"
|
||||
#include "pv/traceRecord.h"
|
||||
|
||||
using std::tr1::static_pointer_cast;
|
||||
using namespace epics::pvData;
|
||||
using namespace epics::pvAccess;
|
||||
using namespace std;
|
||||
|
||||
namespace epics { namespace pvDatabase {
|
||||
|
||||
TraceRecordPtr TraceRecord::create(
|
||||
std::string const & recordName)
|
||||
{
|
||||
FieldCreatePtr fieldCreate = getFieldCreate();
|
||||
PVDataCreatePtr pvDataCreate = getPVDataCreate();
|
||||
StructureConstPtr topStructure = fieldCreate->createFieldBuilder()->
|
||||
addNestedStructure("argument")->
|
||||
add("recordName",pvString)->
|
||||
add("level",pvInt)->
|
||||
endNested()->
|
||||
addNestedStructure("result") ->
|
||||
add("status",pvString) ->
|
||||
endNested()->
|
||||
createStructure();
|
||||
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure);
|
||||
TraceRecordPtr pvRecord(
|
||||
new TraceRecord(recordName,pvStructure));
|
||||
if(!pvRecord->init()) pvRecord.reset();
|
||||
return pvRecord;
|
||||
}
|
||||
|
||||
TraceRecord::TraceRecord(
|
||||
std::string const & recordName,
|
||||
epics::pvData::PVStructurePtr const & pvStructure)
|
||||
: PVRecord(recordName,pvStructure)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool TraceRecord::init()
|
||||
{
|
||||
initPVRecord();
|
||||
PVStructurePtr pvStructure = getPVStructure();
|
||||
pvRecordName = pvStructure->getSubField<PVString>("argument.recordName");
|
||||
if(!pvRecordName) return false;
|
||||
pvLevel = pvStructure->getSubField<PVInt>("argument.level");
|
||||
if(!pvLevel) return false;
|
||||
pvResult = pvStructure->getSubField<PVString>("result.status");
|
||||
if(!pvResult) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void TraceRecord::process()
|
||||
{
|
||||
string name = pvRecordName->get();
|
||||
PVRecordPtr pvRecord = PVDatabase::getMaster()->findRecord(name);
|
||||
if(!pvRecord) {
|
||||
pvResult->put(name + " not found");
|
||||
return;
|
||||
}
|
||||
pvRecord->setTraceLevel(pvLevel->get());
|
||||
pvResult->put("success");
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2013.07.24
|
||||
*/
|
||||
|
||||
|
||||
/* Author: Marty Kraimer */
|
||||
|
||||
#include <epicsThread.h>
|
||||
#include <iocsh.h>
|
||||
#include <pv/event.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/serverContext.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/pvTimeStamp.h>
|
||||
#include <pv/rpcService.h>
|
||||
|
||||
// The following must be the last include for code pvDatabase uses
|
||||
#include <epicsExport.h>
|
||||
#define epicsExportSharedSymbols
|
||||
#include "pv/pvStructureCopy.h"
|
||||
#include "pv/pvDatabase.h"
|
||||
#include "pv/traceRecord.h"
|
||||
|
||||
using namespace epics::pvData;
|
||||
using namespace epics::pvAccess;
|
||||
using namespace epics::pvDatabase;
|
||||
using namespace std;
|
||||
|
||||
static const iocshArg testArg0 = { "recordName", iocshArgString };
|
||||
static const iocshArg *testArgs[] = {
|
||||
&testArg0};
|
||||
|
||||
static const iocshFuncDef traceRecordFuncDef = {"traceRecordCreate", 1,testArgs};
|
||||
|
||||
static void traceRecordCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
char *recordName = args[0].sval;
|
||||
if(!recordName) {
|
||||
throw std::runtime_error("traceRecordCreate invalid number of arguments");
|
||||
}
|
||||
TraceRecordPtr record = TraceRecord::create(recordName);
|
||||
bool result = PVDatabase::getMaster()->addRecord(record);
|
||||
if(!result) cout << "recordname" << " not added" << endl;
|
||||
}
|
||||
|
||||
static void traceRecordRegister(void)
|
||||
{
|
||||
static int firstTime = 1;
|
||||
if (firstTime) {
|
||||
firstTime = 0;
|
||||
iocshRegister(&traceRecordFuncDef, traceRecordCallFunc);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
epicsExportRegistrar(traceRecordRegister);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
registrar("traceRecordRegister")
|
||||
@@ -46,6 +46,7 @@ epics::pvData::StructureConstPtr ControlSupport::controlField(ScalarType scalarT
|
||||
|
||||
ControlSupportPtr ControlSupport::create(PVRecordPtr const & pvRecord)
|
||||
{
|
||||
cerr << "ControlSupport IS DEPRECATED\n";
|
||||
ControlSupportPtr support(new ControlSupport(pvRecord));
|
||||
return support;
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ epics::pvData::StructureConstPtr ScalarAlarmSupport::scalarAlarmField()
|
||||
|
||||
ScalarAlarmSupportPtr ScalarAlarmSupport::create(PVRecordPtr const & pvRecord)
|
||||
{
|
||||
cerr << "ScalarAlarmSupport IS DEPRECATED\n";
|
||||
ScalarAlarmSupportPtr support(new ScalarAlarmSupport(pvRecord));
|
||||
return support;
|
||||
}
|
||||
|
||||
@@ -33,3 +33,8 @@ TESTPROD_HOST += testPVAServer
|
||||
testPVAServer_SRCS += testPVAServer.cpp
|
||||
testHarness_SRCS += testPVAServer.cpp
|
||||
TESTS += testPVAServer
|
||||
|
||||
TESTPROD_HOST += testChannelMonitor
|
||||
testChannelMonitor_SRCS += testChannelMonitor.cpp
|
||||
testHarness_SRCS += testChannelMonitor.cpp
|
||||
TESTS += testChannelMonitor
|
||||
|
||||
@@ -46,7 +46,6 @@ public:
|
||||
std::string const & recordName,
|
||||
epics::pvData::PVStructurePtr const & pvStructure);
|
||||
virtual ~PowerSupply();
|
||||
virtual void destroy();
|
||||
virtual bool init();
|
||||
virtual void process();
|
||||
void put(double power,double voltage);
|
||||
@@ -111,11 +110,6 @@ inline PowerSupply::~PowerSupply()
|
||||
{
|
||||
}
|
||||
|
||||
inline void PowerSupply::destroy()
|
||||
{
|
||||
PVRecord::destroy();
|
||||
}
|
||||
|
||||
inline bool PowerSupply::init()
|
||||
{
|
||||
initPVRecord();
|
||||
|
||||
312
test/src/testChannelMonitor.cpp
Normal file
312
test/src/testChannelMonitor.cpp
Normal file
@@ -0,0 +1,312 @@
|
||||
/* testChannelMonitor.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
|
||||
#include <epicsUnitTest.h>
|
||||
#include <testMain.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
|
||||
#include <epicsStdio.h>
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsEvent.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
#include <pv/standardField.h>
|
||||
#include <pv/standardPVField.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/channelProviderLocal.h>
|
||||
#include <pv/serverContext.h>
|
||||
#include <pv/event.h>
|
||||
#include <pv/clientFactory.h>
|
||||
|
||||
using namespace std;
|
||||
using std::tr1::static_pointer_cast;
|
||||
using namespace epics::pvData;
|
||||
using namespace epics::pvAccess;
|
||||
using namespace epics::pvDatabase;
|
||||
namespace TR1 = std::tr1;
|
||||
|
||||
static bool debug = true;
|
||||
|
||||
PVStructurePtr createTestPvStructure()
|
||||
{
|
||||
FieldCreatePtr fieldCreate = getFieldCreate();
|
||||
StandardFieldPtr standardField = getStandardField()
|
||||
;
|
||||
PVDataCreatePtr pvDataCreate = getPVDataCreate();
|
||||
|
||||
return pvDataCreate->createPVStructure(
|
||||
fieldCreate->createFieldBuilder()->
|
||||
add("id",pvInt) ->
|
||||
add("x",pvInt) ->
|
||||
add("y",pvInt) ->
|
||||
add("z",pvInt) ->
|
||||
add("alarm",standardField->alarm()) ->
|
||||
add("timeStamp",standardField->timeStamp()) ->
|
||||
createStructure());
|
||||
}
|
||||
|
||||
class ChannelMonitorRequesterImpl : public MonitorRequester
|
||||
{
|
||||
public:
|
||||
|
||||
ChannelMonitorRequesterImpl(const std::string& channelName_)
|
||||
: channelName(channelName_)
|
||||
, lastReceivedPvStructure(createTestPvStructure())
|
||||
, lastReceivedBitSet()
|
||||
{
|
||||
}
|
||||
|
||||
virtual string getRequesterName()
|
||||
{
|
||||
return "ChannelMonitorRequesterImpl";
|
||||
}
|
||||
|
||||
virtual void message(const std::string& message, MessageType messageType)
|
||||
{
|
||||
cout << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")" << endl;
|
||||
}
|
||||
|
||||
virtual void monitorConnect(const epics::pvData::Status& status, const Monitor::shared_pointer& /*monitor*/, const Structure::const_shared_pointer& /*structure*/)
|
||||
{
|
||||
if (status.isSuccess()) {
|
||||
// show warning
|
||||
if (!status.isOK()) {
|
||||
cout << "[" << channelName << "] channel monitor create: " << status << endl;
|
||||
}
|
||||
connectionEvent.signal();
|
||||
}
|
||||
else {
|
||||
cout << "[" << channelName << "] failed to create channel monitor: " << status << endl;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void monitorEvent(const Monitor::shared_pointer& monitor)
|
||||
{
|
||||
MonitorElement::shared_pointer element;
|
||||
while ((element = monitor->poll())) {
|
||||
cout << "changed/overrun " << *element->changedBitSet << '/' << *element->overrunBitSet << endl;
|
||||
if (!lastReceivedBitSet) {
|
||||
lastReceivedBitSet = BitSet::create(element->changedBitSet->size());
|
||||
}
|
||||
lastReceivedBitSet->clear();
|
||||
*lastReceivedBitSet |= *element->changedBitSet;
|
||||
lastReceivedPvStructure->copyUnchecked(*element->pvStructurePtr);
|
||||
monitor->release(element);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void unlisten(const Monitor::shared_pointer& /*monitor*/)
|
||||
{
|
||||
}
|
||||
|
||||
bool waitUntilConnected(double timeOut)
|
||||
{
|
||||
return connectionEvent.wait(timeOut);
|
||||
}
|
||||
|
||||
PVStructurePtr getLastReceivedPvStructure()
|
||||
{
|
||||
return lastReceivedPvStructure;
|
||||
}
|
||||
|
||||
BitSetPtr getLastReceivedBitSet()
|
||||
{
|
||||
return lastReceivedBitSet;
|
||||
}
|
||||
|
||||
private:
|
||||
Event event;
|
||||
Event connectionEvent;
|
||||
string channelName;
|
||||
PVStructurePtr lastReceivedPvStructure;
|
||||
BitSetPtr lastReceivedBitSet;
|
||||
};
|
||||
|
||||
class ChannelRequesterImpl : public ChannelRequester
|
||||
{
|
||||
private:
|
||||
Event event;
|
||||
|
||||
public:
|
||||
|
||||
virtual string getRequesterName()
|
||||
{
|
||||
return "ChannelRequesterImpl";
|
||||
};
|
||||
|
||||
virtual void message(const std::string& message, MessageType messageType)
|
||||
{
|
||||
cout << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")" << endl;
|
||||
}
|
||||
|
||||
virtual void channelCreated(const Status& status, const Channel::shared_pointer& channel)
|
||||
{
|
||||
if (status.isSuccess()) {
|
||||
// show warning
|
||||
if (!status.isOK()) {
|
||||
cout << "[" << channel->getChannelName() << "] channel create: " << status << endl;
|
||||
}
|
||||
}
|
||||
else {
|
||||
cout << "[" << channel->getChannelName() << "] failed to create a channel: " << status << endl;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void channelStateChange(const Channel::shared_pointer& /*channel*/, Channel::ConnectionState connectionState)
|
||||
{
|
||||
if (connectionState == Channel::CONNECTED) {
|
||||
event.signal();
|
||||
}
|
||||
else {
|
||||
cout << Channel::ConnectionStateNames[connectionState] << endl;
|
||||
exit(3);
|
||||
}
|
||||
}
|
||||
|
||||
bool waitUntilConnected(double timeOut)
|
||||
{
|
||||
return event.wait(timeOut);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static void test()
|
||||
{
|
||||
PVDatabasePtr master = PVDatabase::getMaster();
|
||||
ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
|
||||
string recordName = "positions";
|
||||
PVStructurePtr pvStructure = createTestPvStructure();
|
||||
PVRecordPtr pvRecord = PVRecord::create(recordName,pvStructure);
|
||||
master->addRecord(pvRecord);
|
||||
pvRecord = master->findRecord(recordName);
|
||||
{
|
||||
pvRecord->lock();
|
||||
pvRecord->process();
|
||||
pvRecord->unlock();
|
||||
}
|
||||
if(debug) {cout << "processed positions" << endl; }
|
||||
ServerContext::shared_pointer ctx = startPVAServer("local",0,true,true);
|
||||
testOk1(ctx.get() != 0);
|
||||
|
||||
ClientFactory::start();
|
||||
ChannelProvider::shared_pointer provider = ChannelProviderRegistry::clients()->getProvider("pva");
|
||||
|
||||
cout << "creating channel: " << recordName << endl;
|
||||
TR1::shared_ptr<ChannelRequesterImpl> channelRequesterImpl(new ChannelRequesterImpl());
|
||||
|
||||
Channel::shared_pointer channel = provider->createChannel(recordName, channelRequesterImpl);
|
||||
bool channelConnected = channelRequesterImpl->waitUntilConnected(1.0);
|
||||
testOk1(channelConnected);
|
||||
if (channelConnected) {
|
||||
string remoteAddress = channel->getRemoteAddress();
|
||||
cout << "remote address: " << remoteAddress << endl;
|
||||
}
|
||||
|
||||
string request = "";
|
||||
PVStructure::shared_pointer pvRequest = CreateRequest::create()->createRequest(request);
|
||||
TR1::shared_ptr<ChannelMonitorRequesterImpl> cmRequesterImpl(new ChannelMonitorRequesterImpl(channel->getChannelName()));
|
||||
Monitor::shared_pointer monitor = channel->createMonitor(cmRequesterImpl, pvRequest);
|
||||
bool monitorConnected = cmRequesterImpl->waitUntilConnected(1.0);
|
||||
testOk1(monitorConnected);
|
||||
Status status = monitor->start();
|
||||
testOk1(status.isOK());
|
||||
epicsThreadSleep(1);
|
||||
|
||||
// Set id, x
|
||||
{
|
||||
pvRecord->beginGroupPut();
|
||||
PVIntPtr id = pvStructure->getSubField<PVInt>("id");
|
||||
id->put(1);
|
||||
PVIntPtr x = pvStructure->getSubField<PVInt>("x");
|
||||
x->put(1);
|
||||
pvRecord->endGroupPut();
|
||||
}
|
||||
epicsThreadSleep(1);
|
||||
|
||||
// Changed set for (id,x): 0 unset, 1 set, 2 set, 3 unset, 4 unset
|
||||
BitSetPtr changedSet = cmRequesterImpl->getLastReceivedBitSet();
|
||||
testOk1(!changedSet->get(0) && changedSet->get(1) && changedSet->get(2) && !changedSet->get(3) && !changedSet->get(4));
|
||||
testOk1(*pvStructure == *cmRequesterImpl->getLastReceivedPvStructure());
|
||||
|
||||
// Set id, y
|
||||
{
|
||||
pvRecord->beginGroupPut();
|
||||
PVIntPtr id = pvStructure->getSubField<PVInt>("id");
|
||||
id->put(2);
|
||||
PVIntPtr y = pvStructure->getSubField<PVInt>("y");
|
||||
y->put(2);
|
||||
pvRecord->endGroupPut();
|
||||
}
|
||||
epicsThreadSleep(1);
|
||||
|
||||
// Changed set for (id,y): 0 unset, 1 set, 2 unset, 3 set, 4 unset
|
||||
changedSet = cmRequesterImpl->getLastReceivedBitSet();
|
||||
testOk1(!changedSet->get(0) && changedSet->get(1) && !changedSet->get(2) && changedSet->get(3) && !changedSet->get(4));
|
||||
testOk1(*pvStructure == *cmRequesterImpl->getLastReceivedPvStructure());
|
||||
|
||||
// Set id, z
|
||||
{
|
||||
pvRecord->beginGroupPut();
|
||||
PVIntPtr id = pvStructure->getSubField<PVInt>("id");
|
||||
id->put(3);
|
||||
PVIntPtr z = pvStructure->getSubField<PVInt>("z");
|
||||
z->put(3);
|
||||
pvRecord->endGroupPut();
|
||||
}
|
||||
epicsThreadSleep(1);
|
||||
|
||||
// Changed set for (id,z): 0 unset, 1 set, 2 unset, 3 unset, 4 set
|
||||
changedSet = cmRequesterImpl->getLastReceivedBitSet();
|
||||
testOk1(!changedSet->get(0) && changedSet->get(1) && !changedSet->get(2) && !changedSet->get(3) && changedSet->get(4));
|
||||
testOk1(*pvStructure == *cmRequesterImpl->getLastReceivedPvStructure());
|
||||
|
||||
status = monitor->stop();
|
||||
testOk1(status.isOK());
|
||||
|
||||
// Test master field
|
||||
request = "field(_)";
|
||||
pvRequest = CreateRequest::create()->createRequest(request);
|
||||
cmRequesterImpl = TR1::shared_ptr<ChannelMonitorRequesterImpl>(new ChannelMonitorRequesterImpl(channel->getChannelName()));
|
||||
monitor = channel->createMonitor(cmRequesterImpl, pvRequest);
|
||||
monitorConnected = cmRequesterImpl->waitUntilConnected(1.0);
|
||||
testOk1(monitorConnected);
|
||||
status = monitor->start();
|
||||
testOk1(status.isOK());
|
||||
epicsThreadSleep(1);
|
||||
{
|
||||
pvRecord->beginGroupPut();
|
||||
PVIntPtr id = pvStructure->getSubField<PVInt>("id");
|
||||
id->put(4);
|
||||
PVIntPtr x = pvStructure->getSubField<PVInt>("x");
|
||||
x->put(4);
|
||||
pvRecord->endGroupPut();
|
||||
}
|
||||
epicsThreadSleep(1);
|
||||
// Changed set with master field requested: 0 set
|
||||
changedSet = cmRequesterImpl->getLastReceivedBitSet();
|
||||
testOk1(changedSet->get(0));
|
||||
testOk1(*pvStructure == *cmRequesterImpl->getLastReceivedPvStructure());
|
||||
|
||||
status = monitor->stop();
|
||||
testOk1(status.isOK());
|
||||
|
||||
}
|
||||
|
||||
MAIN(testChannelMonitor)
|
||||
{
|
||||
testPlan(16);
|
||||
test();
|
||||
return 0;
|
||||
}
|
||||
@@ -274,6 +274,34 @@ static void testPVScalarArray(
|
||||
}
|
||||
}
|
||||
|
||||
static void testMasterField(PVRecordPtr const& pvRecord)
|
||||
{
|
||||
CreateRequest::shared_pointer createRequest = CreateRequest::create();
|
||||
PVStructurePtr pvRequest = createRequest->createRequest("field(_)");
|
||||
if(debug) {
|
||||
cout << "pvRequest" << *pvRequest << endl ;
|
||||
}
|
||||
PVStructurePtr pvStructureRecord = pvRecord->getPVRecordStructure()->getPVStructure();
|
||||
PVCopyPtr pvCopy = PVCopy::create(pvStructureRecord,pvRequest,"");
|
||||
PVStructurePtr pvMasterField = pvCopy->getPVMaster();
|
||||
if(debug) {
|
||||
cout << "PV structure from record" << endl << *pvStructureRecord << endl;
|
||||
cout << "Master PV structure from copy" << endl << *pvMasterField << endl;
|
||||
cout << "Master PV structure from copy offset " << pvMasterField->getFieldOffset() << endl;
|
||||
}
|
||||
testOk1(pvCopy->isMasterFieldRequested());
|
||||
testOk1(pvMasterField->getNumberFields() == pvStructureRecord->getNumberFields());
|
||||
testOk1(pvMasterField->getFieldOffset() == 0);
|
||||
PVStructurePtr pvStructureCopy = pvCopy->createPVStructure();
|
||||
BitSetPtr bitSet = BitSetPtr(new BitSet(pvStructureCopy->getNumberFields()));
|
||||
pvCopy->initCopy(pvStructureCopy, bitSet);
|
||||
if(debug) {
|
||||
cout << "PV structure from copy" << endl << *pvStructureCopy << endl;
|
||||
cout << "PV structure from copy offset " << pvStructureCopy->getFieldOffset() << endl;
|
||||
}
|
||||
testOk1(pvMasterField->getNumberFields() == pvStructureCopy->getNumberFields());
|
||||
}
|
||||
|
||||
static void scalarTest()
|
||||
{
|
||||
if(debug) {cout << endl << endl << "****scalarTest****" << endl;}
|
||||
@@ -393,11 +421,21 @@ static void powerSupplyTest()
|
||||
testPVScalar(valueNameRecord,valueNameCopy,pvRecord,pvCopy);
|
||||
}
|
||||
|
||||
static void masterFieldTest()
|
||||
{
|
||||
if(debug) {
|
||||
cout << endl << endl << "****masterFieldTest****" << endl;
|
||||
}
|
||||
PVRecordPtr pvRecord = createScalar("doubleRecord",pvDouble,"alarm,timeStamp,display");
|
||||
testMasterField(pvRecord);
|
||||
}
|
||||
|
||||
MAIN(testPVCopy)
|
||||
{
|
||||
testPlan(67);
|
||||
testPlan(71);
|
||||
scalarTest();
|
||||
arrayTest();
|
||||
powerSupplyTest();
|
||||
masterFieldTest();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -332,14 +332,128 @@ static void ignoreTest()
|
||||
testOk1(nset==3);
|
||||
}
|
||||
|
||||
static void debugOutput(const string& what, bool result, uint32 nSet, BitSetPtr bitSet, PVStructurePtr pvStructureCopy)
|
||||
{
|
||||
if(debug) {
|
||||
cout << what
|
||||
<< " result " << (result ? "true" : "false")
|
||||
<< " nSet " << nSet
|
||||
<< " bitSet " << *bitSet
|
||||
<< "\n pvStructureCopy\n" << pvStructureCopy
|
||||
<< "\n";
|
||||
}
|
||||
}
|
||||
|
||||
static void dataDistributorTest()
|
||||
{
|
||||
if(debug) {cout << endl << endl << "****dataDistributorTest****" << endl;}
|
||||
bool result = false;
|
||||
uint32 nSet = 0;
|
||||
|
||||
// Create test structure
|
||||
PVStructurePtr pvRecordStructure(getStandardPVField()->scalar(pvInt,""));
|
||||
PVIntPtr pvValue(pvRecordStructure->getSubField<PVInt>("value"));
|
||||
PVRecordPtr pvRecord(PVRecord::create("intRecord",pvRecordStructure));
|
||||
if(debug) {
|
||||
cout << " pvRecordStructure\n" << pvRecordStructure
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
// Request distributor plugin with trigger field value
|
||||
PVStructurePtr pvRequest(CreateRequest::create()->createRequest("_[distributor=trigger:value]"));
|
||||
|
||||
// Create clients
|
||||
PVCopyPtr pvCopy1(PVCopy::create(pvRecordStructure,pvRequest,""));
|
||||
PVStructurePtr pvStructureCopy1(pvCopy1->createPVStructure());
|
||||
BitSetPtr bitSet1(new BitSet(pvStructureCopy1->getNumberFields()));
|
||||
|
||||
PVCopyPtr pvCopy2(PVCopy::create(pvRecordStructure,pvRequest,""));
|
||||
PVStructurePtr pvStructureCopy2(pvCopy2->createPVStructure());
|
||||
BitSetPtr bitSet2(new BitSet(pvStructureCopy2->getNumberFields()));
|
||||
|
||||
// Update 0: both clients get it
|
||||
result = pvCopy1->updateCopySetBitSet(pvStructureCopy1,bitSet1);
|
||||
nSet = bitSet1->cardinality();
|
||||
debugOutput("client 1: update 0", result, nSet, bitSet1, pvStructureCopy1);
|
||||
testOk1(result==true);
|
||||
testOk1(nSet==1);
|
||||
|
||||
result = pvCopy2->updateCopySetBitSet(pvStructureCopy2,bitSet2);
|
||||
nSet = bitSet2->cardinality();
|
||||
debugOutput("client 2: update 0", result, nSet, bitSet2, pvStructureCopy2);
|
||||
testOk1(result==true);
|
||||
testOk1(nSet==1);
|
||||
|
||||
// Update 1: only client 1 gets it
|
||||
pvValue->put(1);
|
||||
|
||||
result = pvCopy1->updateCopySetBitSet(pvStructureCopy1,bitSet1);
|
||||
nSet = bitSet1->cardinality();
|
||||
debugOutput("client 1: update 1", result, nSet, bitSet1, pvStructureCopy1);
|
||||
testOk1(result==true);
|
||||
testOk1(nSet==1);
|
||||
|
||||
result = pvCopy2->updateCopySetBitSet(pvStructureCopy2,bitSet2);
|
||||
nSet = bitSet2->cardinality();
|
||||
debugOutput("client 2: update 1", result, nSet, bitSet2, pvStructureCopy2);
|
||||
testOk1(result==false);
|
||||
testOk1(nSet==0);
|
||||
|
||||
// Update 2: only client 2 gets it
|
||||
pvValue->put(2);
|
||||
|
||||
result = pvCopy1->updateCopySetBitSet(pvStructureCopy1,bitSet1);
|
||||
nSet = bitSet1->cardinality();
|
||||
debugOutput("client 1: update 2", result, nSet, bitSet1, pvStructureCopy1);
|
||||
testOk1(result==false);
|
||||
testOk1(nSet==0);
|
||||
|
||||
result = pvCopy2->updateCopySetBitSet(pvStructureCopy2,bitSet2);
|
||||
nSet = bitSet2->cardinality();
|
||||
debugOutput("client 2: update 2", result, nSet, bitSet2, pvStructureCopy2);
|
||||
testOk1(result==true);
|
||||
testOk1(nSet==1);
|
||||
|
||||
// Update 3: only client 1 gets it
|
||||
pvValue->put(3);
|
||||
|
||||
result = pvCopy1->updateCopySetBitSet(pvStructureCopy1,bitSet1);
|
||||
nSet = bitSet1->cardinality();
|
||||
debugOutput("client 1: update 3", result, nSet, bitSet1, pvStructureCopy1);
|
||||
testOk1(result==true);
|
||||
testOk1(nSet==1);
|
||||
|
||||
result = pvCopy2->updateCopySetBitSet(pvStructureCopy2,bitSet2);
|
||||
nSet = bitSet2->cardinality();
|
||||
debugOutput("client 2: update 3", result, nSet, bitSet2, pvStructureCopy2);
|
||||
testOk1(result==false);
|
||||
testOk1(nSet==0);
|
||||
|
||||
// Update 4: only client 2 gets it
|
||||
pvValue->put(4);
|
||||
|
||||
result = pvCopy1->updateCopySetBitSet(pvStructureCopy1,bitSet1);
|
||||
nSet = bitSet1->cardinality();
|
||||
debugOutput("client 1: update 4", result, nSet, bitSet1, pvStructureCopy1);
|
||||
testOk1(result==false);
|
||||
testOk1(nSet==0);
|
||||
|
||||
result = pvCopy2->updateCopySetBitSet(pvStructureCopy2,bitSet2);
|
||||
nSet = bitSet2->cardinality();
|
||||
debugOutput("client 2: update 4", result, nSet, bitSet2, pvStructureCopy2);
|
||||
testOk1(result==true);
|
||||
testOk1(nSet==1);
|
||||
}
|
||||
|
||||
MAIN(testPlugin)
|
||||
{
|
||||
testPlan(26);
|
||||
testPlan(46);
|
||||
PVDatabasePtr pvDatabase(PVDatabase::getMaster());
|
||||
deadbandTest();
|
||||
arrayTest();
|
||||
unionArrayTest();
|
||||
timeStampTest();
|
||||
ignoreTest();
|
||||
dataDistributorTest();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user