Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 466d41ebb9 | |||
| d18e2219b3 | |||
| ff22538129 | |||
| 67e668795e | |||
| 1ac2e6c809 | |||
| 40b327cfcb | |||
| 20edb8fdf5 | |||
| babda345b4 | |||
| 04fbaf5a2e | |||
| 3438fde276 | |||
| b515fd30ca | |||
| ad8b77e19f | |||
| b8389ac6a1 | |||
| d47008ee53 | |||
| a709854f0d | |||
| 527afaf856 | |||
| d65af720d1 | |||
| 503cf414be | |||
| 1682c991d2 | |||
| c68c0038e6 | |||
| b69c25feb6 | |||
| eae493e732 | |||
| 29d00e7e38 | |||
| 1335d75403 | |||
| c32cc1ba3c | |||
| f5278ef4ac | |||
| b9d3ad8f90 | |||
| 0e772037ee | |||
| d100eac09e | |||
| 740aad6712 | |||
| c66f224602 |
+7
-6
@@ -30,6 +30,7 @@ skip_commits:
|
||||
files:
|
||||
- 'documentation/*'
|
||||
- '**/*.md'
|
||||
- '.github/**'
|
||||
|
||||
# Build Configurations: dll/static, regular/debug
|
||||
configuration:
|
||||
@@ -48,7 +49,7 @@ environment:
|
||||
CMP: vs2019
|
||||
BASE: 7.0
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
CMP: mingw
|
||||
CMP: gcc
|
||||
BASE: 7.0
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
CMP: vs2017
|
||||
@@ -66,15 +67,15 @@ platform:
|
||||
#---------------------------------#
|
||||
|
||||
build_script:
|
||||
- cmd: python .ci/appveyor/do.py prepare
|
||||
- cmd: python .ci/appveyor/do.py build
|
||||
- cmd: python .ci/cue.py prepare
|
||||
- cmd: python .ci/cue.py build
|
||||
|
||||
test_script:
|
||||
- cmd: python .ci/appveyor/do.py test
|
||||
- cmd: python .ci/cue.py test
|
||||
|
||||
on_finish:
|
||||
- ps: Get-ChildItem *.tap -Recurse -Force | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
|
||||
- cmd: python .ci/appveyor/do.py build test-results -s
|
||||
- cmd: python .ci/cue.py build test-results -s
|
||||
|
||||
#---------------------------------#
|
||||
# debugging #
|
||||
@@ -96,7 +97,7 @@ notifications:
|
||||
|
||||
- provider: Email
|
||||
to:
|
||||
- me@example.com
|
||||
- core-talk@aps.anl.gov
|
||||
on_build_success: false
|
||||
|
||||
- provider: GitHubPullRequest
|
||||
|
||||
+1
-1
Submodule .ci updated: ecb7e43660...3db08b5977
@@ -0,0 +1,179 @@
|
||||
# .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:
|
||||
- .appveyor.yml
|
||||
pull_request:
|
||||
|
||||
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 }}
|
||||
WINE: ${{ matrix.wine }}
|
||||
RTEMS: ${{ matrix.rtems }}
|
||||
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"
|
||||
wine: "64"
|
||||
name: "7.0 Ub-20 gcc-9 + MinGW"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: static
|
||||
base: "7.0"
|
||||
wine: "64"
|
||||
name: "7.0 Ub-20 gcc-9 + MinGW, static"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
base: "3.15"
|
||||
wine: "64"
|
||||
name: "3.15 Ub-20 gcc-9 + MinGW"
|
||||
|
||||
- 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-16.04
|
||||
cmp: clang
|
||||
configuration: default
|
||||
base: "7.0"
|
||||
name: "7.0 Ub-16 clang-9"
|
||||
|
||||
- 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"
|
||||
rtems: "4.10"
|
||||
name: "7.0 Ub-20 gcc-9 + RT-4.10"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
base: "7.0"
|
||||
rtems: "4.9"
|
||||
name: "7.0 Ub-20 gcc-9 + RT-4.9"
|
||||
|
||||
- os: ubuntu-16.04
|
||||
cmp: gcc-4.8
|
||||
utoolchain: "4.8"
|
||||
configuration: default
|
||||
base: "7.0"
|
||||
name: "7.0 Ub-16 gcc-4.8"
|
||||
|
||||
- os: ubuntu-16.04
|
||||
cmp: gcc-4.9
|
||||
utoolchain: "4.9"
|
||||
configuration: default
|
||||
base: "7.0"
|
||||
name: "7.0 Ub-16 gcc-4.9"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
cmp: gcc-8
|
||||
utoolchain: "8"
|
||||
configuration: default
|
||||
base: "7.0"
|
||||
name: "7.0 Ub-20 gcc-8"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
cmp: clang
|
||||
configuration: default
|
||||
base: "7.0"
|
||||
name: "7.0 Ub-20 clang-10"
|
||||
|
||||
- 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"
|
||||
|
||||
- os: windows-2019
|
||||
cmp: vs2019
|
||||
configuration: static
|
||||
base: "7.0"
|
||||
name: "7.0 Win2019 MSC-19, static"
|
||||
|
||||
- os: windows-2019
|
||||
cmp: gcc
|
||||
configuration: static
|
||||
base: "7.0"
|
||||
name: "7.0 Win2019 mingw, static"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- name: Cache Dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cache
|
||||
key: ${{ matrix.base }}/${{ matrix.os }}/${{ matrix.cmp }}/${{ matrix.configuration }}/${{ matrix.wine }}${{ matrix.rtems }}/${{ matrix.extra }}/${{ hashFiles('.github/workflows/ci-scripts-build.yml') }}
|
||||
- name: "apt-get install"
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install qemu-system-x86 g++-mingw-w64-x86-64 gdb
|
||||
if: runner.os == 'Linux'
|
||||
- name: "apt-get install gcc-${{ matrix.utoolchain }}"
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install software-properties-common
|
||||
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install g++-${{ matrix.utoolchain }}
|
||||
if: matrix.utoolchain
|
||||
- name: Prepare and compile dependencies
|
||||
run: python .ci/cue.py prepare
|
||||
- name: Build main module
|
||||
run: python .ci/cue.py build
|
||||
- name: Run main module tests
|
||||
run: python .ci/cue.py test
|
||||
- name: Upload tapfiles Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: tapfiles ${{ matrix.name }}
|
||||
path: '**/O.*/*.tap'
|
||||
- name: Collect and show test results
|
||||
run: python .ci/cue.py test-results
|
||||
-108
@@ -1,108 +0,0 @@
|
||||
# .travis.yml for use with EPICS Base ci-scripts
|
||||
# (see: https://github.com/epics-base/ci-scripts)
|
||||
|
||||
# This is YAML - indentation levels are crucial
|
||||
|
||||
language: cpp
|
||||
compiler: gcc
|
||||
dist: bionic
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache
|
||||
|
||||
env:
|
||||
global:
|
||||
- SETUP_PATH=.ci-local:.ci
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
# for all EPICS builds
|
||||
- libreadline6-dev
|
||||
- libncurses5-dev
|
||||
- perl
|
||||
# for clang compiler
|
||||
- clang
|
||||
# for mingw builds (32bit and 64bit)
|
||||
- g++-mingw-w64-i686
|
||||
- g++-mingw-w64-x86-64
|
||||
# for RTEMS cross builds
|
||||
- qemu-system-x86
|
||||
|
||||
install:
|
||||
- ./.ci/travis/prepare.sh
|
||||
|
||||
script:
|
||||
- ./.ci/travis/build.sh
|
||||
|
||||
# If you need to do more during install and build,
|
||||
# add a local directory to your module and do e.g.
|
||||
# - ./.ci-local/travis/install-extras.sh
|
||||
|
||||
# Define build jobs
|
||||
|
||||
# Well-known variables to use
|
||||
# SET source setup file
|
||||
# EXTRA content will be added to make command line
|
||||
# STATIC set to YES for static build (default: NO)
|
||||
# TEST set to NO to skip running the tests (default: YES)
|
||||
# VV set to make build scripts verbose (default: unset)
|
||||
|
||||
# Usually from setup files, but may be specified or overridden
|
||||
# on a job line
|
||||
# MODULES list of dependency modules
|
||||
# BASE branch or release tag name of the EPICS Base to use
|
||||
# <MODULE> branch or release tag for a specific module
|
||||
# ... see README for setup file syntax description
|
||||
|
||||
jobs:
|
||||
include:
|
||||
|
||||
# Different configurations of default gcc and clang
|
||||
|
||||
- env: BASE=7.0
|
||||
|
||||
- env: BASE=7.0
|
||||
compiler: clang
|
||||
|
||||
- env: BASE=7.0 EXTRA="CMD_CXXFLAGS=-std=c++11"
|
||||
|
||||
- env: BASE=7.0 EXTRA="CMD_CXXFLAGS=-std=c++11"
|
||||
compiler: clang
|
||||
|
||||
# Trusty: compiler versions very close to RHEL 7
|
||||
|
||||
- env: BASE=7.0
|
||||
dist: trusty
|
||||
|
||||
- env: BASE=7.0 EXTRA="CMD_CXXFLAGS=-std=c++11"
|
||||
dist: trusty
|
||||
|
||||
- env: BASE=3.15 EXTRA="CMD_CXXFLAGS=-std=c++11"
|
||||
dist: trusty
|
||||
|
||||
# Cross-compilations to Windows using MinGW and WINE
|
||||
|
||||
- env: BASE=7.0 WINE=32 TEST=NO STATIC=YES
|
||||
compiler: mingw
|
||||
|
||||
- env: BASE=7.0 WINE=64 TEST=NO STATIC=NO
|
||||
compiler: mingw
|
||||
|
||||
# Other gcc versions (added as an extra package)
|
||||
|
||||
- env: BASE=7.0
|
||||
compiler: gcc-6
|
||||
addons: { apt: { packages: ["g++-6"], sources: ["ubuntu-toolchain-r-test"] } }
|
||||
|
||||
- env: BASE=7.0
|
||||
compiler: gcc-7
|
||||
addons: { apt: { packages: ["g++-7"], sources: ["ubuntu-toolchain-r-test"] } }
|
||||
|
||||
# MacOS build
|
||||
|
||||
- env: BASE=7.0
|
||||
os: osx
|
||||
compiler: clang
|
||||
addons: { homebrew: { packages: ["re2c"], update: true } }
|
||||
@@ -38,7 +38,7 @@ PROJECT_NAME = pva2pva
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = 1.2.4
|
||||
PROJECT_NUMBER = 1.3.1
|
||||
|
||||
# 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
|
||||
|
||||
@@ -21,6 +21,9 @@ p2p
|
||||
A PV Access gateway (aka proxy).
|
||||
The 'p2p' executable.
|
||||
|
||||
The P2P gateway has been deprecated in favor of
|
||||
[p4p.gw](https://mdavidsaver.github.io/p4p/gw.html).
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# Module (source) version
|
||||
EPICS_QSRV_MAJOR_VERSION = 1
|
||||
EPICS_QSRV_MINOR_VERSION = 2
|
||||
EPICS_QSRV_MAINTENANCE_VERSION = 4
|
||||
EPICS_QSRV_MINOR_VERSION = 3
|
||||
EPICS_QSRV_MAINTENANCE_VERSION = 1
|
||||
|
||||
# ABI version
|
||||
EPICS_QSRV_ABI_MAJOR_VERSION = 1
|
||||
EPICS_QSRV_ABI_MINOR_VERSION = 1
|
||||
EPICS_QSRV_ABI_MINOR_VERSION = 2
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ PROJECT_NAME = pva2pva
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = 1.2.4
|
||||
PROJECT_NUMBER = 1.3.1
|
||||
|
||||
# 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
|
||||
|
||||
@@ -23,7 +23,9 @@ which is also the PV name.
|
||||
So unlike records, the "field" of a group have a different meaning.
|
||||
Group field names are _not_ part of the PV name.
|
||||
|
||||
A group definition is split among several records.
|
||||
A group definition may be split among several records,
|
||||
or included in separate JSON file(s).
|
||||
|
||||
For example of a group including two records is:
|
||||
|
||||
@code
|
||||
@@ -43,6 +45,23 @@ record(ai, "rec:Y") {
|
||||
}
|
||||
@endcode
|
||||
|
||||
Or equivalently with separate .db file and .json files.
|
||||
|
||||
@code
|
||||
# some .db
|
||||
record(ai, "rec:X") {}
|
||||
record(ai, "rec:Y") {}
|
||||
@endcode
|
||||
|
||||
@code
|
||||
{
|
||||
"grp:name": {
|
||||
"X": {+channel:"VAL"},
|
||||
"Y": {+channel:"VAL"}
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
|
||||
This group, named "grp:name", has two fields "X" and "Y".
|
||||
|
||||
@code
|
||||
|
||||
@@ -2,6 +2,22 @@
|
||||
|
||||
@page release_notes Release Notes
|
||||
|
||||
Release 1.3.1 (June 2021)
|
||||
=========================
|
||||
|
||||
- Bug Fixes
|
||||
- Correct handling for server side filters.
|
||||
- Changes
|
||||
- Syncing softMain.cpp with epics-base
|
||||
|
||||
Release 1.3.0 (Feb 2021)
|
||||
========================
|
||||
|
||||
- Changes
|
||||
- Add dbLoadGroup() iocsh function to read group JSON definitions
|
||||
from a file. Mappings in files must refer to full record names
|
||||
instead of fields. eg. 'recname.VAL' instead of 'VAL'.
|
||||
|
||||
Release 1.2.4 (July 2020)
|
||||
=========================
|
||||
|
||||
|
||||
+15
-6
@@ -26,6 +26,7 @@ typedef std::map<std::string, pvd::AnyScalar> options_t;
|
||||
typedef std::map<std::string, options_t> config_t;
|
||||
|
||||
struct context {
|
||||
const std::string chanprefix;
|
||||
std::string msg;
|
||||
std::string group, field, key;
|
||||
unsigned depth; // number of '{'s
|
||||
@@ -34,9 +35,13 @@ struct context {
|
||||
// depth 2 - Group
|
||||
// depth 3 - field
|
||||
|
||||
context() :depth(0u) {}
|
||||
context(const std::string& chanprefix, GroupConfig& conf)
|
||||
:chanprefix(chanprefix)
|
||||
,depth(0u)
|
||||
,conf(conf)
|
||||
{}
|
||||
|
||||
GroupConfig conf;
|
||||
GroupConfig& conf;
|
||||
|
||||
void can_assign()
|
||||
{
|
||||
@@ -69,7 +74,7 @@ struct context {
|
||||
fld.type = value.ref<std::string>();
|
||||
|
||||
} else if(key=="+channel") {
|
||||
fld.channel = value.ref<std::string>();
|
||||
fld.channel = chanprefix + value.ref<std::string>();
|
||||
|
||||
} else if(key=="+id") {
|
||||
fld.id = value.ref<std::string>();
|
||||
@@ -217,6 +222,7 @@ struct handler {
|
||||
}// namespace
|
||||
|
||||
void GroupConfig::parse(const char *txt,
|
||||
const char *recname,
|
||||
GroupConfig& result)
|
||||
{
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
@@ -228,7 +234,12 @@ void GroupConfig::parse(const char *txt,
|
||||
|
||||
std::istringstream strm(txt);
|
||||
|
||||
context ctxt;
|
||||
std::string chanprefix;
|
||||
if(recname) {
|
||||
chanprefix = recname;
|
||||
chanprefix += '.';
|
||||
}
|
||||
context ctxt(chanprefix, result);
|
||||
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
handler handle(yajl_alloc(&conf_cbs, &conf, NULL, &ctxt));
|
||||
@@ -240,6 +251,4 @@ void GroupConfig::parse(const char *txt,
|
||||
|
||||
if(!pvd::yajl_parse_helper(strm, handle))
|
||||
throw std::runtime_error(ctxt.msg);
|
||||
|
||||
ctxt.conf.swap(result);
|
||||
}
|
||||
|
||||
+154
-102
@@ -59,16 +59,14 @@ struct Splitter {
|
||||
};
|
||||
|
||||
struct GroupMemberInfo {
|
||||
// consumes builder
|
||||
GroupMemberInfo(const std::string& a, const std::string& b, const std::tr1::shared_ptr<PVIFBuilder>& builder)
|
||||
:pvname(a), pvfldname(b), builder(builder), putorder(0) {}
|
||||
GroupMemberInfo() :putorder(0) {}
|
||||
|
||||
std::string pvname, // aka. name passed to dbChannelOpen()
|
||||
pvfldname; // PVStructure sub-field
|
||||
std::string structID; // ID to assign to sub-field
|
||||
std::string type; // mapping type
|
||||
typedef std::set<std::string> triggers_t;
|
||||
triggers_t triggers; // names in GroupInfo::members_names which are post()d on events from pvfldname
|
||||
std::tr1::shared_ptr<PVIFBuilder> builder; // not actually shared, but allows us to be copyable
|
||||
int putorder;
|
||||
|
||||
bool operator<(const GroupMemberInfo& o) const {
|
||||
@@ -100,9 +98,6 @@ struct PDBProcessor
|
||||
typedef std::map<std::string, GroupInfo> groups_t;
|
||||
groups_t groups;
|
||||
|
||||
std::string recbase;
|
||||
GroupInfo *curgroup;
|
||||
|
||||
// validate trigger mappings and process into bit map form
|
||||
void resolveTriggers()
|
||||
{
|
||||
@@ -177,8 +172,13 @@ struct PDBProcessor
|
||||
}
|
||||
}
|
||||
|
||||
PDBProcessor() : curgroup(NULL)
|
||||
PDBProcessor()
|
||||
{
|
||||
#ifdef USE_MULTILOCK
|
||||
GroupConfig conf;
|
||||
#endif
|
||||
|
||||
// process info(Q:Group, ...)
|
||||
for(pdbRecordIterator rec; !rec.done(); rec.next())
|
||||
{
|
||||
const char *json = rec.info("Q:group");
|
||||
@@ -189,107 +189,154 @@ struct PDBProcessor
|
||||
warned = true;
|
||||
fprintf(stderr, "%s: ignoring info(Q:Group, ...\n", rec.name());
|
||||
}
|
||||
#else
|
||||
#endif
|
||||
if(PDBProviderDebug>2) {
|
||||
fprintf(stderr, "%s: info(Q:Group, ...\n", rec.name());
|
||||
}
|
||||
|
||||
#ifdef USE_MULTILOCK
|
||||
try {
|
||||
GroupConfig conf;
|
||||
GroupConfig::parse(json, conf);
|
||||
GroupConfig::parse(json, rec.name(), conf);
|
||||
if(!conf.warning.empty())
|
||||
fprintf(stderr, "%s: warning(s) from info(Q:group, ...\n%s", rec.name(), conf.warning.c_str());
|
||||
|
||||
recbase = rec.name();
|
||||
recbase += ".";
|
||||
|
||||
for(GroupConfig::groups_t::const_iterator git=conf.groups.begin(), gend=conf.groups.end();
|
||||
git!=gend; ++git)
|
||||
{
|
||||
const std::string& grpname = git->first;
|
||||
const GroupConfig::Group& grp = git->second;
|
||||
|
||||
if(dbChannelTest(grpname.c_str())==0) {
|
||||
fprintf(stderr, "%s : Error: Group name conflicts with record name. Ignoring...\n", grpname.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
groups_t::iterator it = groups.find(grpname);
|
||||
if(it==groups.end()) {
|
||||
// lazy creation of group
|
||||
std::pair<groups_t::iterator, bool> ins(groups.insert(std::make_pair(grpname, GroupInfo(grpname))));
|
||||
it = ins.first;
|
||||
}
|
||||
curgroup = &it->second;
|
||||
if(!grp.id.empty())
|
||||
curgroup->structID = grp.id;
|
||||
|
||||
for(GroupConfig::Group::fields_t::const_iterator fit=grp.fields.begin(), fend=grp.fields.end();
|
||||
fit!=fend; ++fit)
|
||||
{
|
||||
const std::string& fldname = fit->first;
|
||||
const GroupConfig::Field& fld = fit->second;
|
||||
|
||||
GroupInfo::members_map_t::const_iterator oldgrp(curgroup->members_map.find(fldname));
|
||||
if(oldgrp!=curgroup->members_map.end()) {
|
||||
fprintf(stderr, "%s.%s Warning: ignoring duplicate mapping %s%s\n",
|
||||
grpname.c_str(), fldname.c_str(),
|
||||
recbase.c_str(), fld.channel.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<PVIFBuilder> builder(PVIFBuilder::create(fld.type));
|
||||
|
||||
curgroup->members.push_back(GroupMemberInfo(fld.channel.empty() ? fld.channel : recbase + fld.channel, fldname, builder));
|
||||
curgroup->members.back().structID = fld.id;
|
||||
curgroup->members.back().putorder = fld.putorder;
|
||||
curgroup->members_map[fldname] = (size_t)-1; // placeholder see below
|
||||
|
||||
if(PDBProviderDebug>2) {
|
||||
fprintf(stderr, " pdb map '%s.%s' <-> '%s'\n",
|
||||
curgroup->name.c_str(),
|
||||
curgroup->members.back().pvfldname.c_str(),
|
||||
curgroup->members.back().pvname.c_str());
|
||||
}
|
||||
|
||||
if(!fld.trigger.empty()) {
|
||||
GroupInfo::triggers_t::iterator it = curgroup->triggers.find(fldname);
|
||||
if(it==curgroup->triggers.end()) {
|
||||
std::pair<GroupInfo::triggers_t::iterator, bool> ins(curgroup->triggers.insert(
|
||||
std::make_pair(fldname, GroupInfo::triggers_set_t())));
|
||||
it = ins.first;
|
||||
}
|
||||
|
||||
Splitter sep(fld.trigger.c_str(), ',');
|
||||
std::string target;
|
||||
|
||||
while(sep.snip(target)) {
|
||||
curgroup->hastriggers = true;
|
||||
it->second.insert(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(grp.atomic_set) {
|
||||
GroupInfo::tribool V = grp.atomic ? GroupInfo::True : GroupInfo::False;
|
||||
|
||||
if(curgroup->atomic!=GroupInfo::Unset && curgroup->atomic!=V)
|
||||
fprintf(stderr, "%s Warning: pdb atomic setting inconsistent '%s'\n",
|
||||
grpname.c_str(), curgroup->name.c_str());
|
||||
|
||||
curgroup->atomic=V;
|
||||
|
||||
if(PDBProviderDebug>2)
|
||||
fprintf(stderr, " pdb atomic '%s' %s\n",
|
||||
curgroup->name.c_str(), curgroup->atomic ? "YES" : "NO");
|
||||
}
|
||||
}
|
||||
|
||||
}catch(std::exception& e){
|
||||
fprintf(stderr, "%s: Error parsing info(\"Q:group\", ... : %s\n",
|
||||
rec.record()->name, e.what());
|
||||
}
|
||||
#endif // USE_MULTILOCK
|
||||
#endif
|
||||
}
|
||||
|
||||
// process group definition files
|
||||
for(PDBProvider::group_files_t::const_iterator it(PDBProvider::group_files.begin()), end(PDBProvider::group_files.end());
|
||||
it != end; ++it)
|
||||
{
|
||||
std::ifstream jfile(it->c_str());
|
||||
if(!jfile.is_open()) {
|
||||
fprintf(stderr, "Error opening \"%s\"\n", it->c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<char> contents;
|
||||
size_t pos=0u;
|
||||
while(true) {
|
||||
contents.resize(pos+1024u);
|
||||
if(!jfile.read(&contents[pos], contents.size()-pos))
|
||||
break;
|
||||
pos += jfile.gcount();
|
||||
}
|
||||
|
||||
if(jfile.bad() || !jfile.eof()) {
|
||||
fprintf(stderr, "Error reading \"%s\"\n", it->c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
contents.push_back('\0');
|
||||
const char *json = &contents[0];
|
||||
|
||||
if(PDBProviderDebug>2) {
|
||||
fprintf(stderr, "Process dbGroup file \"%s\"\n", it->c_str());
|
||||
}
|
||||
|
||||
#ifdef USE_MULTILOCK
|
||||
try {
|
||||
GroupConfig::parse(json, "", conf);
|
||||
if(!conf.warning.empty())
|
||||
fprintf(stderr, "warning(s) from dbGroup file \"%s\"\n%s", it->c_str(), conf.warning.c_str());
|
||||
}catch(std::exception& e){
|
||||
fprintf(stderr, "Error from dbGroup file \"%s\"\n%s", it->c_str(), e.what());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_MULTILOCK
|
||||
for(GroupConfig::groups_t::const_iterator git=conf.groups.begin(), gend=conf.groups.end();
|
||||
git!=gend; ++git)
|
||||
{
|
||||
const std::string& grpname = git->first;
|
||||
const GroupConfig::Group& grp = git->second;
|
||||
try {
|
||||
|
||||
if(dbChannelTest(grpname.c_str())==0) {
|
||||
fprintf(stderr, "%s : Error: Group name conflicts with record name. Ignoring...\n", grpname.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
groups_t::iterator it = groups.find(grpname);
|
||||
if(it==groups.end()) {
|
||||
// lazy creation of group
|
||||
std::pair<groups_t::iterator, bool> ins(groups.insert(std::make_pair(grpname, GroupInfo(grpname))));
|
||||
it = ins.first;
|
||||
}
|
||||
GroupInfo *curgroup = &it->second;
|
||||
|
||||
if(!grp.id.empty())
|
||||
curgroup->structID = grp.id;
|
||||
|
||||
for(GroupConfig::Group::fields_t::const_iterator fit=grp.fields.begin(), fend=grp.fields.end();
|
||||
fit!=fend; ++fit)
|
||||
{
|
||||
const std::string& fldname = fit->first;
|
||||
const GroupConfig::Field& fld = fit->second;
|
||||
|
||||
if(curgroup->members_map.find(fldname) != curgroup->members_map.end()) {
|
||||
fprintf(stderr, "%s.%s Warning: ignoring duplicate mapping %s\n",
|
||||
grpname.c_str(), fldname.c_str(),
|
||||
fld.channel.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
curgroup->members.push_back(GroupMemberInfo());
|
||||
GroupMemberInfo& info = curgroup->members.back();
|
||||
info.pvname = fld.channel;
|
||||
info.pvfldname = fldname;
|
||||
info.structID = fld.id;
|
||||
info.putorder = fld.putorder;
|
||||
info.type = fld.type;
|
||||
curgroup->members_map[fldname] = (size_t)-1; // placeholder see below
|
||||
|
||||
if(PDBProviderDebug>2) {
|
||||
fprintf(stderr, " pdb map '%s.%s' <-> '%s'\n",
|
||||
curgroup->name.c_str(),
|
||||
curgroup->members.back().pvfldname.c_str(),
|
||||
curgroup->members.back().pvname.c_str());
|
||||
}
|
||||
|
||||
if(!fld.trigger.empty()) {
|
||||
GroupInfo::triggers_t::iterator it = curgroup->triggers.find(fldname);
|
||||
if(it==curgroup->triggers.end()) {
|
||||
std::pair<GroupInfo::triggers_t::iterator, bool> ins(curgroup->triggers.insert(
|
||||
std::make_pair(fldname, GroupInfo::triggers_set_t())));
|
||||
it = ins.first;
|
||||
}
|
||||
|
||||
Splitter sep(fld.trigger.c_str(), ',');
|
||||
std::string target;
|
||||
|
||||
while(sep.snip(target)) {
|
||||
curgroup->hastriggers = true;
|
||||
it->second.insert(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(grp.atomic_set) {
|
||||
GroupInfo::tribool V = grp.atomic ? GroupInfo::True : GroupInfo::False;
|
||||
|
||||
if(curgroup->atomic!=GroupInfo::Unset && curgroup->atomic!=V)
|
||||
fprintf(stderr, "%s Warning: pdb atomic setting inconsistent '%s'\n",
|
||||
grpname.c_str(), curgroup->name.c_str());
|
||||
|
||||
curgroup->atomic=V;
|
||||
|
||||
if(PDBProviderDebug>2)
|
||||
fprintf(stderr, " pdb atomic '%s' %s\n",
|
||||
curgroup->name.c_str(), curgroup->atomic ? "YES" : "NO");
|
||||
}
|
||||
|
||||
}catch(std::exception& e){
|
||||
fprintf(stderr, "Error processing Q:group \"%s\" : %s\n",
|
||||
grpname.c_str(), e.what());
|
||||
}
|
||||
}
|
||||
|
||||
// re-sort GroupInfo::members to ensure the shorter names appear first
|
||||
@@ -311,6 +358,7 @@ struct PDBProcessor
|
||||
resolveTriggers();
|
||||
// must not re-sort members after this point as resolveTriggers()
|
||||
// has stored array indicies.
|
||||
#endif
|
||||
}
|
||||
|
||||
};
|
||||
@@ -318,6 +366,8 @@ struct PDBProcessor
|
||||
|
||||
size_t PDBProvider::num_instances;
|
||||
|
||||
std::list<std::string> PDBProvider::group_files;
|
||||
|
||||
PDBProvider::PDBProvider(const epics::pvAccess::Configuration::const_shared_pointer &)
|
||||
{
|
||||
/* Long view
|
||||
@@ -405,10 +455,12 @@ PDBProvider::PDBProvider(const epics::pvAccess::Configuration::const_shared_poin
|
||||
chan.swap(temp);
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<PVIFBuilder> pvifbuilder(PVIFBuilder::create(mem.type, chan.chan));
|
||||
|
||||
if(!parts.empty())
|
||||
builder = mem.builder->dtype(builder, parts.back().name, chan);
|
||||
builder = pvifbuilder->dtype(builder, parts.back().name);
|
||||
else
|
||||
builder = mem.builder->dtype(builder, "", chan);
|
||||
builder = pvifbuilder->dtype(builder, "");
|
||||
|
||||
if(!parts.empty()) {
|
||||
for(size_t j=0; j<parts.size()-1; j++)
|
||||
@@ -426,7 +478,7 @@ PDBProvider::PDBProvider(const epics::pvAccess::Configuration::const_shared_poin
|
||||
}
|
||||
|
||||
info.allowProc = mem.putorder != std::numeric_limits<int>::min();
|
||||
info.builder = PTRMOVE(mem.builder);
|
||||
info.builder = PTRMOVE(pvifbuilder);
|
||||
assert(info.builder.get());
|
||||
|
||||
info.attachment.swap(parts);
|
||||
@@ -517,7 +569,7 @@ PDBProvider::PDBProvider(const epics::pvAccess::Configuration::const_shared_poin
|
||||
info.evt_VALUE.self = info.evt_PROPERTY.self = pv;
|
||||
assert(info.chan);
|
||||
|
||||
info.pvif.reset(info.builder->attach(info.chan, pv->complete, info.attachment));
|
||||
info.pvif.reset(info.builder->attach(pv->complete, info.attachment));
|
||||
|
||||
// TODO: don't need evt_PROPERTY for PVIF plain
|
||||
dbChannel *pchan = info.chan2.chan ? info.chan2.chan : info.chan.chan;
|
||||
|
||||
@@ -64,6 +64,9 @@ struct QSRV_API PDBProvider : public epics::pvAccess::ChannelProvider,
|
||||
|
||||
dbEventCtx event_context;
|
||||
|
||||
typedef std::list<std::string> group_files_t;
|
||||
static group_files_t group_files;
|
||||
|
||||
static size_t num_instances;
|
||||
};
|
||||
|
||||
|
||||
+8
-5
@@ -334,7 +334,7 @@ PDBGroupPut::PDBGroupPut(const PDBGroupChannel::shared_pointer& channel,
|
||||
{
|
||||
PDBGroupPV::Info& info = channel->pv->members[i];
|
||||
|
||||
pvif[i].reset(info.builder->attach(info.chan, pvf, info.attachment));
|
||||
pvif[i].reset(info.builder->attach(pvf, info.attachment));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,7 +355,7 @@ void PDBGroupPut::put(pvd::PVStructure::shared_pointer const & value,
|
||||
PDBGroupPV::Info& info = channel->pv->members[i];
|
||||
if(!info.allowProc) continue;
|
||||
|
||||
putpvif[i].reset(info.builder->attach(info.chan, value, info.attachment));
|
||||
putpvif[i].reset(info.builder->attach(value, info.attachment));
|
||||
}
|
||||
|
||||
pvd::Status ret;
|
||||
@@ -394,8 +394,10 @@ void PDBGroupPut::get()
|
||||
changed->clear();
|
||||
if(atomic) {
|
||||
DBManyLocker L(channel->pv->locker);
|
||||
for(size_t i=0; i<npvs; i++)
|
||||
pvif[i]->put(*changed, DBE_VALUE|DBE_ALARM|DBE_PROPERTY, NULL);
|
||||
for(size_t i=0; i<npvs; i++) {
|
||||
LocalFL FL(NULL, channel->pv->members[i].chan);
|
||||
pvif[i]->put(*changed, DBE_VALUE|DBE_ALARM|DBE_PROPERTY, FL.pfl);
|
||||
}
|
||||
} else {
|
||||
|
||||
for(size_t i=0; i<npvs; i++)
|
||||
@@ -403,7 +405,8 @@ void PDBGroupPut::get()
|
||||
PDBGroupPV::Info& info = channel->pv->members[i];
|
||||
|
||||
DBScanLocker L(dbChannelRecord(info.chan));
|
||||
pvif[i]->put(*changed, DBE_VALUE|DBE_ALARM|DBE_PROPERTY, NULL);
|
||||
LocalFL FL(NULL, info.chan);
|
||||
pvif[i]->put(*changed, DBE_VALUE|DBE_ALARM|DBE_PROPERTY, FL.pfl);
|
||||
}
|
||||
}
|
||||
//TODO: report unused fields as changed?
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ struct QSRV_API GroupConfig
|
||||
std::swap(warning, o.warning);
|
||||
}
|
||||
|
||||
static void parse(const char *txt,
|
||||
static void parse(const char *txt, const char *recname,
|
||||
GroupConfig& result);
|
||||
};
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ void pdb_single_event(void *user_arg, struct dbChannel *chan,
|
||||
PDBSinglePV::PDBSinglePV(DBCH& chan,
|
||||
const PDBProvider::shared_pointer& prov)
|
||||
:provider(prov)
|
||||
,builder(new ScalarBuilder)
|
||||
,builder(new ScalarBuilder(chan.chan))
|
||||
,interested_iterating(false)
|
||||
,evt_VALUE(this)
|
||||
,evt_PROPERTY(this)
|
||||
@@ -108,11 +108,11 @@ PDBSinglePV::PDBSinglePV(DBCH& chan,
|
||||
this->chan2.swap(temp);
|
||||
}
|
||||
this->chan.swap(chan);
|
||||
fielddesc = std::tr1::static_pointer_cast<const pvd::Structure>(builder->dtype(this->chan));
|
||||
fielddesc = std::tr1::static_pointer_cast<const pvd::Structure>(builder->dtype());
|
||||
|
||||
complete = pvd::getPVDataCreate()->createPVStructure(fielddesc);
|
||||
FieldName temp;
|
||||
pvif.reset(builder->attach(this->chan, complete, temp));
|
||||
pvif.reset(builder->attach(complete, temp));
|
||||
|
||||
epics::atomic::increment(num_instances);
|
||||
}
|
||||
@@ -308,7 +308,7 @@ PDBSinglePut::PDBSinglePut(const PDBSingleChannel::shared_pointer &channel,
|
||||
,requester(requester)
|
||||
,changed(new pvd::BitSet(channel->fielddesc->getNumberFields()))
|
||||
,pvf(pvd::getPVDataCreate()->createPVStructure(channel->fielddesc))
|
||||
,pvif(channel->pv->builder->attach(channel->pv->chan, pvf, FieldName()))
|
||||
,pvif(channel->pv->builder->attach(pvf, FieldName()))
|
||||
,notifyBusy(0)
|
||||
,doProc(PVIF::ProcPassive)
|
||||
,doWait(false)
|
||||
@@ -375,7 +375,7 @@ void PDBSinglePut::put(pvd::PVStructure::shared_pointer const & value,
|
||||
// TODO: dbNotify doesn't allow us for force processing
|
||||
|
||||
// assume value may be a different struct each time
|
||||
p2p::auto_ptr<PVIF> putpvif(channel->pv->builder->attach(channel->pv->chan, value, FieldName()));
|
||||
p2p::auto_ptr<PVIF> putpvif(channel->pv->builder->attach(value, FieldName()));
|
||||
unsigned mask = putpvif->dbe(*changed);
|
||||
|
||||
if(mask!=DBE_VALUE) {
|
||||
@@ -397,7 +397,7 @@ void PDBSinglePut::put(pvd::PVStructure::shared_pointer const & value,
|
||||
return; // skip notification
|
||||
} else {
|
||||
// assume value may be a different struct each time
|
||||
p2p::auto_ptr<PVIF> putpvif(channel->pv->builder->attach(channel->pv->chan, value, FieldName()));
|
||||
p2p::auto_ptr<PVIF> putpvif(channel->pv->builder->attach(value, FieldName()));
|
||||
try{
|
||||
DBScanLocker L(chan);
|
||||
ret = putpvif->get(*changed, doProc);
|
||||
@@ -426,7 +426,8 @@ void PDBSinglePut::get()
|
||||
changed->clear();
|
||||
{
|
||||
DBScanLocker L(pvif->chan);
|
||||
pvif->put(*changed, DBE_VALUE|DBE_ALARM|DBE_PROPERTY, NULL);
|
||||
LocalFL FL(NULL, pvif->chan);
|
||||
pvif->put(*changed, DBE_VALUE|DBE_ALARM|DBE_PROPERTY, FL.pfl);
|
||||
}
|
||||
//TODO: report unused fields as changed?
|
||||
changed->clear();
|
||||
|
||||
@@ -49,6 +49,9 @@ QSRV_API unsigned qsrvVersion(void);
|
||||
/** returns QSRV_ABI_VERSION_INT captured at compilation time */
|
||||
QSRV_API unsigned qsrvABIVersion(void);
|
||||
|
||||
QSRV_API
|
||||
long dbLoadGroup(const char* fname);
|
||||
|
||||
QSRV_API void testqsrvWaitForLinkEvent(struct link *plink);
|
||||
|
||||
/** Call before testIocShutdownOk()
|
||||
|
||||
@@ -89,7 +89,9 @@ pvd::PVField::const_shared_pointer pvaLink::getSubField(const char *name)
|
||||
} else {
|
||||
// we access a sub-struct
|
||||
ret = lchan->op_mon.root->getSubField(fieldName);
|
||||
if(ret->getField()->getType()!=pvd::structure) {
|
||||
if(!ret) {
|
||||
// noop
|
||||
} else if(ret->getField()->getType()!=pvd::structure) {
|
||||
// addressed sub-field isn't a sub-structure
|
||||
if(strcmp(name, "value")!=0) {
|
||||
// unless we are trying to fetch the "value", we fail here
|
||||
|
||||
+27
-28
@@ -775,7 +775,7 @@ short PVD2DBR(pvd::ScalarType pvt)
|
||||
}
|
||||
|
||||
epics::pvData::FieldConstPtr
|
||||
ScalarBuilder::dtype(dbChannel *channel)
|
||||
ScalarBuilder::dtype()
|
||||
{
|
||||
short dbr = dbChannelFinalFieldType(channel);
|
||||
const long maxelem = dbChannelFinalElements(channel);
|
||||
@@ -830,7 +830,7 @@ ScalarBuilder::dtype(dbChannel *channel)
|
||||
}
|
||||
|
||||
PVIF*
|
||||
ScalarBuilder::attach(dbChannel *channel, const epics::pvData::PVStructurePtr& root, const FieldName& fldname)
|
||||
ScalarBuilder::attach(const epics::pvData::PVStructurePtr& root, const FieldName& fldname)
|
||||
{
|
||||
if(!channel)
|
||||
throw std::runtime_error("+type:\"scalar\" requires +channel:");
|
||||
@@ -950,10 +950,11 @@ struct PVIFPlain : public PVIF
|
||||
|
||||
struct PlainBuilder : public PVIFBuilder
|
||||
{
|
||||
explicit PlainBuilder(dbChannel* chan) :PVIFBuilder(chan) {}
|
||||
virtual ~PlainBuilder() {}
|
||||
|
||||
// fetch the structure description
|
||||
virtual epics::pvData::FieldConstPtr dtype(dbChannel *channel) OVERRIDE FINAL {
|
||||
virtual epics::pvData::FieldConstPtr dtype() OVERRIDE FINAL {
|
||||
const short dbr = dbChannelFinalFieldType(channel);
|
||||
const long maxelem = dbChannelFinalElements(channel);
|
||||
const pvd::ScalarType pvt = DBR2PVD(dbr);
|
||||
@@ -970,8 +971,7 @@ struct PlainBuilder : public PVIFBuilder
|
||||
// Attach to a structure instance.
|
||||
// must be of the type returned by dtype().
|
||||
// need not be the root structure
|
||||
virtual PVIF* attach(dbChannel *channel,
|
||||
const epics::pvData::PVStructurePtr& root,
|
||||
virtual PVIF* attach(const epics::pvData::PVStructurePtr& root,
|
||||
const FieldName& fldname) OVERRIDE FINAL
|
||||
{
|
||||
if(!channel)
|
||||
@@ -990,10 +990,11 @@ struct PlainBuilder : public PVIFBuilder
|
||||
|
||||
struct AnyScalarBuilder : public PVIFBuilder
|
||||
{
|
||||
explicit AnyScalarBuilder(dbChannel* chan) :PVIFBuilder(chan) {}
|
||||
virtual ~AnyScalarBuilder() {}
|
||||
|
||||
// fetch the structure description
|
||||
virtual epics::pvData::FieldConstPtr dtype(dbChannel *channel) OVERRIDE FINAL {
|
||||
virtual epics::pvData::FieldConstPtr dtype() OVERRIDE FINAL {
|
||||
(void)channel; //ignored
|
||||
return pvd::getFieldCreate()->createVariantUnion();
|
||||
}
|
||||
@@ -1001,8 +1002,7 @@ struct AnyScalarBuilder : public PVIFBuilder
|
||||
// Attach to a structure instance.
|
||||
// must be of the type returned by dtype().
|
||||
// need not be the root structure
|
||||
virtual PVIF* attach(dbChannel *channel,
|
||||
const epics::pvData::PVStructurePtr& root,
|
||||
virtual PVIF* attach(const epics::pvData::PVStructurePtr& root,
|
||||
const FieldName& fldname) OVERRIDE FINAL
|
||||
{
|
||||
if(!channel)
|
||||
@@ -1088,16 +1088,16 @@ struct PVIFMeta : public PVIF
|
||||
|
||||
struct MetaBuilder : public PVIFBuilder
|
||||
{
|
||||
explicit MetaBuilder(dbChannel* chan) :PVIFBuilder(chan) {}
|
||||
virtual ~MetaBuilder() {}
|
||||
|
||||
// fetch the structure description
|
||||
virtual epics::pvData::FieldConstPtr dtype(dbChannel *channel) OVERRIDE FINAL {
|
||||
virtual epics::pvData::FieldConstPtr dtype() OVERRIDE FINAL {
|
||||
throw std::logic_error("Don't call me");
|
||||
}
|
||||
|
||||
virtual epics::pvData::FieldBuilderPtr dtype(epics::pvData::FieldBuilderPtr& builder,
|
||||
const std::string& fld,
|
||||
dbChannel *channel)
|
||||
const std::string& fld) OVERRIDE FINAL
|
||||
{
|
||||
pvd::StandardFieldPtr std(pvd::getStandardField());
|
||||
if(fld.empty()) {
|
||||
@@ -1114,8 +1114,7 @@ struct MetaBuilder : public PVIFBuilder
|
||||
// Attach to a structure instance.
|
||||
// must be of the type returned by dtype().
|
||||
// need not be the root structure
|
||||
virtual PVIF* attach(dbChannel *channel,
|
||||
const epics::pvData::PVStructurePtr& root,
|
||||
virtual PVIF* attach(const epics::pvData::PVStructurePtr& root,
|
||||
const FieldName& fldname) OVERRIDE FINAL
|
||||
{
|
||||
if(!channel)
|
||||
@@ -1152,20 +1151,21 @@ struct PVIFProc : public PVIF
|
||||
|
||||
struct ProcBuilder : public PVIFBuilder
|
||||
{
|
||||
explicit ProcBuilder(dbChannel* chan) :PVIFBuilder(chan) {}
|
||||
virtual ~ProcBuilder() {}
|
||||
|
||||
// fetch the structure description
|
||||
virtual epics::pvData::FieldConstPtr dtype(dbChannel *channel) OVERRIDE FINAL {
|
||||
virtual epics::pvData::FieldConstPtr dtype() OVERRIDE FINAL {
|
||||
throw std::logic_error("Don't call me");
|
||||
}
|
||||
|
||||
virtual epics::pvData::FieldBuilderPtr dtype(epics::pvData::FieldBuilderPtr& builder,
|
||||
const std::string& fld,
|
||||
dbChannel *channel) OVERRIDE FINAL
|
||||
const std::string& fld) OVERRIDE FINAL
|
||||
{
|
||||
// invisible
|
||||
return builder;
|
||||
}
|
||||
virtual PVIF* attach(dbChannel *channel,
|
||||
const epics::pvData::PVStructurePtr& root,
|
||||
virtual PVIF* attach(const epics::pvData::PVStructurePtr& root,
|
||||
const FieldName& fldname) OVERRIDE FINAL
|
||||
{
|
||||
if(!channel)
|
||||
@@ -1216,31 +1216,30 @@ pvd::Status PVIF::get(const epics::pvData::BitSet& mask, proc_t proc, bool permi
|
||||
|
||||
epics::pvData::FieldBuilderPtr
|
||||
PVIFBuilder::dtype(epics::pvData::FieldBuilderPtr& builder,
|
||||
const std::string &fld,
|
||||
dbChannel *channel)
|
||||
const std::string &fld)
|
||||
{
|
||||
if(fld.empty())
|
||||
throw std::runtime_error("Can't attach this +type to root");
|
||||
throw std::runtime_error(SB()<<"Can't attach +type "<<typeid(*this).name()<<" to root");
|
||||
|
||||
epics::pvData::FieldConstPtr ftype(this->dtype(channel));
|
||||
epics::pvData::FieldConstPtr ftype(this->dtype());
|
||||
if(ftype)
|
||||
builder = builder->add(fld, ftype);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
PVIFBuilder* PVIFBuilder::create(const std::string& type)
|
||||
PVIFBuilder* PVIFBuilder::create(const std::string& type, dbChannel* chan)
|
||||
{
|
||||
if(type.empty() || type=="scalar")
|
||||
return new ScalarBuilder;
|
||||
return new ScalarBuilder(chan);
|
||||
else if(type=="plain")
|
||||
return new PlainBuilder;
|
||||
return new PlainBuilder(chan);
|
||||
else if(type=="any")
|
||||
return new AnyScalarBuilder;
|
||||
return new AnyScalarBuilder(chan);
|
||||
else if(type=="meta")
|
||||
return new MetaBuilder;
|
||||
return new MetaBuilder(chan);
|
||||
else if(type=="proc")
|
||||
return new ProcBuilder;
|
||||
return new ProcBuilder(chan);
|
||||
else
|
||||
throw std::runtime_error(std::string("Unknown +type=")+type);
|
||||
}
|
||||
|
||||
+12
-9
@@ -254,6 +254,7 @@ struct LocalFL
|
||||
if(pfl) pfl = dbChannelRunPostChain(pchan, pfl);
|
||||
}
|
||||
}
|
||||
this->pfl = pfl;
|
||||
}
|
||||
~LocalFL() {
|
||||
if(ours) db_delete_field_log(pfl);
|
||||
@@ -390,26 +391,27 @@ private:
|
||||
* Caller than creates a PVStructure and uses PVIFBuilder::attach() to
|
||||
* build mappings for each dbChannel in the composed locations.
|
||||
*/
|
||||
struct QSRV_API PVIFBuilder {
|
||||
struct QSRV_API PVIFBuilder
|
||||
{
|
||||
dbChannel* const channel;
|
||||
|
||||
virtual ~PVIFBuilder() {}
|
||||
|
||||
// fetch the structure description
|
||||
virtual epics::pvData::FieldConstPtr dtype(dbChannel *channel) =0;
|
||||
virtual epics::pvData::FieldConstPtr dtype() =0;
|
||||
|
||||
virtual epics::pvData::FieldBuilderPtr dtype(epics::pvData::FieldBuilderPtr& builder,
|
||||
const std::string& fld,
|
||||
dbChannel *channel);
|
||||
const std::string& fld);
|
||||
|
||||
// Attach to a structure instance.
|
||||
// must be of the type returned by dtype().
|
||||
// must be the root structure
|
||||
virtual PVIF* attach(dbChannel *channel, const epics::pvData::PVStructurePtr& root, const FieldName& fld) =0;
|
||||
virtual PVIF* attach(const epics::pvData::PVStructurePtr& root, const FieldName& fld) =0;
|
||||
|
||||
// entry point for Builder
|
||||
static PVIFBuilder* create(const std::string& name);
|
||||
static PVIFBuilder* create(const std::string& mapname, dbChannel* chan);
|
||||
protected:
|
||||
PVIFBuilder() {}
|
||||
explicit PVIFBuilder(dbChannel* chan) : channel(chan) {}
|
||||
private:
|
||||
PVIFBuilder(const PVIFBuilder&);
|
||||
PVIFBuilder& operator=(const PVIFBuilder&);
|
||||
@@ -417,10 +419,11 @@ private:
|
||||
|
||||
struct QSRV_API ScalarBuilder : public PVIFBuilder
|
||||
{
|
||||
explicit ScalarBuilder(dbChannel* chan) :PVIFBuilder(chan) {}
|
||||
virtual ~ScalarBuilder() {}
|
||||
|
||||
virtual epics::pvData::FieldConstPtr dtype(dbChannel *channel) OVERRIDE FINAL;
|
||||
virtual PVIF* attach(dbChannel *channel, const epics::pvData::PVStructurePtr& root, const FieldName& fld) OVERRIDE FINAL;
|
||||
virtual epics::pvData::FieldConstPtr dtype() OVERRIDE FINAL;
|
||||
virtual PVIF* attach(const epics::pvData::PVStructurePtr& root, const FieldName& fld) OVERRIDE FINAL;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -46,8 +46,49 @@ void QSRVRegistrar_counters()
|
||||
epics::registerRefCounter("PDBProvider", &PDBProvider::num_instances);
|
||||
}
|
||||
|
||||
long dbLoadGroup(const char* fname)
|
||||
{
|
||||
try {
|
||||
if(!fname) {
|
||||
printf("dbLoadGroup(\"file.json\")\n"
|
||||
"\n"
|
||||
"Load additional DB group definitions from file.\n");
|
||||
return 1;
|
||||
}
|
||||
#ifndef USE_MULTILOCK
|
||||
static bool warned;
|
||||
if(!warned) {
|
||||
warned = true;
|
||||
fprintf(stderr, "ignoring %s\n", fname);
|
||||
}
|
||||
#endif
|
||||
|
||||
if(fname[0]=='-') {
|
||||
fname++;
|
||||
if(fname[0]=='*' && fname[1]=='\0') {
|
||||
PDBProvider::group_files.clear();
|
||||
} else {
|
||||
PDBProvider::group_files.remove(fname);
|
||||
}
|
||||
} else {
|
||||
PDBProvider::group_files.remove(fname);
|
||||
PDBProvider::group_files.push_back(fname);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}catch(std::exception& e){
|
||||
fprintf(stderr, "Error: %s\n", e.what());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void dbLoadGroupWrap(const char* fname)
|
||||
{
|
||||
(void)dbLoadGroup(fname);
|
||||
}
|
||||
|
||||
void dbgl(int lvl, const char *pattern)
|
||||
{
|
||||
if(!pattern)
|
||||
@@ -88,6 +129,7 @@ void QSRVRegistrar()
|
||||
QSRVRegistrar_counters();
|
||||
pva::ChannelProviderRegistry::servers()->addSingleton<PDBProvider>("QSRV");
|
||||
epics::iocshRegister<int, const char*, &dbgl>("dbgl", "level", "pattern");
|
||||
epics::iocshRegister<const char*, &dbLoadGroupWrap>("dbLoadGroup", "jsonfile");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
+39
-15
@@ -29,6 +29,8 @@
|
||||
#include "iocsh.h"
|
||||
#include "osiFileName.h"
|
||||
|
||||
#include <pv/qsrv.h>
|
||||
|
||||
extern "C" int softIocPVA_registerRecordDeviceDriver(struct dbBase *pdbbase);
|
||||
|
||||
#ifndef EPICS_BASE
|
||||
@@ -50,13 +52,15 @@ extern "C" int softIocPVA_registerRecordDeviceDriver(struct dbBase *pdbbase);
|
||||
|
||||
namespace {
|
||||
|
||||
bool verbose = false;
|
||||
|
||||
static void exitSubroutine(subRecord *precord) {
|
||||
epicsExitLater((precord->a == 0.0) ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void usage(const char *arg0, const std::string& base_dbd) {
|
||||
std::cout<<"Usage: "<<arg0<<
|
||||
" [-D softIocPVA.dbd] [-h] [-S] [-s] [-a ascf]\n"
|
||||
" [-D softIocPVA.dbd] [-h] [-S] [-s] [-v] [-a ascf]\n"
|
||||
"[-m macro=value,macro2=value2] [-d file.db]\n"
|
||||
"[-x prefix] [st.cmd]\n"
|
||||
"\n"
|
||||
@@ -69,9 +73,13 @@ void usage(const char *arg0, const std::string& base_dbd) {
|
||||
"\n"
|
||||
" -s Previously caused a shell to be started. Now accepted and ignored.\n"
|
||||
"\n"
|
||||
" -v Verbose, display steps taken during startup.\n"
|
||||
"\n"
|
||||
" -a <acf> Access Security configuration file. Macro substitution is\n"
|
||||
" performed.\n"
|
||||
"\n"
|
||||
" -G <json> DB Group definition file in JSON format.\n"
|
||||
"\n"
|
||||
" -m <MAC>=<value>,... Set/replace macro definitions used by subsequent -d and\n"
|
||||
" -a.\n"
|
||||
"\n"
|
||||
@@ -106,12 +114,14 @@ void lazy_dbd(const std::string& dbd_file) {
|
||||
if(lazy_dbd_loaded) return;
|
||||
lazy_dbd_loaded = true;
|
||||
|
||||
if (verbose)
|
||||
std::cout<<"dbLoadDatabase(\""<<dbd_file<<"\")\n";
|
||||
errIf(dbLoadDatabase(dbd_file.c_str(), NULL, NULL),
|
||||
std::string("Failed to load DBD file: ")+dbd_file);
|
||||
std::cout<<"dbLoadDatabase(\""<<dbd_file<<"\")\n";
|
||||
|
||||
if (verbose)
|
||||
std::cout<<"softIocPVA_registerRecordDeviceDriver(pdbbase)\n";
|
||||
softIocPVA_registerRecordDeviceDriver(pdbbase);
|
||||
std::cout<<"softIocPVA_registerRecordDeviceDriver(pdbbase)\n";
|
||||
registryFunctionAdd("exit", (REGISTRYFUNCTION) exitSubroutine);
|
||||
}
|
||||
|
||||
@@ -126,6 +136,7 @@ int main(int argc, char *argv[])
|
||||
xmacro;
|
||||
bool interactive = true;
|
||||
bool loadedDb = false;
|
||||
bool ranScript = false;
|
||||
|
||||
#ifdef USE_EXECDIR
|
||||
// attempt to compute relative paths
|
||||
@@ -149,7 +160,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
int opt;
|
||||
|
||||
while ((opt = getopt(argc, argv, "ha:D:d:m:Ssx:")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "ha:D:d:m:Ssx:G:v")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h': /* Print usage */
|
||||
usage(argv[0], dbd_file);
|
||||
@@ -163,13 +174,15 @@ int main(int argc, char *argv[])
|
||||
case 'a':
|
||||
lazy_dbd(dbd_file);
|
||||
if (!macros.empty()) {
|
||||
if (verbose)
|
||||
std::cout<<"asSetSubstitutions(\""<<macros<<"\")\n";
|
||||
if(asSetSubstitutions(macros.c_str()))
|
||||
throw std::bad_alloc();
|
||||
std::cout<<"asSetSubstitutions(\""<<macros<<"\")\n";
|
||||
}
|
||||
if (verbose)
|
||||
std::cout<<"asSetFilename(\""<<optarg<<"\")\n";
|
||||
if(asSetFilename(optarg))
|
||||
throw std::bad_alloc();
|
||||
std::cout<<"asSetFilename(\""<<optarg<<"\")\n";
|
||||
break;
|
||||
case 'D':
|
||||
if(lazy_dbd_loaded) {
|
||||
@@ -179,12 +192,14 @@ int main(int argc, char *argv[])
|
||||
break;
|
||||
case 'd':
|
||||
lazy_dbd(dbd_file);
|
||||
if (verbose) {
|
||||
std::cout<<"dbLoadRecords(\""<<optarg<<"\"";
|
||||
if(!macros.empty())
|
||||
std::cout<<", \""<<macros<<"\"";
|
||||
std::cout<<")\n";
|
||||
}
|
||||
errIf(dbLoadRecords(optarg, macros.c_str()),
|
||||
std::string("Failed to load: ")+optarg);
|
||||
std::cout<<"dbLoadRecords(\""<<optarg<<"\"";
|
||||
if(!macros.empty())
|
||||
std::cout<<", \""<<macros<<"\"";
|
||||
std::cout<<")\n";
|
||||
loadedDb = true;
|
||||
break;
|
||||
case 'm':
|
||||
@@ -195,6 +210,9 @@ int main(int argc, char *argv[])
|
||||
break;
|
||||
case 's':
|
||||
break; // historical
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
case 'x':
|
||||
lazy_dbd(dbd_file);
|
||||
xmacro = "IOC=";
|
||||
@@ -203,6 +221,9 @@ int main(int argc, char *argv[])
|
||||
std::string("Failed to load: ")+exit_file);
|
||||
loadedDb = true;
|
||||
break;
|
||||
case 'G':
|
||||
dbLoadGroup(optarg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,17 +233,20 @@ int main(int argc, char *argv[])
|
||||
// run script
|
||||
// ignore any extra positional args (historical)
|
||||
|
||||
std::cout<<"# Begin "<<argv[optind]<<"\n";
|
||||
if (verbose)
|
||||
std::cout<<"# Begin "<<argv[optind]<<"\n";
|
||||
errIf(iocsh(argv[optind]),
|
||||
std::string("Error in ")+argv[optind]);
|
||||
std::cout<<"# End "<<argv[optind]<<"\n";
|
||||
if (verbose)
|
||||
std::cout<<"# End "<<argv[optind]<<"\n";
|
||||
|
||||
epicsThreadSleep(0.2);
|
||||
loadedDb = true; /* Give it the benefit of the doubt... */
|
||||
ranScript = true; /* Assume the script has done any necessary initialization */
|
||||
}
|
||||
|
||||
if (loadedDb) {
|
||||
std::cout<<"iocInit()\n";
|
||||
if (verbose)
|
||||
std::cout<<"iocInit()\n";
|
||||
iocInit();
|
||||
epicsThreadSleep(0.2);
|
||||
}
|
||||
@@ -236,7 +260,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
} else {
|
||||
if (loadedDb) {
|
||||
if (loadedDb || ranScript) {
|
||||
epicsThreadExitMain();
|
||||
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
record (waveform, "TEST") {
|
||||
field(FTVL, "SHORT")
|
||||
field(NELM, "10")
|
||||
}
|
||||
@@ -18,6 +18,7 @@ void test_parse()
|
||||
" \"+atomic\":false,\n"
|
||||
" \"fld\":{\n"
|
||||
" \"+type\": \"simple\","
|
||||
" \"+channel\": \"VAL\","
|
||||
" \"+putorder\": -4"
|
||||
" },\n"
|
||||
" \"\":{\n"
|
||||
@@ -29,7 +30,7 @@ void test_parse()
|
||||
"}";
|
||||
|
||||
GroupConfig conf;
|
||||
GroupConfig::parse(txt, conf);
|
||||
GroupConfig::parse(txt, "rec", conf);
|
||||
|
||||
testOk(conf.warning.empty(), "Warnings: %s", conf.warning.c_str());
|
||||
|
||||
@@ -37,7 +38,7 @@ void test_parse()
|
||||
testOk1(conf.groups["grpa"].atomic_set);
|
||||
|
||||
testEqual(conf.groups["grpa"].fields["fld"].type, "simple");
|
||||
testEqual(conf.groups["grpa"].fields["fld"].channel, "");
|
||||
testEqual(conf.groups["grpa"].fields["fld"].channel, "rec.VAL");
|
||||
testEqual(conf.groups["grpa"].fields["fld"].putorder, -4);
|
||||
|
||||
testEqual(conf.groups["grpa"].fields[""].type, "top");
|
||||
@@ -49,15 +50,15 @@ void test_fail()
|
||||
|
||||
{
|
||||
GroupConfig conf;
|
||||
testThrows(std::runtime_error, GroupConfig::parse("{", conf));
|
||||
testThrows(std::runtime_error, GroupConfig::parse("{", "", conf));
|
||||
}
|
||||
{
|
||||
GroupConfig conf;
|
||||
testThrows(std::runtime_error, GroupConfig::parse("{\"G\":{\"F\":{\"K\":{}}}}", conf));
|
||||
testThrows(std::runtime_error, GroupConfig::parse("{\"G\":{\"F\":{\"K\":{}}}}", "", conf));
|
||||
}
|
||||
{
|
||||
GroupConfig conf;
|
||||
testThrows(std::runtime_error, GroupConfig::parse("{\"G\":5}", conf));
|
||||
testThrows(std::runtime_error, GroupConfig::parse("{\"G\":5}", "", conf));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+42
-1
@@ -292,6 +292,45 @@ void testGroupMonitorTriggers(pvac::ClientProvider& client)
|
||||
#endif
|
||||
}
|
||||
|
||||
void testFilters(pvac::ClientProvider& client)
|
||||
{
|
||||
testDiag("test w/ server side filters");
|
||||
|
||||
pvd::shared_vector<const pvd::int16> expected;
|
||||
{
|
||||
pvd::shared_vector<pvd::int16> scratch(9);
|
||||
scratch[0] = 9;
|
||||
scratch[1] = 8;
|
||||
scratch[2] = 7;
|
||||
scratch[3] = 6;
|
||||
scratch[4] = 5;
|
||||
scratch[5] = 4;
|
||||
scratch[6] = 3;
|
||||
scratch[7] = 2;
|
||||
scratch[8] = 1;
|
||||
expected = pvd::freeze(scratch);
|
||||
}
|
||||
|
||||
client.connect("TEST").put().set("value", expected).exec();
|
||||
|
||||
pvd::PVStructure::const_shared_pointer root(client.connect("TEST").get());
|
||||
|
||||
testFieldEqual<pvd::PVShortArray>(root, "value", expected);
|
||||
|
||||
root = client.connect("TEST.{\"arr\":{\"i\":2}}").get();
|
||||
{
|
||||
pvd::shared_vector<pvd::int16> scratch(5);
|
||||
scratch[0] = 9;
|
||||
scratch[1] = 7;
|
||||
scratch[2] = 5;
|
||||
scratch[3] = 3;
|
||||
scratch[4] = 1;
|
||||
expected = pvd::freeze(scratch);
|
||||
}
|
||||
|
||||
testFieldEqual<pvd::PVShortArray>(root, "value", expected);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C"
|
||||
@@ -299,7 +338,7 @@ void p2pTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
MAIN(testpdb)
|
||||
{
|
||||
testPlan(93);
|
||||
testPlan(95);
|
||||
try{
|
||||
QSRVRegistrar_counters();
|
||||
epics::RefSnapshot ref_before;
|
||||
@@ -318,6 +357,7 @@ MAIN(testpdb)
|
||||
#ifdef USE_MULTILOCK
|
||||
testdbReadDatabase("testpdb-groups.db", NULL, NULL);
|
||||
#endif
|
||||
testdbReadDatabase("testfilters.db", NULL, NULL);
|
||||
|
||||
IOC.init();
|
||||
|
||||
@@ -333,6 +373,7 @@ MAIN(testpdb)
|
||||
testSingleMonitor(client);
|
||||
testGroupMonitor(client);
|
||||
testGroupMonitorTriggers(client);
|
||||
testFilters(client);
|
||||
|
||||
testEqual(epics::atomic::get(PDBProvider::num_instances), 1u);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ record(ai, "target:ai") {
|
||||
}
|
||||
|
||||
record(int64in, "src:i1") {
|
||||
field(INP, {pva:"target:i"})
|
||||
field(INP, {"pva":"target:i"})
|
||||
}
|
||||
|
||||
# used by testPut()
|
||||
@@ -17,5 +17,5 @@ record(int64in, "target:i2") {
|
||||
}
|
||||
|
||||
record(int64out, "src:o2") {
|
||||
field(OUT, {pva:"target:i2"})
|
||||
field(OUT, {"pva":"target:i2"})
|
||||
}
|
||||
|
||||
+136
-48
@@ -74,38 +74,56 @@ void testScalar()
|
||||
testEqual(dbChannelFinalFieldType(chan_i64), DBR_INT64);
|
||||
#endif
|
||||
|
||||
ScalarBuilder builder;
|
||||
|
||||
pvd::FieldConstPtr dtype_li(builder.dtype(chan_li));
|
||||
pvd::PVStructurePtr root;
|
||||
p2p::auto_ptr<PVIF> pvif_li;
|
||||
#ifdef USE_INT64
|
||||
pvd::FieldConstPtr dtype_i64(builder.dtype(chan_i64));
|
||||
p2p::auto_ptr<PVIF> pvif_i64;
|
||||
#endif
|
||||
pvd::FieldConstPtr dtype_si(builder.dtype(chan_si));
|
||||
pvd::FieldConstPtr dtype_ai(builder.dtype(chan_ai));
|
||||
pvd::FieldConstPtr dtype_ai_rval(builder.dtype(chan_ai_rval));
|
||||
pvd::FieldConstPtr dtype_mbbi(builder.dtype(chan_mbbi));
|
||||
|
||||
pvd::StructureConstPtr dtype_root(pvd::getFieldCreate()->createFieldBuilder()
|
||||
->add("li", dtype_li)
|
||||
p2p::auto_ptr<PVIF> pvif_si;
|
||||
p2p::auto_ptr<PVIF> pvif_ai;
|
||||
p2p::auto_ptr<PVIF> pvif_ai_rval;
|
||||
p2p::auto_ptr<PVIF> pvif_mbbi;
|
||||
{
|
||||
ScalarBuilder builder_li(chan_li);
|
||||
#ifdef USE_INT64
|
||||
->add("i64", dtype_i64)
|
||||
ScalarBuilder builder_i64(chan_i64);
|
||||
#endif
|
||||
->add("si", dtype_si)
|
||||
->add("ai", dtype_ai)
|
||||
->add("ai_rval", dtype_ai_rval)
|
||||
->add("mbbi", dtype_mbbi)
|
||||
->createStructure());
|
||||
ScalarBuilder builder_si(chan_si);
|
||||
ScalarBuilder builder_ai(chan_ai);
|
||||
ScalarBuilder builder_ai_rval(chan_ai_rval);
|
||||
ScalarBuilder builder_mbbi(chan_mbbi);
|
||||
|
||||
pvd::PVStructurePtr root(pvd::getPVDataCreate()->createPVStructure(dtype_root));
|
||||
pvd::FieldConstPtr dtype_li(builder_li.dtype());
|
||||
#ifdef USE_INT64
|
||||
pvd::FieldConstPtr dtype_i64(builder_i64.dtype());
|
||||
#endif
|
||||
pvd::FieldConstPtr dtype_si(builder_si.dtype());
|
||||
pvd::FieldConstPtr dtype_ai(builder_ai.dtype());
|
||||
pvd::FieldConstPtr dtype_ai_rval(builder_ai_rval.dtype());
|
||||
pvd::FieldConstPtr dtype_mbbi(builder_mbbi.dtype());
|
||||
|
||||
p2p::auto_ptr<PVIF> pvif_li(builder.attach(chan_li, root, FieldName("li")));
|
||||
#ifdef USE_INT64
|
||||
p2p::auto_ptr<PVIF> pvif_i64(builder.attach(chan_i64, root, FieldName("i64")));
|
||||
#endif
|
||||
p2p::auto_ptr<PVIF> pvif_si(builder.attach(chan_si, root, FieldName("si")));
|
||||
p2p::auto_ptr<PVIF> pvif_ai(builder.attach(chan_ai, root, FieldName("ai")));
|
||||
p2p::auto_ptr<PVIF> pvif_ai_rval(builder.attach(chan_ai_rval, root, FieldName("ai_rval")));
|
||||
p2p::auto_ptr<PVIF> pvif_mbbi(builder.attach(chan_mbbi, root, FieldName("mbbi")));
|
||||
pvd::StructureConstPtr dtype_root(pvd::getFieldCreate()->createFieldBuilder()
|
||||
->add("li", dtype_li)
|
||||
#ifdef USE_INT64
|
||||
->add("i64", dtype_i64)
|
||||
#endif
|
||||
->add("si", dtype_si)
|
||||
->add("ai", dtype_ai)
|
||||
->add("ai_rval", dtype_ai_rval)
|
||||
->add("mbbi", dtype_mbbi)
|
||||
->createStructure());
|
||||
|
||||
root = pvd::getPVDataCreate()->createPVStructure(dtype_root);
|
||||
|
||||
pvif_li.reset(builder_li.attach(root, FieldName("li")));
|
||||
#ifdef USE_INT64
|
||||
pvif_i64.reset(builder_i64.attach(root, FieldName("i64")));
|
||||
#endif
|
||||
pvif_si.reset(builder_si.attach(root, FieldName("si")));
|
||||
pvif_ai.reset(builder_ai.attach(root, FieldName("ai")));
|
||||
pvif_ai_rval.reset(builder_ai_rval.attach(root, FieldName("ai_rval")));
|
||||
pvif_mbbi.reset(builder_mbbi.attach(root, FieldName("mbbi")));
|
||||
}
|
||||
|
||||
testShow()<<"Entire structure\n"<<root;
|
||||
|
||||
@@ -424,30 +442,37 @@ void testPlain()
|
||||
DBCH chan_ai("test:ai");
|
||||
DBCH chan_mbbi("test:mbbi");
|
||||
|
||||
p2p::auto_ptr<PVIFBuilder> builder;
|
||||
pvd::PVStructurePtr root;
|
||||
p2p::auto_ptr<PVIF> pvif_li;
|
||||
p2p::auto_ptr<PVIF> pvif_si;
|
||||
p2p::auto_ptr<PVIF> pvif_ai;
|
||||
p2p::auto_ptr<PVIF> pvif_mbbi;
|
||||
{
|
||||
builder.reset(PVIFBuilder::create("plain"));
|
||||
p2p::auto_ptr<PVIFBuilder> builder_li(PVIFBuilder::create("plain", chan_li));
|
||||
p2p::auto_ptr<PVIFBuilder> builder_si(PVIFBuilder::create("plain", chan_si));
|
||||
p2p::auto_ptr<PVIFBuilder> builder_ai(PVIFBuilder::create("plain", chan_ai));
|
||||
p2p::auto_ptr<PVIFBuilder> builder_mbbi(PVIFBuilder::create("plain", chan_mbbi));
|
||||
|
||||
pvd::FieldConstPtr dtype_li(builder_li->dtype());
|
||||
pvd::FieldConstPtr dtype_si(builder_si->dtype());
|
||||
pvd::FieldConstPtr dtype_ai(builder_ai->dtype());
|
||||
pvd::FieldConstPtr dtype_mbbi(builder_mbbi->dtype());
|
||||
|
||||
pvd::StructureConstPtr dtype_root(pvd::getFieldCreate()->createFieldBuilder()
|
||||
->add("li", dtype_li)
|
||||
->add("si", dtype_si)
|
||||
->add("ai", dtype_ai)
|
||||
->add("mbbi", dtype_mbbi)
|
||||
->createStructure());
|
||||
|
||||
root = pvd::getPVDataCreate()->createPVStructure(dtype_root);
|
||||
|
||||
pvif_li.reset(builder_li->attach(root, FieldName("li")));
|
||||
pvif_si.reset(builder_si->attach(root, FieldName("si")));
|
||||
pvif_ai.reset(builder_ai->attach(root, FieldName("ai")));
|
||||
pvif_mbbi.reset(builder_mbbi->attach(root, FieldName("mbbi")));
|
||||
}
|
||||
|
||||
pvd::FieldConstPtr dtype_li(builder->dtype(chan_li));
|
||||
pvd::FieldConstPtr dtype_si(builder->dtype(chan_si));
|
||||
pvd::FieldConstPtr dtype_ai(builder->dtype(chan_ai));
|
||||
pvd::FieldConstPtr dtype_mbbi(builder->dtype(chan_mbbi));
|
||||
|
||||
pvd::StructureConstPtr dtype_root(pvd::getFieldCreate()->createFieldBuilder()
|
||||
->add("li", dtype_li)
|
||||
->add("si", dtype_si)
|
||||
->add("ai", dtype_ai)
|
||||
->add("mbbi", dtype_mbbi)
|
||||
->createStructure());
|
||||
|
||||
pvd::PVStructurePtr root(pvd::getPVDataCreate()->createPVStructure(dtype_root));
|
||||
|
||||
p2p::auto_ptr<PVIF> pvif_li(builder->attach(chan_li, root, FieldName("li")));
|
||||
p2p::auto_ptr<PVIF> pvif_si(builder->attach(chan_si, root, FieldName("si")));
|
||||
p2p::auto_ptr<PVIF> pvif_ai(builder->attach(chan_ai, root, FieldName("ai")));
|
||||
p2p::auto_ptr<PVIF> pvif_mbbi(builder->attach(chan_mbbi, root, FieldName("mbbi")));
|
||||
|
||||
pvd::BitSet mask;
|
||||
|
||||
mask.clear();
|
||||
@@ -521,11 +546,73 @@ void testPlain()
|
||||
dbScanUnlock((dbCommon*)prec_mbbi);
|
||||
}
|
||||
|
||||
void testFilters()
|
||||
{
|
||||
testDiag("testFilter");
|
||||
|
||||
#if EPICS_VERSION_INT < VERSION_INT(7, 0, 0, 0)
|
||||
testSkip(5, "Needs Base >=7.0");
|
||||
#else
|
||||
|
||||
TestIOC IOC;
|
||||
|
||||
testdbReadDatabase("p2pTestIoc.dbd", NULL, NULL);
|
||||
p2pTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("testfilters.db", NULL, NULL);
|
||||
|
||||
IOC.init();
|
||||
|
||||
static const epicsInt32 arr[] = {9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
testdbPutArrFieldOk("TEST", DBF_LONG, 9, arr);
|
||||
|
||||
#if EPICS_VERSION_INT > VERSION_INT(7, 0, 5, 0)
|
||||
testdbGetArrFieldEqual("TEST", DBF_LONG, 10, 9, arr);
|
||||
testdbGetArrFieldEqual("TEST.{\"arr\":{\"s\":5}}", DBF_LONG, 10, 4, arr+5);
|
||||
|
||||
static const epicsInt32 arr2[] = {9, 7, 5, 3, 1};
|
||||
testdbGetArrFieldEqual("TEST.{\"arr\":{\"i\":2}}", DBF_LONG, 10, 5, arr2);
|
||||
|
||||
#else
|
||||
testSkip(3, "dbUnitTest doesn't use dbChannel");
|
||||
#endif
|
||||
|
||||
pvd::PVStructurePtr root;
|
||||
p2p::auto_ptr<PVIF> pvif;
|
||||
|
||||
DBCH chan("TEST.{\"arr\":{\"i\":2}}");
|
||||
ScalarBuilder builder(chan);
|
||||
|
||||
root = pvd::FieldBuilder::begin()
|
||||
->add("dut", builder.dtype())
|
||||
->createStructure()->build();
|
||||
|
||||
pvif.reset(builder.attach(root, FieldName("dut")));
|
||||
|
||||
LocalFL fl(0, chan.chan);
|
||||
|
||||
pvd::shared_vector<pvd::int16> scratch(5);
|
||||
scratch[0] = 9;
|
||||
scratch[1] = 7;
|
||||
scratch[2] = 5;
|
||||
scratch[3] = 3;
|
||||
scratch[4] = 1;
|
||||
pvd::shared_vector<const pvd::int16> expected(pvd::freeze(scratch));
|
||||
|
||||
dbCommon *prec = testdbRecordPtr("TEST");
|
||||
dbScanLock(prec);
|
||||
pvd::BitSet changed;
|
||||
pvif->put(changed, DBE_VALUE, fl.pfl);
|
||||
dbScanUnlock(prec);
|
||||
|
||||
testFieldEqual<pvd::PVShortArray>(root, "dut.value", expected);
|
||||
#endif // >= 7.0
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MAIN(testpvif)
|
||||
{
|
||||
testPlan(75
|
||||
testPlan(80
|
||||
#ifdef USE_INT64
|
||||
+13
|
||||
#endif
|
||||
@@ -537,5 +624,6 @@ MAIN(testpvif)
|
||||
#endif
|
||||
testScalar();
|
||||
testPlain();
|
||||
testFilters();
|
||||
return testDone();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user