114 Commits
4.5.0 ... 4.7.2

Author SHA1 Message Date
Andrew Johnson
8cf550ff57 Final commit for 4.7.2 2025-02-21 15:29:09 -06:00
Andrew Johnson
c070a3485b Merge pull request #82 from sveseli/changed-field-fix
Changed Field Fix
2024-07-09 10:04:14 -05:00
Sinisa Veseli
b31d5079bf updated release notes 2024-07-03 21:55:48 -05:00
Sinisa Veseli
7c78b15a5c added tests for changed set 2024-05-06 10:30:45 -05:00
Sinisa Veseli
cd7d8735af update master field tests 2024-04-30 13:09:59 -05:00
Sinisa Veseli
94b48e4893 do not proceed with pvcopy in master field callback unless master field was requested 2024-04-30 13:09:33 -05:00
Sinisa Veseli
9d94e95521 make sure only one record field has pointer to the master field; fix code indents to 4 spaces 2024-04-30 13:08:06 -05:00
Sinisa Veseli
e0d5925af3 add interface to check if master field was requested 2024-04-30 13:04:08 -05:00
Andrew Johnson
f207e512d6 Updates to the GHA build settings 2023-12-28 20:45:21 +00:00
Andrew Johnson
0a7d5b16aa Configure CI using ci-scripts for GHA and Appveyor 2023-12-28 14:05:15 +00:00
Andrew Johnson
aea8d9105e Delete now-unused CI configuration files 2023-12-27 22:02:28 +00:00
Andrew Johnson
d18e0c913a Set next development version 2023-12-13 17:12:22 -06:00
Andrew Johnson
e4cb8d2397 Set version numbers for release 2023-12-13 17:09:58 -06:00
Andrew Johnson
71b2a4bfe1 Update Release notes 2023-12-13 17:07:59 -06:00
Andrew Johnson
80703b6de1 Merge pull request #80 from sveseli/master
Static Build Fixes
2023-11-17 15:02:06 -06:00
Sinisa Veseli
203ee1b450 fix windows warnings 2023-11-17 14:23:10 -06:00
Sinisa Veseli
f43d50768e fix windows warnings 2023-11-17 14:05:30 -06:00
Sinisa Veseli
8ec8e47924 fix static build 2023-11-17 12:58:58 -06:00
Sinisa Veseli
d374669f04 include cctype, fixes Windows builds 2023-06-28 16:43:22 -05:00
Andrew Johnson
2f53d13021 Merge pull request #79 from sveseli/master
Data Distributor Plugin
2023-06-28 14:10:21 -05:00
Sinisa Veseli
00a8459944 address ANL feedback 2023-06-28 13:11:56 -05:00
Sinisa Veseli
91d0d2c315 added data distributor plugin documentation and updated release notes 2023-05-26 13:35:06 -05:00
Sinisa Veseli
c41f7cb3dc added data distributor plugin tests 2023-05-26 13:34:43 -05:00
Sinisa Veseli
dbf83dd017 added data distributor plugin 2023-05-26 13:34:34 -05:00
Andrew Johnson
1b787c5149 Set next development version 2022-09-07 12:14:21 -05:00
Andrew Johnson
ee3027f641 Update release notes and version numbers for release 2022-09-07 12:10:55 -05:00
Andrew Johnson
6cd0a1bdb0 Merge pull request #76 from sveseli/master
Resolve monitor overrun issue from PR #75.
2022-06-24 10:29:44 -05:00
Sinisa Veseli
0d85561481 this resolves issue with monitor overrun counter; master field listeners will be called only once, before calling listeners for the first subfield 2022-03-14 09:17:33 -05:00
Andrew Johnson
05d54c61e1 Merge pull request #75 from sveseli/master
Support requests to apply a pvCopy filter plugin to a whole PVRecord structure by using a field name `_` in the pvRequest.
2021-12-06 11:28:22 -06:00
Sinisa Veseli
182eee57e2 add tests for whole structure request 2021-12-01 14:36:50 -06:00
Sinisa Veseli
8daf322025 update module release notes 2021-12-01 14:36:20 -06:00
Sinisa Veseli
a68ef61a10 use _ instead of masterField to designate top level structure in the request 2021-11-30 10:32:38 -06:00
Sinisa Veseli
9dfebf1897 allow writing pvCopy plugins for the master field 2021-11-30 08:54:57 -06:00
Andrew Johnson
0cf706511e Set next development version 2021-07-02 15:52:05 -05:00
Andrew Johnson
8cac3975cc Update version numbers for release 2021-07-02 15:49:24 -05:00
Marty Kraimer
219e5eaf94 Merge pull request #73 from epics-base/pvdbcr_changes
more work on special; all pvdbcr* redone
2021-04-14 11:20:46 -04:00
mrkraimer
f345651b67 more work on special; all pvdbcr* redone 2021-04-14 10:26:30 -04:00
Marty Kraimer
4895107854 Merge pull request #72 from epics-base/pvdbcr_changes
changes were made to all the special code, i.e. all pvdbcr*.
2021-04-12 12:19:35 -04:00
mrkraimer
1c121b3f1e changes were made to all the special code, i.e. all pvdbcr*.
All now have a .h file in src/pv.
Each can now be used via an iocshell command or by a PVDatabase application that not part of an ioc database.
2021-04-12 09:30:50 -04:00
Marty Kraimer
d493e52dda Merge pull request #71 from epics-base/jenkins_problem
another attempt to fix jenkins failure
2021-04-08 07:03:56 -04:00
mrkraimer
91d4d2a423 another attempt to fix jenkins failure 2021-04-08 06:11:52 -04:00
Marty Kraimer
d54031a2fb Merge pull request #70 from epics-base/jenkins_problem
another attempt to fix jenkins failure
2021-04-07 11:55:18 -04:00
mrkraimer
1e1d5455a8 another attempt to fix jenkins failure 2021-04-07 11:08:16 -04:00
Marty Kraimer
426af714a1 Merge pull request #69 from epics-base/jenkins_problem
another atrempt to fix jenkins failure
2021-04-07 08:45:46 -04:00
mrkraimer
68bbb55370 another atrempt to fix jenkins failure 2021-04-07 07:57:56 -04:00
Marty Kraimer
716ec9857c Merge pull request #68 from epics-base/jenkins_problem
another attempt to fix jenkins_problem
2021-04-03 08:59:19 -04:00
mrkraimer
e91b4b1b66 another attempt to fix jenkins_problem 2021-04-03 07:14:31 -04:00
mrkraimer
2c4e98db8c another attempt to fix jenkins_problem 2021-04-03 06:30:09 -04:00
Marty Kraimer
d4a13397d7 Merge pull request #67 from epics-base/jenkins_problem
another attempt to fix jenkins_problem
2021-04-03 05:52:10 -04:00
mrkraimer
a622f66a94 another attempt to fix jenkins_problem 2021-04-02 18:09:38 -04:00
mrkraimer
81abd5f413 another attempt to fix jenkins_problem 2021-04-02 17:00:51 -04:00
Marty Kraimer
4df01038c3 Merge pull request #66 from epics-base/jenkins_problem
another attempt to fix jenkins_problem
2021-04-02 15:31:12 -04:00
mrkraimer
e648cba659 another attempt to fix jenkins_problem 2021-04-02 13:36:32 -04:00
Marty Kraimer
42747b167c Merge pull request #65 from epics-base/jenkins_problem
another attempt to fix jenkins_problem
2021-04-02 11:20:11 -04:00
mrkraimer
0a893a331b attempt to fix jenkins_problem 2021-04-02 10:06:38 -04:00
Marty Kraimer
e95c959cfd Merge pull request #64 from epics-base/jenkins_problem
attempt to fix jenkins_problem
2021-04-02 08:17:07 -04:00
mrkraimer
e51c29d147 attempt to fix jenkins_problem 2021-04-02 07:00:59 -04:00
Marty Kraimer
fec903fb06 Merge pull request #63 from epics-base/accesss-security
Accesss security
2021-04-01 12:15:38 -04:00
mrkraimer
ca066bc088 addRecord,removeRecord,processRecord.traceRecord replaced by pvdbcr versions 2021-04-01 10:20:18 -04:00
mrkraimer
0481a7bf5b redo addRecord, removeRecord, processRecord, and traceRecord 2021-04-01 08:22:34 -04:00
mrkraimer
17f43b4452 another attempt to get pull request to build 2021-04-01 07:15:26 -04:00
mrkraimer
a1514e5206 another attempt to get pull request to build 2021-04-01 06:15:46 -04:00
mrkraimer
72c2489921 another attempt to get pull request to build 2021-03-31 14:31:06 -04:00
mrkraimer
895698c3cd another attempt to get pull request to build 2021-03-31 13:05:51 -04:00
mrkraimer
9d10b039fb another attempt to get pull request to build 2021-03-31 12:25:44 -04:00
mrkraimer
bf82de407e still more work on includes 2021-03-31 10:57:48 -04:00
mrkraimer
58a603556b add using std::tr1::static_pointer_cast 2021-03-31 08:32:34 -04:00
mrkraimer
22e849c6f7 remove all references to pvaClient 2021-03-31 07:39:56 -04:00
mrkraimer
2a4d3a15f8 try adding pvaClient to src/Makefile 2021-03-31 05:58:46 -04:00
mrkraimer
7143a8585f more changes to includes 2021-03-30 18:29:11 -04:00
mrkraimer
2d61b4c0f3 fix include order 2021-03-30 16:04:50 -04:00
mrkraimer
120fa22558 remove nt 2021-03-30 12:58:01 -04:00
mrkraimer
4ed8e6d625 change order of includes 2021-03-30 10:48:22 -04:00
mrkraimer
4718021e39 update documentation 2021-03-30 09:50:23 -04:00
mrkraimer
b1822f5fbd 1) added canRead for access security
2) main documentation moved to https://mrkraimer.github.io/website/developerGuide/pvDatabase/pvDatabaseCPP.html
2021-03-24 14:35:05 -04:00
mrkraimer
333cd44da0 The following were made to all special pvdbcr modules
1) Much cleaner implementation.
2) pvdbSpecialRegister.dbd is new
2021-03-20 10:48:08 -04:00
Andrew Johnson
a514ab7b51 Add missing removal instructions for destroy() 2021-03-15 12:12:15 -05:00
mrkraimer
dafc37b585 The following changes were made:
1) PVRecord now has two new methods: setAsLevel and setAsGroup.
2) The following special records are DEPRECATED: addRecord,processRecord,removeRecord, and traceRecord.
    They are replaced by pvdbcrAddRecord,pvdbcrProcessRecord,pvdbcrRemoveRecord, and pvdbcrTraceRecord.
3) A new convention is that all special records start with pvdbcr, which means pvDatabase Create Record.
4) pvdbcrAddRecord,pvdbcrProcessRecord,pvdbcrRemoveRecord, and pvdbcrTraceRecord are like what they replace.
   But they also allow the asLevel to be set when they are created.
5) pvdbcrScalar and pvdbcrScalarArray are new special records.
   They created records that have fields value, alarm, and timeStamp.
   value can have any of the allowed scalar types, i.e. boolean,byte,...,string.
2021-03-15 07:04:32 -04:00
Andrew Johnson
2432f85784 Fix for Windows DLL builds 2021-03-12 17:38:51 -06:00
mrkraimer
bd1054f247 Changes were to check for null pointer after calling lock on a weak pointer. 2021-03-06 14:14:39 -05:00
Marty Kraimer
bc70b5f449 Merge pull request #58 from sveseli/master
Enable Access Security
2021-03-05 13:11:30 -05:00
Andrew Johnson
93a259cbde Set next development version after tag 2021-02-28 17:01:22 -06:00
Andrew Johnson
09423edeab Release version 4.5.3 for EPICS 7.0.5 2021-02-28 16:57:31 -06:00
Sinisa Veseli
ac1de6770e enable access security for PV Database 2021-02-28 16:31:36 -06:00
Sinisa Veseli
5427311390 initialize PVRecord with access security group/level 2021-02-28 16:28:54 -06:00
Marty Kraimer
b62b047f63 Merge pull request #57 from epics-base/removedestroy
remove all destroy methods
2020-07-23 07:58:30 -04:00
mrkraimer
3bc89bfe0e remove all destroy methods 2020-07-21 10:33:50 -04:00
Andrew Johnson
cb5d9f976a Set next development version 2020-05-28 16:26:01 -05:00
Andrew Johnson
3f5bfd067f Update version numbers for release 2020-05-28 16:14:30 -05:00
mrkraimer
85165e6579 get ready for next epics7 release 2020-05-20 14:03:13 -04:00
Marty Kraimer
476a8f1e32 Merge pull request #56 from dirk-zimoch/CleanupWhitespace
Cleanup whitespace
2020-05-20 08:47:32 -04:00
31e883dbbc removed empty lines at end of file 2020-04-15 18:00:08 +02:00
57cbf66833 removed spaces at end of line 2020-04-15 17:58:14 +02:00
7f31332a80 replaced tabs with spaces 2020-04-15 17:54:47 +02:00
Marty Kraimer
12015309d8 Merge pull request #55 from epics-base/arrayfilterforunion
Arrayfilterforunion
2020-04-13 06:27:41 -04:00
mrkraimer
1e62844a22 testPlugin found bug: added unionArrayTest 2020-04-12 14:01:22 -04:00
mrkraimer
ad479309b0 fix bitset bug 2020-04-08 11:06:59 -04:00
mrkraimer
2f7c82757f pvArrayPlugin now supports union scalarArray 2020-04-07 12:53:01 -04:00
Marty Kraimer
634153a28d Merge pull request #54 from epics-base/issue53
Issue53
2020-02-18 05:39:58 -05:00
Heesterman, Peter J
e664037063 static analysis during the Codathon at Diamond. 2020-02-17 12:06:08 +00:00
mrkraimer
75c16bd423 pvDatabase::removeRecord and pvRecord::remove no longer call eachother directly 2020-02-12 09:13:19 -05:00
mrkraimer
083dffac3c pvDatabase::removeRecord and pvRecord::remove changes;descructors now have at most a print statement 2020-02-10 06:15:59 -05:00
mrkraimer
42ba054e5f add createdestroy example 2020-02-07 10:58:55 -05:00
mrkraimer
3173e9aeae problems with examples 2020-02-07 09:17:08 -05:00
mrkraimer
785d654129 first attempt to fix issue 53; add example 2020-02-07 08:54:18 -05:00
Ralph Lange
22ce4440b7 Fix bug preventing "whole structure" bitset(0) copy
(fixes #52)
(this should have a test added, but I am not familiar enough with the code)
2020-01-28 15:44:27 +01:00
Andrew Johnson
80baccfd9c Incr version and set development flag after release 2019-11-01 12:52:23 -05:00
Andrew Johnson
0c92f07749 Clear development flag for 4.5.1 release 2019-11-01 12:50:11 -05:00
Andrew Johnson
73a9f1f84f Release notes for 4.5.1 2019-11-01 12:48:58 -05:00
Marty Kraimer
3c3f0ab7f1 Merge pull request #51 from mrkraimer/master
addRecord is new
2019-09-13 10:00:22 -04:00
Marty Kraimer
803098922a Update .travis.yml 2019-09-13 09:04:54 -04:00
mrkraimer
c028af8b6d addRecord is new 2019-09-11 10:17:09 -04:00
Ralph Lange
d33d03189e rtd-ci: add read-the-docs integration 2019-09-06 14:19:00 +02:00
Andrew Johnson
d7bd5628d4 Update version number to 4.5.1 DEVELOPMENT 2019-08-13 11:04:36 -05:00
93 changed files with 3818 additions and 1545 deletions

93
.appveyor.yml Normal file
View 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

Submodule .ci added at 130e88b709

14
.ci-local/defaults.set Normal file
View 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

View File

@@ -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

View File

@@ -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
View 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
View File

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

17
.readthedocs.yml Normal file
View File

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

View File

@@ -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
- BRBASE=3.14

View File

@@ -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 =
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
@@ -765,7 +765,7 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = include
INPUT = src
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -1035,7 +1035,7 @@ GENERATE_HTML = YES
# The default directory is: html.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_OUTPUT = documentation/html
HTML_OUTPUT = html/doxygen
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
# generated HTML page (for example: .htm, .php, .asp).

View File

@@ -1,3 +1,12 @@
# Version number for the PV Database API and shared library
EPICS_PVDATABASE_MAJOR_VERSION = 4
EPICS_PVDATABASE_MINOR_VERSION = 5
EPICS_PVDATABASE_MAINTENANCE_VERSION = 0
EPICS_PVDATABASE_MINOR_VERSION = 7
EPICS_PVDATABASE_MAINTENANCE_VERSION = 2
# Development flag, set to zero for release versions
EPICS_PVDATABASE_DEVELOPMENT_FLAG = 0
# Immediately after a release the MAINTENANCE_VERSION
# will be incremented and the DEVELOPMENT_FLAG set to 1

View File

@@ -2,10 +2,77 @@
This document summarizes the changes to the module between releases.
## 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
## Release 4.5.1 (EPICS 7.0.3.1, Nov 2019)
* addRecord is new.
* Doxygen updates and read-the-docs integration.
## Release 4.5.0 (EPICS 7.0.3, Jul 2019)
1) support is a new feature.
2) processRecord is new
* support is a new feature.
* processRecord is new.
## Release 4.4.2 (EPICS 7.0.2.2, Apr 2019)

View File

@@ -1,8 +0,0 @@
TODO
===========
create more regression tests
----------------
Currently only some simple tests exist. Most of the testing has been via the examples

View File

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

80
documentation/conf.py Normal file
View File

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

View 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

17
documentation/index.rst Normal file
View File

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

View File

@@ -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.06.19.
<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,200 +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>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, 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>

25
example/createdestroy/Makefile Executable file
View File

@@ -0,0 +1,25 @@
TOP=../..
include $(TOP)/configure/CONFIG
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
#=============================
#=============================
# Build the application
TESTPROD_HOST = createdestroy
createdestroy_SRCS += createdestroy.cpp
# Add all the support libraries needed by this application
#pvatest_LIBS += xxx
# Finally link to the EPICS Base libraries
createdestroy_LIBS += pvDatabase pvAccess pvData
createdestroy_LIBS += $(EPICS_BASE_IOC_LIBS)
#===========================
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

View File

@@ -0,0 +1,20 @@
# pvDatabaseCPP/example/createdestroy
This is an example that:
1) Gets the master PVDatabase
2) Create ChannelProviderLocal
3) Creates a ServerContext
Then it executes a forever loop that:
1) creates a pvRecord and adds it to the pvDatabase.
2) creates a pvac::ClientProvider
3) creates a pvac::ClientChannel
4) creates a monitor on the channel
5) runs a loop 10 times that: does a put to the channel, and then gets the data for any outstanding monitors
6) removes the pvRecord from the pvDatabase
It also has options to set trace level for the pvRecord and to periodically pause by asking for input.

View File

@@ -0,0 +1,179 @@
/******************************************************************************
* This is modeled after a test program created by Bertrand Bauvir from the ITER Organization
******************************************************************************/
#include <iostream>
#include <epicsGetopt.h>
#include <pv/pvData.h>
#include <pv/pvDatabase.h>
#include <pv/serverContext.h>
#include <pv/channelProviderLocal.h>
#include <pva/client.h>
#include <epicsEvent.h>
// Local header files
// Constants
#define DEFAULT_RECORD_NAME "examplechannel"
using std::tr1::static_pointer_cast;
class Record : public ::epics::pvDatabase::PVRecord
{
public:
std::shared_ptr<::epics::pvData::PVStructure> __pv;
static std::shared_ptr<Record> create (std::string const & name, std::shared_ptr<::epics::pvData::PVStructure> const & pvstruct);
Record (std::string const & name, std::shared_ptr<epics::pvData::PVStructure> const & pvstruct)
: epics::pvDatabase::PVRecord(name, pvstruct) { __pv = pvstruct; };
virtual void process (void);
};
std::shared_ptr<Record> Record::create (std::string const & name, std::shared_ptr<::epics::pvData::PVStructure> const & pvstruct)
{
std::shared_ptr<Record> pvrecord (new Record (name, pvstruct));
// Need to be explicitly called .. not part of the base constructor
if(!pvrecord->init()) pvrecord.reset();
return pvrecord;
}
void Record::process (void)
{
PVRecord::process();
std::string name = this->getRecordName();
std::cout << this->getRecordName()
<< " process\n";
}
class MyMonitor
{
private:
std::tr1::shared_ptr<::pvac::MonitorSync> monitor;
MyMonitor(std::tr1::shared_ptr<::pvac::ClientChannel> const &channel)
{
monitor = std::tr1::shared_ptr<::pvac::MonitorSync>(new ::pvac::MonitorSync(channel->monitor()));
}
public:
static std::tr1::shared_ptr<MyMonitor> create(std::tr1::shared_ptr<::pvac::ClientChannel> const &channel)
{
return std::tr1::shared_ptr<MyMonitor>(new MyMonitor(channel));
}
void getData();
};
void MyMonitor::getData()
{
while (true) {
if(!monitor->wait(.001)) break;
switch(monitor->event.event) {
case pvac::MonitorEvent::Fail:
std::cerr<<monitor->name()<<" : Error : "<<monitor->event.message<<"\n";
return;
case pvac::MonitorEvent::Cancel:
std::cout<<monitor->name()<<" <Cancel>\n";
return;
case pvac::MonitorEvent::Disconnect:
std::cout<<monitor->name()<<" <Disconnect>\n";
return;
case pvac::MonitorEvent::Data:
while(monitor->poll()) {
std::cout<<monitor->name()<<" : "<<monitor->root;
}
if(monitor->complete()) {
return;
}
}
}
}
int main (int argc, char** argv)
{
int verbose = 0;
unsigned loopctr = 0;
unsigned pausectr = 0;
bool allowExit = false;
bool callRecord = false;
bool callDatabase = false;
int opt;
while((opt = getopt(argc, argv, "v:ardh")) != -1) {
switch(opt) {
case 'v':
verbose = std::stoi(optarg);
break;
case 'a' :
allowExit = true;
break;
case 'r' :
callRecord = true;
break;
case 'd' :
callDatabase = true;
break;
case 'h':
std::cout << " -v level -a -r -d -h \n";
std::cout << "-r call pvRecord->remove -d call master->removeRecord\n";
std::cout << "default\n";
std::cout << "-v " << verbose
<< " -a false"
<< " -d"
<< "\n";
return 0;
default:
std::cerr<<"Unknown argument: "<<opt<<"\n";
return -1;
}
}
if(!callRecord && !callDatabase) callDatabase = true;
::epics::pvDatabase::PVDatabasePtr master = epics::pvDatabase::PVDatabase::getMaster();
::epics::pvDatabase::ChannelProviderLocalPtr channelProvider = epics::pvDatabase::getChannelProviderLocal();
epics::pvAccess::ServerContext::shared_pointer context
= epics::pvAccess::startPVAServer(epics::pvAccess::PVACCESS_ALL_PROVIDERS, 0, true, true);
std::string startset("starting set of puts valuectr = ");
while (true) {
loopctr++;
std::string name = DEFAULT_RECORD_NAME + std::to_string(loopctr);
// Create record
// Create record structure
::epics::pvData::FieldBuilderPtr builder = epics::pvData::getFieldCreate()->createFieldBuilder();
builder->add("value", ::epics::pvData::pvULong);
std::shared_ptr<::epics::pvData::PVStructure> pvstruct
= ::epics::pvData::getPVDataCreate()->createPVStructure(builder->createStructure());
std::shared_ptr<Record> pvrecord = Record::create(std::string(name), pvstruct);
master->addRecord(pvrecord);
pvrecord->setTraceLevel(verbose);
// Start PVA (local) client
std::tr1::shared_ptr<::pvac::ClientProvider> provider
= std::tr1::shared_ptr<::pvac::ClientProvider>(new ::pvac::ClientProvider ("pva"));
std::tr1::shared_ptr<::pvac::ClientChannel> channel
= std::tr1::shared_ptr<::pvac::ClientChannel>(new ::pvac::ClientChannel (provider->connect(name)));
std::tr1::shared_ptr<MyMonitor> mymonitor = MyMonitor::create(channel);
unsigned valuectr = loopctr;
std::cout << startset << loopctr << "\n";
for (int ind=0; ind<100; ind++) {
channel->put().set("value",valuectr++).exec();
mymonitor->getData();
}
pausectr++;
if(allowExit && pausectr>10) {
pausectr = 0;
std::cout << "Type exit to stop: \n";
int c = std::cin.peek(); // peek character
if ( c == EOF ) continue;
std::string str;
std::getline(std::cin,str);
if(str.compare("exit")==0) break;
}
if(callRecord) {
std::cout << "callRecord\n";
pvrecord->remove();
}
if(callDatabase) {
std::cout << "callDatabase\n";
master->removeRecord(pvrecord);
}
}
return (0);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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,22 +16,32 @@ INC += pv/pvDatabase.h
INC += pv/channelProviderLocal.h
INC += pv/traceRecord.h
INC += pv/removeRecord.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

View File

@@ -8,5 +8,6 @@ LIBSRCS += pvCopy.cpp
LIBSRCS += pvArrayPlugin.cpp
LIBSRCS += pvDeadbandPlugin.cpp
LIBSRCS += pvTimestampPlugin.cpp
LIBSRCS += dataDistributorPlugin.cpp

View 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;
}
}}

View File

@@ -76,9 +76,19 @@ static vector<string> split(string const & colonSeparatedList) {
PVArrayFilterPtr PVArrayFilter::create(
const std::string & requestValue,
const PVFieldPtr & master)
const PVFieldPtr & masterField)
{
Type type = master->getField()->getType();
bool masterIsUnion = false;
PVUnionPtr pvUnion;
Type type = masterField->getField()->getType();
if(type==epics::pvData::union_) {
pvUnion = std::tr1::static_pointer_cast<PVUnion>(masterField);
PVFieldPtr pvField = pvUnion->get();
if(pvField) {
masterIsUnion = true;
type = pvField->getField()->getType();
}
}
if(type!=scalarArray) {
PVArrayFilterPtr filter = PVArrayFilterPtr();
return filter;
@@ -112,60 +122,82 @@ PVArrayFilterPtr PVArrayFilter::create(
PVArrayFilterPtr filter = PVArrayFilterPtr();
return filter;
}
PVScalarArrayPtr masterArray;
if(masterIsUnion) {
masterArray = static_pointer_cast<PVScalarArray>(pvUnion->get());
} else {
masterArray = static_pointer_cast<PVScalarArray>(masterField);
}
PVArrayFilterPtr filter =
PVArrayFilterPtr(
new PVArrayFilter(
start,increment,end,static_pointer_cast<PVScalarArray>(master)));
new PVArrayFilter(start,increment,end,masterField,masterArray));
return filter;
}
PVArrayFilter::PVArrayFilter(long start,long increment,long end,const PVScalarArrayPtr & masterArray)
PVArrayFilter::PVArrayFilter(
long start,long increment,long end,
const PVFieldPtr & masterField,
const epics::pvData::PVScalarArrayPtr masterArray)
: start(start),
increment(increment),
end(end),
masterField(masterField),
masterArray(masterArray)
{
}
bool PVArrayFilter::filter(const PVFieldPtr & pvCopy,const BitSetPtr & bitSet,bool toCopy)
bool PVArrayFilter::filter(const PVFieldPtr & pvField,const BitSetPtr & bitSet,bool toCopy)
{
PVScalarArrayPtr copyArray = static_pointer_cast<PVScalarArray>(pvCopy);
PVFieldPtr pvCopy = pvField;
PVScalarArrayPtr copyArray;
bool isUnion = false;
Type type = masterField->getField()->getType();
if(type==epics::pvData::union_) {
isUnion = true;
PVUnionPtr pvMasterUnion = std::tr1::static_pointer_cast<PVUnion>(masterField);
PVUnionPtr pvCopyUnion = std::tr1::static_pointer_cast<PVUnion>(pvCopy);
if(toCopy) pvCopyUnion->copy(*pvMasterUnion);
PVFieldPtr pvField = pvCopyUnion->get();
copyArray = static_pointer_cast<PVScalarArray>(pvField);
} else {
copyArray = static_pointer_cast<PVScalarArray>(pvCopy);
}
long len = 0;
long start = this->start;
long end = this->end;
long no_elements = masterArray->getLength();
if(start<0) {
start = no_elements+start;
if(start<0) start = 0;
start = no_elements+start;
if(start<0) start = 0;
}
if (end < 0) {
end = no_elements + end;
if (end < 0) end = 0;
end = no_elements + end;
if (end < 0) end = 0;
}
if(toCopy) {
if (end >= no_elements) end = no_elements - 1;
if (end - start >= 0) len = 1 + (end - start) / increment;
if(len<=0 || start>=no_elements) {
copyArray->setLength(0);
return true;
}
long indfrom = start;
long indto = 0;
copyArray->setCapacity(len);
if(increment==1) {
if(toCopy) {
if (end >= no_elements) end = no_elements - 1;
if (end - start >= 0) len = 1 + (end - start) / increment;
if(len<=0 || start>=no_elements) {
copyArray->setLength(0);
return true;
}
long indfrom = start;
long indto = 0;
copyArray->setCapacity(len);
if(increment==1) {
copy(*masterArray,indfrom,1,*copyArray,indto,1,len);
} else {
for(long i=0; i<len; ++i) {
copy(*masterArray,indfrom,1,*copyArray,indto,1,1);
indfrom += increment;
indto += 1;
}
}
copyArray->setLength(len);
bitSet->set(pvCopy->getFieldOffset());
return true;
} else {
for(long i=0; i<len; ++i) {
copy(*masterArray,indfrom,1,*copyArray,indto,1,1);
indfrom += increment;
indto += 1;
}
}
copyArray->setLength(len);
bitSet->set(pvField->getFieldOffset());
return true;
}
if (end - start >= 0) len = 1 + (end - start) / increment;
if(len<=0) return true;
@@ -173,21 +205,21 @@ bool PVArrayFilter::filter(const PVFieldPtr & pvCopy,const BitSetPtr & bitSet,bo
long indfrom = 0;
long indto = start;
if(increment==1) {
copy(*copyArray,indfrom,1,*masterArray,indto,1,len);
copy(*copyArray,indfrom,1,*masterArray,indto,1,len);
} else {
for(long i=0; i<len; ++i) {
copy(*copyArray,indfrom,1,*masterArray,indto,1,1);
indfrom += 1;
indto += increment;
}
for(long i=0; i<len; ++i) {
copy(*copyArray,indfrom,1,*masterArray,indto,1,1);
indfrom += 1;
indto += increment;
}
}
if(isUnion) masterField->postPut();
return true;
}
string PVArrayFilter::getName()
{
return name;
return name;
}
}}

View File

@@ -29,7 +29,7 @@ using std::endl;
using std::vector;
using namespace epics::pvData;
namespace epics { namespace pvCopy {
namespace epics { namespace pvCopy {
/**
* Convenience method for implementing dump.
@@ -60,19 +60,19 @@ struct CopyNode {
PVStructurePtr options;
vector<PVFilterPtr> pvFilters;
};
static CopyNodePtr NULLCopyNode;
typedef std::vector<CopyNodePtr> CopyNodePtrArray;
typedef std::tr1::shared_ptr<CopyNodePtrArray> CopyNodePtrArrayPtr;
struct CopyStructureNode : public CopyNode {
CopyNodePtrArrayPtr nodes;
};
PVCopyPtr PVCopy::create(
PVStructurePtr const &pvMaster,
PVStructurePtr const &pvRequest,
PVStructurePtr const &pvMaster,
PVStructurePtr const &pvRequest,
string const & structureName)
{
PVStructurePtr pvStructure(pvRequest);
@@ -88,7 +88,6 @@ PVCopyPtr PVCopy::create(
bool result = pvCopy->init(pvStructure);
if(!result) return PVCopyPtr();
pvCopy->traverseMasterInitPlugin();
//cout << pvCopy->dump() << endl;
return pvCopy;
}
@@ -114,7 +113,7 @@ PVStructurePtr PVCopy::createPVStructure()
cacheInitStructure.reset();
return save;
}
PVStructurePtr pvStructure =
PVStructurePtr pvStructure =
getPVDataCreate()->createPVStructure(structure);
return pvStructure;
}
@@ -244,7 +243,7 @@ void PVCopy::updateMasterCheckBitSet(
bitSet->clear(nextSet);
PVStructurePtr pv = static_pointer_cast<PVStructure>(pvField);
PVFieldPtrArray pvFieldArray = pv->getPVFields();
for(size_t i=0; i>pvFieldArray.size(); ++i) {
for(size_t i=0; i<pvFieldArray.size(); ++i) {
PVFieldPtr pvField = pvFieldArray[i];
bitSet->set(pvField->getFieldOffset());
}
@@ -296,7 +295,7 @@ void PVCopy::updateMaster(
bitSet->clear(nextSet);
}
}
PVStructurePtr PVCopy::getOptions(std::size_t fieldOffset)
{
if(fieldOffset==0) return headNode->options;
@@ -414,7 +413,7 @@ void PVCopy::updateCopyFromBitSet(
size_t offset = structureNode->structureOffset;
size_t nextSet = bitSet->nextSetBit(offset);
if(nextSet==string::npos) return;
if(offset>=pvCopy->getNextFieldOffset()) return;
if(offset>=pvCopy->getNextFieldOffset()) return;
PVStructurePtr pvCopyStructure = static_pointer_cast<PVStructure>(pvCopy);
PVFieldPtrArray const & pvCopyFields = pvCopyStructure->getPVFields();
for(size_t i=0; i<pvCopyFields.size(); ++i) {
@@ -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();
@@ -529,9 +536,9 @@ CopyNodePtr PVCopy::createStructureNodes(
for(size_t i=0; i<number; i++) {
PVFieldPtr copyPVField = copyPVFields[i];
string fieldName = copyPVField->getFieldName();
PVStructurePtr requestPVStructure =
PVStructurePtr requestPVStructure =
pvFromRequest->getSubField<PVStructure>(fieldName);
PVStructurePtr pvSubFieldOptions =
PVStructurePtr pvSubFieldOptions =
requestPVStructure->getSubField<PVStructure>("_options");
PVFieldPtr pvMasterField = pvMasterStructure->getSubField(fieldName);
if(!pvMasterField) {
@@ -641,14 +648,14 @@ void PVCopy::initPlugin(
void PVCopy::traverseMasterInitPlugin()
{
traverseMasterInitPlugin(headNode);
traverseMasterInitPlugin(headNode);
}
void PVCopy::traverseMasterInitPlugin(CopyNodePtr const & node)
{
PVFieldPtr pvField = node->masterPVField;
PVStructurePtr pvOptions = node->options;
if(pvOptions) initPlugin(node,pvOptions,pvField);
if(pvOptions) initPlugin(node,pvOptions,pvField);
if(!node->isStructure) return;
CopyStructureNodePtr structureNode = static_pointer_cast<CopyStructureNode>(node);
CopyNodePtrArrayPtr nodes = structureNode->nodes;
@@ -667,7 +674,7 @@ CopyNodePtr PVCopy::getCopyOffset(
CopyNodePtr node = (*nodes)[i];
if(!node->isStructure) {
size_t off = node->masterPVField->getFieldOffset();
size_t nextOffset = node->masterPVField->getNextFieldOffset();
size_t nextOffset = node->masterPVField->getNextFieldOffset();
if(offset>= off && offset<nextOffset) return node;
} else {
CopyStructureNodePtr subNode =
@@ -710,7 +717,7 @@ void PVCopy::setIgnore(CopyNodePtr const &node) {
CopyNodePtrArrayPtr nodes = structureNode->nodes;
for(size_t i=0; i<nodes->size(); ++i) {
CopyNodePtr node = (*nodes)[i];
setIgnore(node); }
setIgnore(node); }
} else {
size_t num = node->masterPVField->getNumberFields();
if(num>1) {

View File

@@ -82,7 +82,7 @@ PVDeadbandFilterPtr PVDeadbandFilter::create(
PVDeadbandFilterPtr filter =
PVDeadbandFilterPtr(
new PVDeadbandFilter(
absolute,deadband,static_pointer_cast<PVScalar>(master)));
absolute,deadband,static_pointer_cast<PVScalar>(master)));
return filter;
}
@@ -91,7 +91,7 @@ PVDeadbandFilter::PVDeadbandFilter(bool absolute,double deadband,PVScalarPtr con
deadband(deadband),
master(master),
firstTime(true),
lastReportedValue(0.0)
lastReportedValue(0.0)
{
}
@@ -128,8 +128,7 @@ bool PVDeadbandFilter::filter(const PVFieldPtr & pvCopy,const BitSetPtr & bitSet
string PVDeadbandFilter::getName()
{
return name;
return name;
}
}}

View File

@@ -14,7 +14,7 @@
using namespace epics::pvData;
namespace epics { namespace pvCopy{
namespace epics { namespace pvCopy{
typedef std::map<std::string,PVPluginPtr> PVPluginMap;
@@ -38,4 +38,3 @@ PVPluginPtr PVPluginRegistry::find(const std::string & name)
}
}}

View File

@@ -89,7 +89,7 @@ PVTimestampFilter::PVTimestampFilter(bool current,bool copy,PVFieldPtr const & m
bool PVTimestampFilter::filter(const PVFieldPtr & pvCopy,const BitSetPtr & bitSet,bool toCopy)
{
if(current) {
if(current) {
timeStamp.getCurrent();
if(toCopy) {
if(!pvTimeStamp.attach(pvCopy)) return false;
@@ -100,7 +100,7 @@ bool PVTimestampFilter::filter(const PVFieldPtr & pvCopy,const BitSetPtr & bitSe
bitSet->set(pvCopy->getFieldOffset());
return true;
}
if(copy) {
if(copy) {
if(toCopy) {
if(!pvTimeStamp.attach(master)) return false;
pvTimeStamp.get(timeStamp);
@@ -120,8 +120,7 @@ bool PVTimestampFilter::filter(const PVFieldPtr & pvCopy,const BitSetPtr & bitSe
string PVTimestampFilter::getName()
{
return name;
return name;
}
}}

View File

@@ -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,7 +45,8 @@ PVDatabasePtr PVDatabase::getMaster()
PVArrayPlugin::create();
PVTimestampPlugin::create();
PVDeadbandPlugin::create();
}
DataDistributorPlugin::create();
}
return pvDatabaseMaster;
}
@@ -56,14 +58,6 @@ PVDatabase::PVDatabase()
PVDatabase::~PVDatabase()
{
if(DEBUG_LEVEL>0) cout << "PVDatabase::~PVDatabase()\n";
size_t len = recordMap.size();
shared_vector<string> names(len);
PVRecordMap::iterator iter;
size_t i = 0;
for(iter = recordMap.begin(); iter!=recordMap.end(); ++iter) {
names[i++] = (*iter).first;
}
for(size_t i=0; i<len; ++i) removeRecord(findRecord(names[i]));
}
void PVDatabase::lock() {
@@ -100,17 +94,28 @@ bool PVDatabase::addRecord(PVRecordPtr const & record)
return true;
}
bool PVDatabase::removeRecord(PVRecordPtr const & record)
PVRecordWPtr PVDatabase::removeFromMap(PVRecordPtr const & record)
{
if(record->getTraceLevel()>0) {
cout << "PVDatabase::removeRecord " << record->getRecordName() << endl;
}
epicsGuard<epics::pvData::Mutex> guard(mutex);
string recordName = record->getRecordName();
PVRecordMap::iterator iter = recordMap.find(recordName);
if(iter!=recordMap.end()) {
PVRecordPtr pvRecord = (*iter).second;
recordMap.erase(iter);
return pvRecord->shared_from_this();
}
return PVRecordWPtr();
}
bool PVDatabase::removeRecord(PVRecordPtr const & record)
{
if(record->getTraceLevel()>0) {
cout << "PVDatabase::removeRecord " << record->getRecordName() << endl;
}
epicsGuard<epics::pvData::Mutex> guard(mutex);
PVRecordWPtr pvRecord = removeFromMap(record);
if(pvRecord.use_count()!=0) {
pvRecord.lock()->unlistenClients();
return true;
}
return false;

View File

@@ -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,73 +51,29 @@ 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_)
{
}
void PVRecord::notifyClients()
{
{
epicsGuard<epics::pvData::Mutex> guard(mutex);
if(traceLevel>0) {
cout << "PVRecord::notifyClients() " << recordName
<< endl;
}
}
pvTimeStamp.detach();
for(std::list<PVListenerWPtr>::iterator iter = pvListenerList.begin();
iter!=pvListenerList.end();
iter++ )
{
PVListenerPtr listener = iter->lock();
if(!listener) continue;
if(traceLevel>0) {
cout << "PVRecord::notifyClients() calling listener->unlisten "
<< recordName << endl;
}
listener->unlisten(shared_from_this());
}
pvListenerList.clear();
for (std::list<PVRecordClientWPtr>::iterator iter = clientList.begin();
iter!=clientList.end();
iter++ )
{
PVRecordClientPtr client = iter->lock();
if(!client) continue;
if(traceLevel>0) {
cout << "PVRecord::notifyClients() calling client->detach "
<< recordName << endl;
}
client->detach(shared_from_this());
}
if(traceLevel>0) {
cout << "PVRecord::notifyClients() calling clientList.clear() "
<< recordName << endl;
}
clientList.clear();
if(traceLevel>0) {
cout << "PVRecord::notifyClients() returning " << recordName << endl;
}
}
PVRecord::~PVRecord()
{
if(traceLevel>0) {
cout << "~PVRecord() " << recordName << endl;
}
notifyClients();
}
void PVRecord::remove()
void PVRecord::unlistenClients()
{
PVDatabasePtr pvDatabase(PVDatabase::getMaster());
if(pvDatabase) pvDatabase->removeRecord(shared_from_this());
pvTimeStamp.detach();
epicsGuard<epics::pvData::Mutex> guard(mutex);
for(std::list<PVListenerWPtr>::iterator iter = pvListenerList.begin();
iter!=pvListenerList.end();
iter++ )
@@ -142,6 +100,19 @@ void PVRecord::remove()
clientList.clear();
}
void PVRecord::remove()
{
if(traceLevel>0) {
cout << "PVRecord::remove() " << recordName << endl;
}
unlistenClients();
epicsGuard<epics::pvData::Mutex> guard(mutex);
PVDatabasePtr pvDatabase(PVDatabase::getMaster());
if(pvDatabase) pvDatabase->removeFromMap(shared_from_this());
pvTimeStamp.detach();
}
void PVRecord::initPVRecord()
{
PVRecordStructurePtr parent;
@@ -283,7 +254,7 @@ void PVRecord::nextMasterPVField(PVFieldPtr const & pvField)
PVRecordFieldPtr pvRecordField = findPVRecordField(pvField);
PVListenerPtr listener = pvListener.lock();
if(!listener.get()) return;
if(isAddListener) {
if(isAddListener) {
pvRecordField->addListener(listener);
} else {
pvRecordField->removeListener(listener);
@@ -361,6 +332,7 @@ PVRecordField::PVRecordField(
PVRecordPtr const & pvRecord)
: pvField(pvField),
isStructure(pvField->getField()->getType()==structure ? true : false),
master(),
parent(parent),
pvRecord(pvRecord)
{
@@ -412,7 +384,7 @@ bool PVRecordField::addListener(PVListenerPtr const & pvListener)
void PVRecordField::removeListener(PVListenerPtr const & pvListener)
{
PVRecordPtr pvRecord(this->pvRecord.lock());
PVRecordPtr pvRecord(this->pvRecord.lock());
if(pvRecord && pvRecord->getTraceLevel()>1) {
cout << "PVRecordField::removeListener() " << getFullName() << endl;
}
@@ -447,14 +419,21 @@ 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 =
PVRecordStructurePtr pvrs =
static_pointer_cast<PVRecordStructure>(shared_from_this());
PVRecordFieldPtrArrayPtr pvRecordFields = pvrs->getPVRecordFields();
PVRecordFieldPtrArray::iterator iter;
@@ -494,19 +473,38 @@ void PVRecordStructure::init()
PVRecordStructurePtr self =
static_pointer_cast<PVRecordStructure>(shared_from_this());
PVRecordPtr pvRecord = getPVRecord();
for(size_t i=0; i<numFields; i++) {
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;
}
}
}
}
}

View File

@@ -27,8 +27,9 @@
#include <pv/pvDatabase.h>
#include <shareLib.h>
#include <asLib.h>
namespace epics { namespace pvDatabase {
namespace epics { namespace pvDatabase {
class ChannelProviderLocal;
typedef std::tr1::shared_ptr<ChannelProviderLocal> ChannelProviderLocalPtr;
@@ -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.
*
@@ -93,7 +102,7 @@ public:
virtual epics::pvAccess::ChannelFind::shared_pointer channelFind(
std::string const &channelName,
epics::pvAccess::ChannelFindRequester::shared_pointer const & channelFindRequester);
/**
/**
* @brief Calls method channelListRequester::channelListResult.
*
* This provides the caller with a list of the record names on the PVDatabase.
@@ -101,7 +110,7 @@ public:
* @param channelListRequester The client callback.
* @return shared pointer to ChannelFind.
* The interface for SyncChannelFind is defined by pvAccessCPP.
*/
*/
virtual epics::pvAccess::ChannelFind::shared_pointer channelList(
epics::pvAccess::ChannelListRequester::shared_pointer const & channelListRequester);
/**
@@ -163,6 +172,7 @@ private:
friend class ChannelProviderLocalRun;
};
/**
* @brief Channel for accessing a PVRecord.
*
@@ -186,28 +196,23 @@ public:
epics::pvAccess::ChannelRequester::shared_pointer const & requester,
PVRecordPtr const & pvRecord
);
/**
/**
* @brief Destructor
*/
virtual ~ChannelLocal();
/**
* @brief DEPRECATED
*
*/
virtual void destroy() {};
/**
/**
* @brief Detach from the record.
*
* This is called when a record is being removed from the database.
* @param pvRecord The record being removed.
*/
virtual void detach(PVRecordPtr const &pvRecord);
/**
/**
* @brief Get the requester name.
* @return returns the name of the channel requester.
*/
virtual std::string getRequesterName();
/**
/**
* @brief Passes the message to the channel requester.
* @param message The message.
* @param messageType The message type.
@@ -215,37 +220,37 @@ public:
virtual void message(
std::string const & message,
epics::pvData::MessageType messageType);
/**
/**
* @brief Get the channel provider
* @return The provider.
*/
virtual epics::pvAccess::ChannelProvider::shared_pointer getProvider();
/**
/**
* @brief Get the remote address
* @return <b>local</b>
*/
virtual std::string getRemoteAddress();
/**
/**
* Get the connection state.
* @return Channel::CONNECTED.
*/
virtual epics::pvAccess::Channel::ConnectionState getConnectionState();
/**
/**
* @brief Get the channel name.
* @return the record name.
*/
virtual std::string getChannelName();
/**
/**
* @brief Get the channel requester
* @return The channel requester.
*/
virtual epics::pvAccess::ChannelRequester::shared_pointer getChannelRequester();
/**
/**
* @brief Is the channel connected?
* @return true.
*/
virtual bool isConnected();
/**
/**
* @brief Get the introspection interface for subField.
*
* The introspection interface is given via GetFieldRequester::getDone.
@@ -257,14 +262,14 @@ public:
virtual void getField(
epics::pvAccess::GetFieldRequester::shared_pointer const &requester,
std::string const & subField);
/**
/**
* Get the access rights for the record.
* This throws an exception because it is assumed that access rights are
* handled by a higher level.
*/
virtual epics::pvAccess::AccessRights getAccessRights(
epics::pvData::PVField::shared_pointer const &pvField);
/**
/**
* @brief Create a channelProcess.
*
* @param requester The client callback.
@@ -275,9 +280,9 @@ public:
virtual epics::pvAccess::ChannelProcess::shared_pointer createChannelProcess(
epics::pvAccess::ChannelProcessRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
/**
/**
* @brief Create a channelGet.
*
*
* @param requester The client callback.
* @param pvRequest The options specified by the client.
* @return A shared pointer to the newly created implementation.
@@ -286,7 +291,7 @@ public:
virtual epics::pvAccess::ChannelGet::shared_pointer createChannelGet(
epics::pvAccess::ChannelGetRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
/**
/**
* @brief Create a channelPut.
*
* @param requester The client callback.
@@ -297,9 +302,9 @@ public:
virtual epics::pvAccess::ChannelPut::shared_pointer createChannelPut(
epics::pvAccess::ChannelPutRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
/**
/**
* @brief Create a channelPutGet.
*
*
* @param requester The client callback.
* @param pvRequest The options specified by the client.
* @return A shared pointer to the newly created implementation.
@@ -308,7 +313,7 @@ public:
virtual epics::pvAccess::ChannelPutGet::shared_pointer createChannelPutGet(
epics::pvAccess::ChannelPutGetRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
/**
/**
* @brief Create a channelRPC.
*
* The PVRecord must implement <b>getService</b> or an empty shared pointer is returned.
@@ -319,9 +324,9 @@ public:
virtual epics::pvAccess::ChannelRPC::shared_pointer createChannelRPC(
epics::pvAccess::ChannelRPCRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
/**
/**
* @brief Create a monitor.
*
*
* @param requester The client callback.
* @param pvRequest The options specified by the client.
* @return A shared pointer to the newly created implementation.
@@ -330,9 +335,9 @@ public:
virtual epics::pvData::Monitor::shared_pointer createMonitor(
epics::pvData::MonitorRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
/**
/**
* @brief Create a channelArray.
*
*
* @param requester The client callback.
* @param pvRequest The options specified by the client.
* @return A shared pointer to the newly created implementation.
@@ -341,16 +346,28 @@ public:
virtual epics::pvAccess::ChannelArray::shared_pointer createChannelArray(
epics::pvAccess::ChannelArrayRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
/**
/**
* @brief calls printInfo(std::cout);
*/
virtual void printInfo();
/**
/**
* @brief displays a message
*
*
* @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;
};
}}

View File

@@ -16,7 +16,7 @@
#include <shareLib.h>
namespace epics { namespace pvDatabase {
namespace epics { namespace pvDatabase {
class ControlSupport;
typedef std::tr1::shared_ptr<ControlSupport> ControlSupportPtr;
@@ -57,20 +57,20 @@ public:
* @return Returns true is any fields were modified; otherwise false.
*/
virtual void reset();
/**
/**
* @brief create a ControlSupport
*
* @param pvRecord - The pvRecord to which the support is attached.
* @return The new ControlSupport
*/
static ControlSupportPtr create(PVRecordPtr const & pvRecord);
/**
/**
* @brief create a controlSupport required by ControlSupport
*
* @param scalarType The type for outputValue.
* @return The controlField introspection structure.
*/
static epics::pvData::StructureConstPtr controlField(epics::pvData::ScalarType scalarType);
static epics::pvData::StructureConstPtr controlField(epics::pvData::ScalarType scalarType);
private:
ControlSupport(PVRecordPtr const & pvRecord);
PVRecordPtr pvRecord;
@@ -87,4 +87,3 @@ private:
}}
#endif /* CONTROLSUPPORT_H */

View 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

View File

@@ -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 */

View File

@@ -63,9 +63,13 @@ private:
long start;
long increment;
long end;
epics::pvData::PVFieldPtr masterField;
epics::pvData::PVScalarArrayPtr masterArray;
PVArrayFilter(long start,long increment,long end,const epics::pvData::PVScalarArrayPtr & masterArray);
PVArrayFilter(
long start,long increment,long end,
const epics::pvData::PVFieldPtr & masterField,
const epics::pvData::PVScalarArrayPtr masterArray);
public:
POINTER_DEFINITIONS(PVArrayFilter);
virtual ~PVArrayFilter();
@@ -95,4 +99,3 @@ public:
}}
#endif /* PVARRAYPLUGIN_H */

View File

@@ -16,7 +16,7 @@
#include <shareLib.h>
namespace epics { namespace pvDatabase {
namespace epics { namespace pvDatabase {
class PVRecord;
typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
@@ -59,6 +59,7 @@ class epicsShareClass PVRecord :
{
public:
POINTER_DEFINITIONS(PVRecord);
/**
* The Destructor.
*/
@@ -86,14 +87,10 @@ public:
* the base class sets the timeStamp to the current time.
*/
virtual void process();
/**
* @brief DEPRECATED
*/
virtual void destroy() {}
/**
* @brief remove record from database.
*
* Remove the PVRecord. Release any resources used and
* Remove the PVRecord. Release any resources used and
* get rid of listeners and requesters.
* If derived class overrides this then it must call PVRecord::remove()
* after it has destroyed any resorces it uses.
@@ -103,7 +100,7 @@ public:
* @brief Optional method for derived class.
*
* Return a service corresponding to the specified request PVStructure.
* @param pvRequest The request PVStructure
* @param pvRequest The request PVStructure
* @return The corresponding service
*/
virtual epics::pvAccess::RPCServiceAsync::shared_pointer getService(
@@ -112,15 +109,18 @@ public:
return epics::pvAccess::RPCServiceAsync::shared_pointer();
}
/**
* @brief Creates a <b>soft</b> record.
* @brief Creates a <b>soft</b> record.
*
* @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,26 +236,53 @@ 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.
*
*
* Must be called by derived classes.
*/
void initPVRecord();
private:
friend class PVDatabase;
void unlistenClients();
PVRecordFieldPtr findPVRecordField(
PVRecordStructurePtr const & pvrs,
epics::pvData::PVFieldPtr const & pvField);
void notifyClients();
std::string recordName;
epics::pvData::PVStructurePtr pvStructure;
@@ -271,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);
@@ -346,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;
@@ -499,6 +530,7 @@ public:
/**
* @brief Remove a record.
* @param record The record to remove.
*
* @return <b>true</b> if record was removed.
*/
bool removeRecord(PVRecordPtr const & record);
@@ -508,6 +540,9 @@ public:
*/
epics::pvData::PVStringArrayPtr getRecordNames();
private:
friend class PVRecord;
PVRecordWPtr removeFromMap(PVRecordPtr const & record);
PVDatabase();
void lock();
void unlock();
@@ -519,4 +554,3 @@ private:
}}
#endif /* PVDATABASE_H */

View File

@@ -66,7 +66,7 @@ private:
epics::pvData::PVScalarPtr master;
bool firstTime;
double lastReportedValue;
PVDeadbandFilter(bool absolute,double deadband,epics::pvData::PVScalarPtr const & master);
public:
@@ -100,4 +100,3 @@ public:
}}
#endif /* PVDEADBANDPLUGIN_H */

View File

@@ -16,7 +16,7 @@
#include <shareLib.h>
namespace epics { namespace pvCopy{
namespace epics { namespace pvCopy{
class PVPlugin;
class PVFilter;
@@ -36,7 +36,7 @@ typedef std::map<std::string,PVPluginPtr> PVPluginMap;
* PVCopy looks for plugins defined in pvRequest and calls the filter when a pvCopy is updated.
* @author mrk
* @since 2017.03.17
*
*
* Interface for a filter plugin for PVCopy.
*
*/

View File

@@ -18,7 +18,7 @@
#include <shareLib.h>
namespace epics { namespace pvCopy{
namespace epics { namespace pvCopy{
class PVCopyTraverseMasterCallback;
typedef std::tr1::shared_ptr<PVCopyTraverseMasterCallback> PVCopyTraverseMasterCallbackPtr;
@@ -62,7 +62,7 @@ typedef std::tr1::shared_ptr<CopyStructureNode> CopyStructureNodePtr;
* Class that manages one or more PVStructures that holds an arbitrary subset of the fields
* in another PVStructure called master.
*/
class epicsShareClass PVCopy :
class epicsShareClass PVCopy :
public std::tr1::enable_shared_from_this<PVCopy>
{
public:
@@ -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,22 +167,27 @@ 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.
*/
std::string dump();
private:
PVCopyPtr getPtrSelf()
{
return shared_from_this();
}
epics::pvData::PVStructurePtr pvMaster;
epics::pvData::StructureConstPtr structure;
CopyNodePtr headNode;
epics::pvData::PVStructurePtr cacheInitStructure;
epics::pvData::BitSetPtr ignorechangeBitSet;
bool requestHasMasterField;
void traverseMaster(
CopyNodePtr const &node,

View File

@@ -20,7 +20,7 @@
#include <shareLib.h>
namespace epics { namespace pvDatabase {
namespace epics { namespace pvDatabase {
class PVSupport;
typedef std::tr1::shared_ptr<PVSupport> PVSupportPtr;
@@ -29,7 +29,7 @@ typedef std::tr1::shared_ptr<PVSupport> PVSupportPtr;
* @brief Base interface for a PVSupport.
*
*/
class epicsShareClass PVSupport
class epicsShareClass PVSupport
{
public:
POINTER_DEFINITIONS(PVSupport);
@@ -75,4 +75,3 @@ public:
}}
#endif /* PVSUPPORT_H */

View File

@@ -66,7 +66,7 @@ private:
bool current;
bool copy;
epics::pvData::PVFieldPtr master;
PVTimestampFilter(bool current,bool copy,epics::pvData::PVFieldPtr const & pvField);
public:
@@ -98,4 +98,3 @@ public:
}}
#endif /* PVTIMESTAMPPLUGIN_H */

67
src/pv/pvdbcrAddRecord.h Normal file
View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View File

@@ -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 */

View File

@@ -19,7 +19,7 @@
#include <shareLib.h>
namespace epics { namespace pvDatabase {
namespace epics { namespace pvDatabase {
class ScalarAlarmSupport;
typedef std::tr1::shared_ptr<ScalarAlarmSupport> ScalarAlarmSupportPtr;
@@ -61,19 +61,19 @@ public:
*
*/
virtual void reset();
/**
/**
* @brief create a ScalarAlarm
*
* @param pvRecord - The pvRecord to which the support is attached.
* @return The new ScalarAlarm
*/
static ScalarAlarmSupportPtr create(PVRecordPtr const & pvRecord);
/**
/**
* @brief create a scalarAlarm required by ScalarAlarm
*
* @return The scalarAlarmField introspection structure.
*/
static epics::pvData::StructureConstPtr scalarAlarmField();
static epics::pvData::StructureConstPtr scalarAlarmField();
private:
ScalarAlarmSupport(PVRecordPtr const & pvRecord);
@@ -108,4 +108,3 @@ private:
}}
#endif /* SCALARALARMSUPPORT_H */

View File

@@ -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 */

View File

@@ -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"
@@ -38,7 +41,7 @@ using std::cout;
using std::endl;
using std::string;
namespace epics { namespace pvDatabase {
namespace epics { namespace pvDatabase {
static StructureConstPtr nullStructure;
@@ -68,19 +71,18 @@ static bool getProcess(PVStructurePtr pvRequest,bool processDefault)
return pvString->get().compare("true")==0 ? true : false;
} else if(scalar->getScalarType()==pvBoolean) {
PVBooleanPtr pvBoolean = static_pointer_cast<PVBoolean>(pvField);
return pvBoolean->get();
return pvBoolean->get();
}
return 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,
@@ -102,7 +104,7 @@ private:
ChannelProcessRequester::shared_pointer const & channelProcessRequester,
PVRecordPtr const &pvRecord,
int nProcess)
:
:
channelLocal(channelLocal),
channelProcessRequester(channelProcessRequester),
pvRecord(pvRecord),
@@ -132,7 +134,7 @@ ChannelProcessLocalPtr ChannelProcessLocal::create(
if(pvField) {
PVStringPtr pvString = pvOptions->getSubField<PVString>("nProcess");
if(pvString) {
int size;
int size=0;
std::stringstream ss;
ss << pvString->get();
ss >> size;
@@ -156,10 +158,7 @@ ChannelProcessLocalPtr ChannelProcessLocal::create(
ChannelProcessLocal::~ChannelProcessLocal()
{
PVRecordPtr pvr(pvRecord.lock());
if(pvr && pvr->getTraceLevel()>0) {
cout << "~ChannelProcessLocal() " << pvr->getRecordName() << endl;
}
//cout << "~ChannelProcessLocal()\n";
}
std::tr1::shared_ptr<Channel> ChannelProcessLocal::getChannel()
@@ -207,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,
@@ -238,7 +236,7 @@ private:
PVStructurePtr const&pvStructure,
BitSetPtr const & bitSet,
PVRecordPtr const &pvRecord)
:
:
firstTime(true),
callProcess(callProcess),
channelLocal(channelLocal),
@@ -304,10 +302,7 @@ ChannelGetLocalPtr ChannelGetLocal::create(
ChannelGetLocal::~ChannelGetLocal()
{
PVRecordPtr pvr(pvRecord.lock());
if(pvr && pvr->getTraceLevel()>0) {
cout << "~ChannelGetLocal() " << pvr->getRecordName() << endl;
}
//cout << "~ChannelGetLocal()\n";
}
std::tr1::shared_ptr<Channel> ChannelGetLocal::getChannel()
@@ -334,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 {
@@ -381,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,
@@ -469,10 +470,7 @@ ChannelPutLocalPtr ChannelPutLocal::create(
ChannelPutLocal::~ChannelPutLocal()
{
PVRecordPtr pvr(pvRecord.lock());
if(pvr && pvr->getTraceLevel()>0) {
cout << "~ChannelPutLocal() " << pvr->getRecordName() << endl;
}
//cout << "~ChannelPutLocal()\n";
}
std::tr1::shared_ptr<Channel> ChannelPutLocal::getChannel()
@@ -499,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 {
@@ -529,10 +534,18 @@ 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 {
{
{
epicsGuard <PVRecord> guard(*pvr);
pvr->beginGroupPut();
pvCopy->updateMaster(pvStructure, bitSet);
@@ -543,7 +556,7 @@ void ChannelPutLocal::put(
}
requester->putDone(Status::Ok,getPtrSelf());
if(pvr->getTraceLevel()>1)
{
{
cout << "ChannelPutLocal::put" << endl;
}
} catch(std::exception& ex) {
@@ -554,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,
@@ -590,7 +602,7 @@ private:
PVStructurePtr const&pvGetStructure,
BitSetPtr const & getBitSet,
PVRecordPtr const &pvRecord)
:
:
callProcess(callProcess),
channelLocal(channelLocal),
channelPutGetRequester(channelPutGetRequester),
@@ -662,10 +674,7 @@ ChannelPutGetLocalPtr ChannelPutGetLocal::create(
ChannelPutGetLocal::~ChannelPutGetLocal()
{
PVRecordPtr pvr(pvRecord.lock());
if(pvr && pvr->getTraceLevel()>0) {
cout << "~ChannelPutGetLocal() " << pvr->getRecordName() << endl;
}
//cout << "~ChannelPutGetLocal()\n";
}
std::tr1::shared_ptr<Channel> ChannelPutGetLocal::getChannel()
@@ -693,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 {
@@ -721,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 {
@@ -748,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 {
@@ -772,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,
@@ -862,10 +891,7 @@ ChannelRPCLocalPtr ChannelRPCLocal::create(
ChannelRPCLocal::~ChannelRPCLocal()
{
PVRecordPtr pvr(pvRecord.lock());
if(pvr && pvr->getTraceLevel()>0) {
cout << "~ChannelRPCLocal() " << pvr->getRecordName() << endl;
}
//cout << "~ChannelRPCLocal()\n";
}
std::tr1::shared_ptr<Channel> ChannelRPCLocal::getChannel()
@@ -886,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;
@@ -902,7 +928,7 @@ void ChannelRPCLocal::processRequest(
status = Status(Status::STATUSTYPE_FATAL, "Unexpected exception caught while calling RPCService.request(PVStructure).");
ok = false;
}
// check null result
if (ok && !result)
{
@@ -967,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,
@@ -1001,7 +1026,7 @@ private:
PVArrayPtr const &pvArray,
PVArrayPtr const &pvCopy,
PVRecordPtr const &pvRecord)
:
:
channelLocal(channelLocal),
channelArrayRequester(channelArrayRequester),
pvArray(pvArray),
@@ -1104,10 +1129,7 @@ ChannelArrayLocalPtr ChannelArrayLocal::create(
ChannelArrayLocal::~ChannelArrayLocal()
{
PVRecordPtr pvr(pvRecord.lock());
if(pvr && pvr->getTraceLevel()>0) {
cout << "~ChannelArrayLocal() " << pvr->getRecordName() << endl;
}
//cout << "~ChannelArrayLocal()\n";
}
std::tr1::shared_ptr<Channel> ChannelArrayLocal::getChannel()
@@ -1246,10 +1268,16 @@ ChannelLocal::ChannelLocal(
ChannelProviderLocalPtr const & provider,
ChannelRequester::shared_pointer const & requester,
PVRecordPtr const & pvRecord)
:
:
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()"
@@ -1257,15 +1285,91 @@ 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()
{
PVRecordPtr pvr(pvRecord.lock());
if(!pvr) return;
if(pvr->getTraceLevel()>0)
{
cout << "~ChannelLocal()" << endl;
if(asMemberPvt) {
asRemoveMember(&asMemberPvt);
asMemberPvt = 0;
}
if(asClientPvt) {
asRemoveClient(&asClientPvt);
asClientPvt = 0;
}
}
@@ -1296,7 +1400,7 @@ string ChannelLocal::getRequesterName()
<< " requester exists " << (requester ? "true" : "false")
<< endl;
}
if(!requester) return string();
return requester->getRequesterName();
}
@@ -1319,7 +1423,7 @@ void ChannelLocal::message(
string recordName("record deleted");
if(pvr) recordName = pvr->getRecordName();
cout << recordName
<< " message " << message
<< " message " << message
<< " messageType " << getMessageTypeName(messageType)
<< endl;
}
@@ -1362,8 +1466,8 @@ void ChannelLocal::getField(GetFieldRequester::shared_pointer const &requester,
pvr->getPVRecordStructure()->getPVStructure()->getStructure();
requester->getDone(Status::Ok,structure);
return;
}
PVFieldPtr pvField =
}
PVFieldPtr pvField =
pvr->getPVRecordStructure()->getPVStructure()->getSubField(subField);
if(pvField) {
requester->getDone(Status::Ok,pvField->getField());
@@ -1479,7 +1583,7 @@ ChannelRPC::shared_pointer ChannelLocal::createChannelRPC(
<< endl;
}
ChannelRPCLocalPtr channelRPC =
ChannelRPCLocalPtr channelRPC =
ChannelRPCLocal::create(
getPtrSelf(),
channelRPCRequester,

View File

@@ -10,6 +10,7 @@
*/
#include <epicsThread.h>
#include <asLib.h>
#include <pv/serverContext.h>
#include <pv/syncChannelFind.h>
#include <pv/pvTimeStamp.h>
@@ -30,13 +31,13 @@ using std::cout;
using std::endl;
using std::string;
namespace epics { namespace pvDatabase {
namespace epics { namespace pvDatabase {
static string providerName("local");
static ChannelProviderLocalPtr channelProvider;
class LocalChannelProviderFactory : public ChannelProviderFactory
{
{
public:
POINTER_DEFINITIONS(LocalChannelProviderFactory);
virtual string getFactoryName() { return providerName;}
@@ -114,7 +115,7 @@ ChannelFind::shared_pointer ChannelProviderLocal::channelFind(
Status::Ok,
shared_from_this(),
true);
} else {
Status notFoundStatus(Status::STATUSTYPE_ERROR,"pv not found");
channelFindRequester->channelFindResult(
@@ -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;
}
}}

View File

@@ -34,7 +34,7 @@ using std::cout;
using std::endl;
using std::string;
namespace epics { namespace pvDatabase {
namespace epics { namespace pvDatabase {
class MonitorLocal;
typedef std::tr1::shared_ptr<MonitorLocal> MonitorLocalPtr;
@@ -88,7 +88,7 @@ public:
nextGetUsed = 0;
nextReleaseUsed = 0;
}
MonitorElementPtr getFree()
{
if(numberFree==0) return MonitorElementPtr();
@@ -98,7 +98,7 @@ public:
if(nextGetFree>=size) nextGetFree = 0;
return elements[ind];
}
void setUsed(MonitorElementPtr const &element)
{
if(element!=elements[nextSetUsed++]) {
@@ -107,7 +107,7 @@ public:
numberUsed++;
if(nextSetUsed>=size) nextSetUsed = 0;
}
MonitorElementPtr getUsed()
{
if(numberUsed==0) return MonitorElementPtr();
@@ -131,7 +131,7 @@ public:
typedef std::tr1::shared_ptr<MonitorRequester> MonitorRequesterPtr;
class MonitorLocal :
public Monitor,
public PVListener,
@@ -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();
@@ -191,10 +190,7 @@ MonitorLocal::MonitorLocal(
MonitorLocal::~MonitorLocal()
{
if(pvRecord->getTraceLevel()>0)
{
cout << "MonitorLocal::~MonitorLocal()" << endl;
}
//cout << "MonitorLocal::~MonitorLocal()" << endl;
}
@@ -294,7 +290,13 @@ void MonitorLocal::dataPut(PVRecordFieldPtr const & pvRecordField)
{
if(pvRecord->getTraceLevel()>1)
{
cout << "PVCopyMonitor::dataPut(pvRecordField)" << endl;
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;
{
@@ -319,7 +321,7 @@ void MonitorLocal::dataPut(
{
if(pvRecord->getTraceLevel()>1)
{
cout << "PVCopyMonitor::dataPut(requested,pvRecordField)" << endl;
cout << "MonitorLocal::dataPut(requested,pvRecordField)" << endl;
}
if(state!=active) return;
{
@@ -346,7 +348,7 @@ void MonitorLocal::beginGroupPut(PVRecordPtr const & pvRecord)
{
if(pvRecord->getTraceLevel()>1)
{
cout << "PVCopyMonitor::beginGroupPut()" << endl;
cout << "MonitorLocal::beginGroupPut()" << endl;
}
if(state!=active) return;
{
@@ -360,7 +362,7 @@ void MonitorLocal::endGroupPut(PVRecordPtr const & pvRecord)
{
if(pvRecord->getTraceLevel()>1)
{
cout << "PVCopyMonitor::endGroupPut dataChanged " << dataChanged << endl;
cout << "MonitorLocal::endGroupPut dataChanged " << dataChanged << endl;
}
if(state!=active) return;
{
@@ -377,7 +379,7 @@ void MonitorLocal::unlisten(PVRecordPtr const & pvRecord)
{
if(pvRecord->getTraceLevel()>1)
{
cout << "PVCopyMonitor::unlisten\n";
cout << "MonitorLocal::unlisten\n";
}
{
Lock xx(mutex);
@@ -387,7 +389,7 @@ void MonitorLocal::unlisten(PVRecordPtr const & pvRecord)
if(requester) {
if(pvRecord->getTraceLevel()>1)
{
cout << "PVCopyMonitor::unlisten calling requester->unlisten\n";
cout << "MonitorLocal::unlisten calling requester->unlisten\n";
}
requester->unlisten(getPtrSelf());
}

View File

@@ -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);

View File

@@ -2,14 +2,17 @@
SRC_DIRS += $(PVDATABASE_SRC)/special
LIBSRCS += traceRecord.cpp
LIBSRCS += removeRecord.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 += processRecordRegister.dbd
LIBSRCS += traceRecordRegister.cpp
LIBSRCS += removeRecordRegister.cpp
LIBSRCS += processRecordRegister.cpp
LIBSRCS += pvdbcrScalarRecord.cpp
LIBSRCS += pvdbcrScalarArrayRecord.cpp
LIBSRCS += pvdbcrAddRecord.cpp
LIBSRCS += pvdbcrRemoveRecord.cpp
LIBSRCS += pvdbcrProcessRecord.cpp
LIBSRCS += pvdbcrTraceRecord.cpp

View File

@@ -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);
}

View File

@@ -1 +0,0 @@
registrar("processRecordRegister")

View File

@@ -0,0 +1,147 @@
/*
* 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 exampleLink uses
#include <epicsExport.h>
#define epicsExportSharedSymbols
#include "pv/pvDatabase.h"
#include "pv/pvdbcrAddRecord.h"
using namespace epics::pvData;
using namespace std;
namespace epics { namespace pvDatabase {
PvdbcrAddRecordPtr PvdbcrAddRecord::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)->
addNestedUnion("union") ->
endNested()->
endNested()->
addNestedStructure("result") ->
add("status",pvString) ->
endNested()->
createStructure();
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure);
PvdbcrAddRecordPtr pvRecord(
new PvdbcrAddRecord(recordName,pvStructure,asLevel,asGroup));
if(!pvRecord->init()) pvRecord.reset();
return pvRecord;
}
PvdbcrAddRecord::PvdbcrAddRecord(
std::string const & recordName,
PVStructurePtr const & pvStructure,
int asLevel,std::string const & asGroup)
: PVRecord(recordName,pvStructure,asLevel,asGroup)
{
}
bool PvdbcrAddRecord::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 PvdbcrAddRecord::process()
{
PVDataCreatePtr pvDataCreate = getPVDataCreate();
string name = pvRecordName->get();
PVRecordPtr pvRecord = PVDatabase::getMaster()->findRecord(name);
if(pvRecord) {
pvResult->put(name + " already exists");
return;
}
PVUnionPtr pvUnion = getPVStructure()->getSubField<PVUnion>("argument.union");
if(!pvUnion) {
pvResult->put(name + " argument.union is NULL");
return;
}
PVFieldPtr pvField(pvUnion->get());
if(!pvField) {
pvResult->put(name + " union has no value");
return;
}
if(pvField->getField()->getType()!=epics::pvData::structure) {
pvResult->put(name + " union most be a structure");
return;
}
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);
if(result) {
pvResult->put("success");
} else {
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);
}

View File

@@ -0,0 +1 @@
registrar("pvdbcrAddRecord")

View File

@@ -0,0 +1,6 @@
include "pvdbcrAddRecord.dbd"
include "pvdbcrRemoveRecord.dbd"
include "pvdbcrProcessRecord.dbd"
include "pvdbcrTraceRecord.dbd"
include "pvdbcrScalarRecord.dbd"
include "pvdbcrScalarArrayRecord.dbd"

View File

@@ -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 {
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,13 +132,13 @@ void ProcessRecord::process()
}
}
void ProcessRecord::run()
void PvdbcrProcessRecord::run()
{
while(true) {
if(runStop.tryWait()) {
runReturn.signal();
return;
}
}
if(delay>0.0) epicsThreadSleep(delay);
epicsGuard<epics::pvData::Mutex> guard(mutex);
PVRecordMap::iterator iter;
@@ -161,7 +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);
}

View File

@@ -0,0 +1 @@
registrar("pvdbcrProcessRecord")

View 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);
}

View File

@@ -0,0 +1 @@
registrar("pvdbcrRemoveRecord")

View 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);
}

View File

@@ -0,0 +1 @@
registrar("pvdbcrScalarArrayRecord")

View 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);
}

View File

@@ -0,0 +1 @@
registrar("pvdbcrScalarRecord")

View 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);
}

View File

@@ -0,0 +1 @@
registrar("pvdbcrTraceRecord")

View File

@@ -1,93 +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");
}
}}

View File

@@ -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);
}

View File

@@ -1 +0,0 @@
registrar("removeRecordRegister")

View File

@@ -1,96 +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");
}
}}

View File

@@ -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);
}

View File

@@ -1 +0,0 @@
registrar("traceRecordRegister")

View File

@@ -25,11 +25,11 @@ using namespace epics::pvData;
using namespace epics::pvAccess;
using namespace std;
namespace epics { namespace pvDatabase {
namespace epics { namespace pvDatabase {
ControlSupport::~ControlSupport()
{
cout << "ControlSupport::~ControlSupport()\n";
//cout << "ControlSupport::~ControlSupport()\n";
}
epics::pvData::StructureConstPtr ControlSupport::controlField(ScalarType scalarType)
@@ -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;
}
@@ -135,4 +136,3 @@ void ControlSupport::reset()
}}

View File

@@ -26,11 +26,11 @@ using namespace epics::pvData;
using namespace epics::pvAccess;
using namespace std;
namespace epics { namespace pvDatabase {
namespace epics { namespace pvDatabase {
ScalarAlarmSupport::~ScalarAlarmSupport()
{
cout << "ScalarAlarmSupport::~ScalarAlarmSupport()\n";
//cout << "ScalarAlarmSupport::~ScalarAlarmSupport()\n";
}
@@ -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;
}
@@ -83,7 +84,7 @@ bool ScalarAlarmSupport::init(
pvHysteresis = pvScalarAlarm->getSubField<PVDouble>("hysteresis");
}
if(!pvScalarAlarm
|| !pvLowAlarmLimit || !pvLowWarningLimit
|| !pvLowAlarmLimit || !pvLowWarningLimit
|| !pvLowWarningLimit || !pvHighAlarmLimit
|| !pvHysteresis)
{
@@ -216,4 +217,3 @@ void ScalarAlarmSupport::setAlarm(
}}

View File

@@ -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

View File

@@ -26,14 +26,14 @@
#ifdef listenerEpicsExportSharedSymbols
# define epicsExportSharedSymbols
# undef listenerEpicsExportSharedSymbols
# undef listenerEpicsExportSharedSymbols
#endif
#include <shareLib.h>
//epicsShareFunc epics::pvData::PVStructurePtr createListener();
namespace epics { namespace pvDatabase {
namespace epics { namespace pvDatabase {
using namespace epics::pvData;
using namespace std;
@@ -59,7 +59,7 @@ public:
}
virtual void dataPut(PVRecordFieldPtr const & pvRecordField)
{
cout << "Listener::dataPut record " << recordName
cout << "Listener::dataPut record " << recordName
<< " pvRecordField " << pvRecordField->getPVField()->getFullName()
<< endl;
}
@@ -67,7 +67,7 @@ public:
PVRecordStructurePtr const & requested,
PVRecordFieldPtr const & pvRecordField)
{
cout << "Listener::dataPut record " << recordName
cout << "Listener::dataPut record " << recordName
<< " requested " << requested->getPVStructure()->getFullName()
<< " pvRecordField " << pvRecordField->getPVField()->getFullName()
<< endl;
@@ -84,7 +84,7 @@ public:
{
cout << "Listener::unlisten record " << recordName << endl;
}
private:
Listener(PVRecordPtr const & pvRecord)
: pvCopy(

View File

@@ -25,14 +25,14 @@
#ifdef powerSupplyEpicsExportSharedSymbols
# define epicsExportSharedSymbols
# undef powerSupplyEpicsExportSharedSymbols
# undef powerSupplyEpicsExportSharedSymbols
#endif
#include <shareLib.h>
//epicsShareFunc epics::pvData::PVStructurePtr createPowerSupply();
namespace epics { namespace pvDatabase {
namespace epics { namespace pvDatabase {
class PowerSupply;
typedef std::tr1::shared_ptr<PowerSupply> PowerSupplyPtr;
@@ -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();

View File

@@ -25,12 +25,12 @@
#ifdef pvRecordClientEpicsExportSharedSymbols
# define epicsExportSharedSymbols
# undef pvRecordClientEpicsExportSharedSymbols
# undef pvRecordClientEpicsExportSharedSymbols
#endif
#include <shareLib.h>
namespace epics { namespace pvDatabase {
namespace epics { namespace pvDatabase {
class RecordClient;
typedef std::tr1::shared_ptr<RecordClient> RecordClientPtr;
@@ -59,7 +59,7 @@ public:
std::cout << "RecordClient::detach record " << pvRecord->getRecordName() << std::endl;
this->pvRecord.reset();
}
private:
RecordClient(PVRecordPtr const & pvRecord)
: pvRecord(pvRecord)

View 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;
}

View File

@@ -134,4 +134,3 @@ MAIN(testExampleRecord)
test();
return 0;
}

View File

@@ -79,4 +79,3 @@ MAIN(testLocalProvider)
test();
return 0;
}

View File

@@ -75,4 +75,3 @@ MAIN(testPVAServer)
test();
return 0;
}

View File

@@ -273,7 +273,35 @@ static void testPVScalarArray(
cout << endl;
}
}
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,12 +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;
}

View File

@@ -103,4 +103,3 @@ MAIN(testPVRecord)
powerSupplyTest();
return 0;
}

View File

@@ -107,7 +107,7 @@ static void arrayTest()
uint32 nset = 0;
size_t n = 10;
shared_vector<double> values(n);
PVStructurePtr pvRecordStructure(getStandardPVField()->scalarArray(pvDouble,""));
PVRecordPtr pvRecord(PVRecord::create("doubleArrayRecord",pvRecordStructure));
PVStructurePtr pvRequest(CreateRequest::create()->createRequest("value[array=1:3]"));
@@ -145,6 +145,73 @@ static void arrayTest()
testOk1(nset==1);
}
static void unionArrayTest()
{
if(debug) {cout << endl << endl << "****unionArrayTest****" << endl;}
bool result = false;
uint32 nset = 0;
size_t n = 10;
shared_vector<double> values(n);
for(size_t i=0; i<n; i++) values[i] = i + .06;
PVDoubleArrayPtr pvDoubleArray =
static_pointer_cast<PVDoubleArray>(PVDataCreate::getPVDataCreate()->createPVScalarArray(pvDouble));
const shared_vector<const double> yyy(freeze(values));
pvDoubleArray->putFrom(yyy);
StandardFieldPtr standardField = getStandardField();
FieldCreatePtr fieldCreate = getFieldCreate();
StructureConstPtr top = fieldCreate->createFieldBuilder()->
add("value",fieldCreate->createVariantUnion()) ->
add("timeStamp", standardField->timeStamp()) ->
addNestedStructure("subfield") ->
add("value",fieldCreate->createVariantUnion()) ->
endNested()->
createStructure();
PVStructurePtr pvRecordStructure(PVDataCreate::getPVDataCreate()->createPVStructure(top));
PVRecordPtr pvRecord(PVRecord::create("unionArrayRecord",pvRecordStructure));
PVUnionPtr pvUnion = pvRecord->getPVStructure()->getSubField<PVUnion>("value");
pvUnion->set(pvDoubleArray);
pvUnion = pvRecord->getPVStructure()->getSubField<PVUnion>("subfield.value");
pvUnion->set(pvDoubleArray);
if(debug) { cout << "initial\n" << pvRecordStructure << "\n";}
PVStructurePtr pvRequest(CreateRequest::create()->createRequest("value[array=1:3]"));
PVCopyPtr pvCopy(PVCopy::create(pvRecordStructure,pvRequest,""));
PVStructurePtr pvStructureCopy(pvCopy->createPVStructure());
BitSetPtr bitSet(new BitSet(pvStructureCopy->getNumberFields()));
PVDoubleArrayPtr pvValue(pvRecordStructure->getSubField<PVDoubleArray>("value"));
result = pvCopy->updateCopySetBitSet(pvStructureCopy,bitSet);
nset = bitSet->cardinality();
if(debug) {
cout << "after get value"
<< " result " << (result ? "true" : "false")
<< " nset " << nset
<< " bitSet " << *bitSet
<< " pvStructureCopy\n" << pvStructureCopy
<< "\n";
}
testOk1(result==true);
testOk1(nset==1);
pvRequest = CreateRequest::create()->createRequest("subfield.value[array=1:3]");
pvCopy = PVCopy::create(pvRecordStructure,pvRequest,"");
pvStructureCopy = pvCopy->createPVStructure();
bitSet = BitSetPtr(new BitSet(pvStructureCopy->getNumberFields()));
pvValue = pvRecordStructure->getSubField<PVDoubleArray>("subfield.value");
result = pvCopy->updateCopySetBitSet(pvStructureCopy,bitSet);
nset = bitSet->cardinality();
if(debug) {
cout << "after get subfield.value"
<< " result " << (result ? "true" : "false")
<< " nset " << nset
<< " bitSet " << *bitSet
<< " pvStructureCopy\n" << pvStructureCopy
<< "\n";
}
testOk1(result==true);
testOk1(nset==1);
}
static void timeStampTest()
{
if(debug) {cout << endl << endl << "****timeStampTest****" << endl;}
@@ -265,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(22);
testPlan(46);
PVDatabasePtr pvDatabase(PVDatabase::getMaster());
deadbandTest();
arrayTest();
unionArrayTest();
timeStampTest();
ignoreTest();
dataDistributorTest();
return 0;
}