12 Commits
4.7.1 ... 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
20 changed files with 638 additions and 316 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

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

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 = 4.7.1
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

View File

@@ -2,7 +2,7 @@
EPICS_PVDATABASE_MAJOR_VERSION = 4
EPICS_PVDATABASE_MINOR_VERSION = 7
EPICS_PVDATABASE_MAINTENANCE_VERSION = 1
EPICS_PVDATABASE_MAINTENANCE_VERSION = 2
# Development flag, set to zero for release versions

View File

@@ -2,6 +2,13 @@
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

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

@@ -432,6 +432,7 @@ bool PVCopy::init(epics::pvData::PVStructurePtr const &pvRequest)
PVStructurePtr pvMasterStructure = pvMaster;
size_t len = pvRequest->getPVFields().size();
bool entireMaster = false;
requestHasMasterField = false;
PVStructurePtr pvOptions;
if(len==0) {
entireMaster = true;
@@ -441,6 +442,9 @@ bool PVCopy::init(epics::pvData::PVStructurePtr const &pvRequest)
// 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");

View File

@@ -481,27 +481,30 @@ void PVRecordStructure::init()
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();
// 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) {
pvRecordField->master = p;
p = p->parent.lock();
}
}
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

@@ -167,6 +167,10 @@ public:
* name is the subField name and value is the subField value.
*/
epics::pvData::PVStructurePtr getOptions(std::size_t fieldOffset);
/**
* Is master field requested?
*/
bool isMasterFieldRequested() const {return requestHasMasterField;}
/**
* For debugging.
*/
@@ -183,6 +187,7 @@ private:
CopyNodePtr headNode;
epics::pvData::PVStructurePtr cacheInitStructure;
epics::pvData::BitSetPtr ignorechangeBitSet;
bool requestHasMasterField;
void traverseMaster(
CopyNodePtr const &node,

View File

@@ -292,6 +292,12 @@ void MonitorLocal::dataPut(PVRecordFieldPtr const & pvRecordField)
{
cout << "MonitorLocal::dataPut(pvRecordField)" << endl;
}
// If this record field is the master field, and the master field was not
// requested, we do not proceed with copy
bool isMasterField = pvRecordField->getPVRecord()->getPVStructure()->getFieldOffset()==0;
if (isMasterField && !pvCopy->isMasterFieldRequested()) {
return;
}
if(state!=active) return;
{
Lock xx(mutex);

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

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

@@ -289,6 +289,7 @@ static void testMasterField(PVRecordPtr const& pvRecord)
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();
@@ -431,7 +432,7 @@ static void masterFieldTest()
MAIN(testPVCopy)
{
testPlan(70);
testPlan(71);
scalarTest();
arrayTest();
powerSupplyTest();