Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3024f9fb0c | |||
| 58a2159969 | |||
| c200dd22f1 | |||
| 9c6fb5c539 | |||
| 54a882d844 | |||
| c7a54de469 | |||
| c2ee7c9dc4 | |||
| 3e59a4b6a8 | |||
| 0e0400022c | |||
| bdbf57350b | |||
| 2e58e54e1d | |||
| ddfd746c62 | |||
| e41269230c | |||
| c3b6fc08b8 | |||
| d70a2ff8c3 | |||
| ce39c93201 | |||
| d7314eaef4 | |||
| 5170c2230d | |||
| 3ed88f2559 | |||
| 2c533d79ab | |||
| a72bc31a44 |
@@ -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
|
||||
@@ -1,9 +1,15 @@
|
||||
# Module (source) version
|
||||
EPICS_QSRV_MAJOR_VERSION = 1
|
||||
EPICS_QSRV_MINOR_VERSION = 2
|
||||
EPICS_QSRV_MAINTENANCE_VERSION = 0
|
||||
EPICS_QSRV_DEVELOPMENT_FLAG = 0
|
||||
EPICS_QSRV_MAINTENANCE_VERSION = 2
|
||||
|
||||
# ABI version
|
||||
EPICS_QSRV_ABI_MAJOR_VERSION = 1
|
||||
EPICS_QSRV_ABI_MINOR_VERSION = 1
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
EPICS_QSRV_DEVELOPMENT_FLAG = 0
|
||||
|
||||
# Immediately after a release the MAINTENANCE_VERSION
|
||||
# will be incremented and the DEVELOPMENT_FLAG set to 1
|
||||
|
||||
@@ -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.0.1-DEV
|
||||
PROJECT_NUMBER = 1.2.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
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
.wy-side-nav-search {
|
||||
background-color: #18334B;
|
||||
}
|
||||
|
||||
.wy-side-nav-search input[type="text"] {
|
||||
border-color: #18334b;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file only contains a selection of the most common options. For a full
|
||||
# list see the documentation:
|
||||
# http://www.sphinx-doc.org/en/master/config
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'EPICS Documentation'
|
||||
copyright = '2019, EPICS Controls.'
|
||||
author = 'EPICS'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.intersphinx',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
# Intersphinx links to subprojects
|
||||
intersphinx_mapping = {
|
||||
'how-tos': ('https://docs.epics-controls.org/projects/how-tos/en/latest', None),
|
||||
}
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
html_css_files = [
|
||||
'css/custom.css',
|
||||
]
|
||||
|
||||
master_doc = 'index'
|
||||
|
||||
html_theme_options = {
|
||||
'logo_only': True,
|
||||
}
|
||||
html_logo = "images/EPICS_white_logo_v02.png"
|
||||
|
||||
html_extra_path = ['../html']
|
||||
|
||||
|
||||
# -- Run Doxygen ------------------------------------------------------------
|
||||
|
||||
import subprocess
|
||||
subprocess.call('cd ..; mkdir -p html/doxygen; doxygen', shell=True)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
@@ -0,0 +1,16 @@
|
||||
pva2pva (QSRV / pvAccess Gateway)
|
||||
=================================
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
EPICS Website <https://epics-controls.org>
|
||||
EPICS Documentation Home <https://docs.epics-controls.org>
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: pva2pva
|
||||
|
||||
Reference Manual and API Documentation <https://docs.epics-controls.org/projects/pva2pva/en/latest/doxygen>
|
||||
Source Code Repository on GitHub <https://github.com/epics-base/pva2pva>
|
||||
@@ -162,6 +162,23 @@ record(ai, "...") {
|
||||
}
|
||||
@endcode
|
||||
|
||||
@subsection qsrv_aslib Access Security
|
||||
|
||||
QSRV will enforce an optional access control policy file (.acf) loaded by the usual means (cf. asSetFilename() ).
|
||||
This policy is applied to both Single and Group PVs. With Group PVs, restrictions are not defined for the group,
|
||||
but rather for the individual member records. So the same policy will be applied regardess of how a record
|
||||
is accessed (individually, or through a group).
|
||||
|
||||
Policy application differs from CA (RSRV) in several ways:
|
||||
|
||||
* Client hostname is always the numeric IP address. HAG() entries must either contained numeric IP addresses,
|
||||
or that asCheckClientIP=1 flag must be set to translate hostnames into IPs on ACF file load (effects CA server as well).
|
||||
This prevents clients from trivially forging "hostname".
|
||||
* In additional to client usernames. UAG definitions may contained items beginning with "role/" which are matched
|
||||
against the list of groups of which the client username is a member. Username to group lookup is done internally
|
||||
to QSRV, and depends on IOC host authentication configuration. Note that this is still based on the client provided
|
||||
username string.
|
||||
|
||||
@subsection qsrv_link PVAccess Links
|
||||
|
||||
When built against Base >= 3.16.1, support is enabled for PVAccess links,
|
||||
|
||||
@@ -2,33 +2,46 @@
|
||||
|
||||
@page release_notes Release Notes
|
||||
|
||||
Release 1.2.2 (Nov 2019)
|
||||
========================
|
||||
|
||||
- Changes
|
||||
- Enforce Access Security policy.
|
||||
- NTEnum .value field add missing "enum_t" type ID string.
|
||||
|
||||
Release 1.2.1 (July 2019)
|
||||
=========================
|
||||
|
||||
- Fixes
|
||||
- Fix unittest: testpvalink. No functional change.
|
||||
|
||||
Release 1.2.0 (Mar 2019)
|
||||
========================
|
||||
|
||||
- Incompatible changes
|
||||
- The field 'display.format' is replaced with 'display.form' and 'display.precision'.
|
||||
https://github.com/epics-base/pva2pva/issues/19
|
||||
- The field 'display.format' is replaced with 'display.form' and 'display.precision'.
|
||||
https://github.com/epics-base/pva2pva/issues/19
|
||||
- Additions
|
||||
- Use @code info(Q:form, "...") @endcode to set 'display.form'. See @ref qsrv_form
|
||||
- Use @code info(Q:form, "...") @endcode to set 'display.form'. See @ref qsrv_form
|
||||
- Fixes
|
||||
- Correct handling of 64-bit integer fields.
|
||||
- Install a copy of softIocExit.db for standalone builds
|
||||
- Correct handling of 64-bit integer fields.
|
||||
- Install a copy of softIocExit.db for standalone builds
|
||||
|
||||
Release 1.1.0 (Nov 2018)
|
||||
==========================
|
||||
|
||||
- Incompatible changes
|
||||
- Requires pvDataCPP >= 7.1.0
|
||||
- Requires pvAccessCPP >= 6.1.0
|
||||
- Requires pvDataCPP >= 7.1.0
|
||||
- Requires pvAccessCPP >= 6.1.0
|
||||
- Removals
|
||||
- Drop the broken ioccircle and ioccircle2 examples.
|
||||
- Drop the broken ioccircle and ioccircle2 examples.
|
||||
- Fixes
|
||||
- Fix QSRV monitor locking causing crash
|
||||
- Fix Windows DLL import/export errors
|
||||
- Correctly handle empty "scalar" case of NELM=1, NORD=0.
|
||||
- Fix QSRV monitor locking causing crash
|
||||
- Fix Windows DLL import/export errors
|
||||
- Correctly handle empty "scalar" case of NELM=1, NORD=0.
|
||||
- Additions
|
||||
- QSRV implement channelList() (aka. 'pvlist') with list of record and group names.
|
||||
- @ref qsrv_link type (requires Base >= 3.16.1)
|
||||
- QSRV implement channelList() (aka. 'pvlist') with list of record and group names.
|
||||
- @ref qsrv_link type (requires Base >= 3.16.1)
|
||||
|
||||
Release 1.0.0 (Dec 2017)
|
||||
========================
|
||||
|
||||
+25
-25
@@ -75,7 +75,7 @@ private:
|
||||
mutex_type mutex;
|
||||
store_t store;
|
||||
};
|
||||
std::tr1::shared_ptr<data> m_data;
|
||||
std::tr1::shared_ptr<data> _data;
|
||||
|
||||
struct dtor {
|
||||
std::tr1::weak_ptr<data> container;
|
||||
@@ -110,7 +110,7 @@ private:
|
||||
};
|
||||
public:
|
||||
//! Construct a new empty set
|
||||
weak_value_map() :m_data(new data) {}
|
||||
weak_value_map() :_data(new data) {}
|
||||
|
||||
private:
|
||||
//! Not copyable
|
||||
@@ -122,22 +122,22 @@ public:
|
||||
//! exchange the two sets.
|
||||
//! @warning Not thread safe (exchanges mutexes as well)
|
||||
void swap(weak_value_map& O) {
|
||||
m_data.swap(O.m_data);
|
||||
_data.swap(O._data);
|
||||
}
|
||||
|
||||
//! Remove all (weak) entries from the set
|
||||
//! @note Thread safe
|
||||
void clear() {
|
||||
guard_type G(m_data->mutex);
|
||||
return m_data->store.clear();
|
||||
guard_type G(_data->mutex);
|
||||
return _data->store.clear();
|
||||
}
|
||||
|
||||
//! Test if set is empty at this moment
|
||||
//! @note Thread safe
|
||||
//! @warning see size()
|
||||
bool empty() const {
|
||||
guard_type G(m_data->mutex);
|
||||
return m_data->store.empty();
|
||||
guard_type G(_data->mutex);
|
||||
return _data->store.empty();
|
||||
}
|
||||
|
||||
//! number of entries in the set at this moment
|
||||
@@ -145,8 +145,8 @@ public:
|
||||
//! @warning May be momentarily inaccurate (larger) due to dead refs.
|
||||
//! which have not yet been removed.
|
||||
size_t size() const {
|
||||
guard_type G(m_data->mutex);
|
||||
return m_data->store.size();
|
||||
guard_type G(_data->mutex);
|
||||
return _data->store.size();
|
||||
}
|
||||
|
||||
//! proxy class for lookup of non-const
|
||||
@@ -167,8 +167,8 @@ public:
|
||||
{
|
||||
if(!v.unique())
|
||||
throw std::invalid_argument("Only unique() references may be inserted");
|
||||
value_pointer chainptr(v.get(), dtor(M.m_data, k, v));
|
||||
M.m_data->store[k] = chainptr;
|
||||
value_pointer chainptr(v.get(), dtor(M._data, k, v));
|
||||
M._data->store[k] = chainptr;
|
||||
v.swap(chainptr);
|
||||
return v;
|
||||
}
|
||||
@@ -215,9 +215,9 @@ public:
|
||||
value_pointer find(const K& k) const
|
||||
{
|
||||
value_pointer ret;
|
||||
guard_type G(m_data->mutex);
|
||||
typename store_t::const_iterator it(m_data->store.find(k));
|
||||
if(it!=m_data->store.end()) {
|
||||
guard_type G(_data->mutex);
|
||||
typename store_t::const_iterator it(_data->store.find(k));
|
||||
if(it!=_data->store.end()) {
|
||||
// may be nullptr if we race destruction
|
||||
// as ref. count falls to zero before we can remove it
|
||||
ret = it->second.lock();
|
||||
@@ -230,9 +230,9 @@ public:
|
||||
value_pointer insert(const K& k, value_pointer& v)
|
||||
{
|
||||
value_pointer ret;
|
||||
guard_type G(m_data->mutex);
|
||||
typename store_t::const_iterator it = m_data->store.find(k);
|
||||
if(it!=m_data->store.end())
|
||||
guard_type G(_data->mutex);
|
||||
typename store_t::const_iterator it = _data->store.find(k);
|
||||
if(it!=_data->store.end())
|
||||
ret = it->second.lock();
|
||||
(*this)[k] = v;
|
||||
return ret;
|
||||
@@ -243,9 +243,9 @@ public:
|
||||
lock_map_type lock_map() const
|
||||
{
|
||||
lock_map_type ret;
|
||||
guard_type G(m_data->mutex);
|
||||
for(typename store_t::const_iterator it = m_data->store.begin(),
|
||||
end = m_data->store.end(); it!=end; ++it)
|
||||
guard_type G(_data->mutex);
|
||||
for(typename store_t::const_iterator it = _data->store.begin(),
|
||||
end = _data->store.end(); it!=end; ++it)
|
||||
{
|
||||
value_pointer P(it->second.lock);
|
||||
if(P) ret[it->first] = P;
|
||||
@@ -259,10 +259,10 @@ public:
|
||||
lock_vector_type lock_vector() const
|
||||
{
|
||||
lock_vector_type ret;
|
||||
guard_type G(m_data->mutex);
|
||||
ret.reserve(m_data->store.size());
|
||||
for(typename store_t::const_iterator it = m_data->store.begin(),
|
||||
end = m_data->store.end(); it!=end; ++it)
|
||||
guard_type G(_data->mutex);
|
||||
ret.reserve(_data->store.size());
|
||||
for(typename store_t::const_iterator it = _data->store.begin(),
|
||||
end = _data->store.end(); it!=end; ++it)
|
||||
{
|
||||
value_pointer P(it->second.lock());
|
||||
if(P) ret.push_back(std::make_pair(it->first, P));
|
||||
@@ -274,7 +274,7 @@ public:
|
||||
//! for use with batch operations.
|
||||
//! @warning Use caution when swap()ing while holding this lock!
|
||||
inline epicsMutex& mutex() const {
|
||||
return m_data->mutex;
|
||||
return _data->mutex;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+25
-25
@@ -94,7 +94,7 @@ private:
|
||||
mutex_type mutex;
|
||||
store_t store;
|
||||
};
|
||||
std::tr1::shared_ptr<data> m_data;
|
||||
std::tr1::shared_ptr<data> _data;
|
||||
|
||||
//! Destroyer for a chained shared_ptr
|
||||
//! which holds the unique() real strong
|
||||
@@ -131,7 +131,7 @@ private:
|
||||
};
|
||||
public:
|
||||
//! Construct a new empty set
|
||||
weak_set() :m_data(new data) {}
|
||||
weak_set() :_data(new data) {}
|
||||
|
||||
private:
|
||||
//! Not copyable
|
||||
@@ -143,22 +143,22 @@ public:
|
||||
//! exchange the two sets.
|
||||
//! @warning Not thread safe (exchanges mutexes as well)
|
||||
void swap(weak_set& O) {
|
||||
m_data.swap(O.m_data);
|
||||
_data.swap(O._data);
|
||||
}
|
||||
|
||||
//! Remove all (weak) entries from the set
|
||||
//! @note Thread safe
|
||||
void clear() {
|
||||
guard_type G(m_data->mutex);
|
||||
return m_data->store.clear();
|
||||
guard_type G(_data->mutex);
|
||||
return _data->store.clear();
|
||||
}
|
||||
|
||||
//! Test if set is empty
|
||||
//! @note Thread safe
|
||||
//! @warning see size()
|
||||
bool empty() const {
|
||||
guard_type G(m_data->mutex);
|
||||
return m_data->store.empty();
|
||||
guard_type G(_data->mutex);
|
||||
return _data->store.empty();
|
||||
}
|
||||
|
||||
//! number of entries in the set at this moment
|
||||
@@ -166,8 +166,8 @@ public:
|
||||
//! @warning May be momentarily inaccurate (larger) due to dead refs.
|
||||
//! which have not yet been removed.
|
||||
size_t size() const {
|
||||
guard_type G(m_data->mutex);
|
||||
return m_data->store.size();
|
||||
guard_type G(_data->mutex);
|
||||
return _data->store.size();
|
||||
}
|
||||
|
||||
//! Insert a new entry into the set
|
||||
@@ -178,8 +178,8 @@ public:
|
||||
//! Remove any (weak) ref to this object from the set
|
||||
//! @returns the number of objects removed (0 or 1)
|
||||
size_t erase(value_pointer& v) {
|
||||
guard_type G(m_data->mutex);
|
||||
return m_data->store.erase(v);
|
||||
guard_type G(_data->mutex);
|
||||
return _data->store.erase(v);
|
||||
}
|
||||
|
||||
//! Return a set of strong references to all entries
|
||||
@@ -197,7 +197,7 @@ public:
|
||||
//! for use with batch operations.
|
||||
//! @warning Use caution when swap()ing while holding this lock!
|
||||
inline epicsMutex& mutex() const {
|
||||
return m_data->mutex;
|
||||
return _data->mutex;
|
||||
}
|
||||
|
||||
//! an iterator-ish object which also locks the set during iteration
|
||||
@@ -205,7 +205,7 @@ public:
|
||||
weak_set& set;
|
||||
epicsGuard<epicsMutex> guard;
|
||||
typename store_t::iterator it, end;
|
||||
XIterator(weak_set& S) :set(S), guard(S.mutex()), it(S.m_data->store.begin()), end(S.m_data->store.end()) {}
|
||||
XIterator(weak_set& S) :set(S), guard(S.mutex()), it(S._data->store.begin()), end(S._data->store.end()) {}
|
||||
//! yield the next live entry
|
||||
value_pointer next() {
|
||||
value_pointer ret;
|
||||
@@ -229,14 +229,14 @@ void weak_set<T>::insert(value_pointer &v)
|
||||
if(!v.unique())
|
||||
throw std::invalid_argument("Only unique() references may be inserted");
|
||||
|
||||
guard_type G(m_data->mutex);
|
||||
typename store_t::const_iterator it = m_data->store.find(v);
|
||||
if(it==m_data->store.end()) { // new object
|
||||
guard_type G(_data->mutex);
|
||||
typename store_t::const_iterator it = _data->store.find(v);
|
||||
if(it==_data->store.end()) { // new object
|
||||
|
||||
// wrapped strong ref. which removes from our map
|
||||
value_pointer chainptr(v.get(), dtor(m_data, v));
|
||||
value_pointer chainptr(v.get(), dtor(_data, v));
|
||||
|
||||
m_data->store.insert(chainptr);
|
||||
_data->store.insert(chainptr);
|
||||
|
||||
v.swap(chainptr); // we only keep the chained pointer
|
||||
} else {
|
||||
@@ -253,9 +253,9 @@ typename weak_set<T>::set_type
|
||||
weak_set<T>::lock_set() const
|
||||
{
|
||||
set_type ret;
|
||||
guard_type G(m_data->mutex);
|
||||
for(typename store_t::const_iterator it=m_data->store.begin(),
|
||||
end=m_data->store.end(); it!=end; ++it)
|
||||
guard_type G(_data->mutex);
|
||||
for(typename store_t::const_iterator it=_data->store.begin(),
|
||||
end=_data->store.end(); it!=end; ++it)
|
||||
{
|
||||
value_pointer P(it->lock());
|
||||
if(P) ret.insert(P);
|
||||
@@ -275,10 +275,10 @@ weak_set<T>::lock_vector() const
|
||||
template<typename T>
|
||||
void weak_set<T>::lock_vector(vector_type& ret) const
|
||||
{
|
||||
guard_type G(m_data->mutex);
|
||||
ret.reserve(m_data->store.size());
|
||||
for(typename store_t::const_iterator it=m_data->store.begin(),
|
||||
end=m_data->store.end(); it!=end; ++it)
|
||||
guard_type G(_data->mutex);
|
||||
ret.reserve(_data->store.size());
|
||||
for(typename store_t::const_iterator it=_data->store.begin(),
|
||||
end=_data->store.end(); it!=end; ++it)
|
||||
{
|
||||
value_pointer P(it->lock());
|
||||
if(P) ret.push_back(P);
|
||||
|
||||
@@ -16,6 +16,7 @@ LIBRARY += qsrv
|
||||
|
||||
SHRLIB_VERSION ?= $(EPICS_QSRV_ABI_MAJOR_VERSION).$(EPICS_QSRV_ABI_MINOR_VERSION)
|
||||
|
||||
USR_CPPFLAGS += -DQSRV_API_BUILDING
|
||||
USR_CPPFLAGS += -I$(TOP)/common -I$(TOP)/p2pApp
|
||||
|
||||
INC += pv/qsrv.h
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include <pv/configuration.h>
|
||||
#include <pv/json.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "pdbgroup.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/anyscalar.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "pvif.h"
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/configuration.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "helper.h"
|
||||
#include "pdbsingle.h"
|
||||
#include "pvif.h"
|
||||
|
||||
+2
-2
@@ -30,7 +30,7 @@ struct PDBPV
|
||||
virtual void show(int lvl) {}
|
||||
};
|
||||
|
||||
struct epicsShareClass PDBProvider : public epics::pvAccess::ChannelProvider,
|
||||
struct QSRV_API PDBProvider : public epics::pvAccess::ChannelProvider,
|
||||
public epics::pvAccess::ChannelFind,
|
||||
public std::tr1::enable_shared_from_this<PDBProvider>
|
||||
{
|
||||
@@ -67,7 +67,7 @@ struct epicsShareClass PDBProvider : public epics::pvAccess::ChannelProvider,
|
||||
static size_t num_instances;
|
||||
};
|
||||
|
||||
epicsShareFunc
|
||||
QSRV_API
|
||||
void QSRVRegistrar_counters();
|
||||
|
||||
#endif // PDB_H
|
||||
|
||||
+12
-2
@@ -13,7 +13,6 @@
|
||||
#include <pv/configuration.h>
|
||||
#include <pv/epicsException.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "helper.h"
|
||||
#include "pdbgroup.h"
|
||||
#include "pdb.h"
|
||||
@@ -139,6 +138,15 @@ PDBGroupPV::connect(const std::tr1::shared_ptr<PDBProvider>& prov,
|
||||
const pva::ChannelRequester::shared_pointer& req)
|
||||
{
|
||||
PDBGroupChannel::shared_pointer ret(new PDBGroupChannel(shared_from_this(), prov, req));
|
||||
|
||||
ret->cred.update(req);
|
||||
|
||||
ret->aspvt.resize(members.size());
|
||||
for(size_t i=0, N=members.size(); i<N; i++)
|
||||
{
|
||||
ret->aspvt[i].add(members[i].chan, ret->cred);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -368,7 +376,9 @@ void PDBGroupPut::put(pvd::PVStructure::shared_pointer const & value,
|
||||
|
||||
DBScanLocker L(dbChannelRecord(info.chan));
|
||||
|
||||
ret |= putpvif[i]->get(*changed, info.allowProc ? doProc : PVIF::ProcInhibit);
|
||||
ret |= putpvif[i]->get(*changed,
|
||||
info.allowProc ? doProc : PVIF::ProcInhibit,
|
||||
channel->aspvt[i].canWrite());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-5
@@ -19,9 +19,9 @@
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
struct epicsShareClass GroupConfig
|
||||
struct QSRV_API GroupConfig
|
||||
{
|
||||
struct epicsShareClass Field {
|
||||
struct QSRV_API Field {
|
||||
std::string type, channel, trigger, id;
|
||||
int putorder;
|
||||
|
||||
@@ -36,7 +36,7 @@ struct epicsShareClass GroupConfig
|
||||
}
|
||||
};
|
||||
|
||||
struct epicsShareClass Group {
|
||||
struct QSRV_API Group {
|
||||
typedef std::map<std::string, Field> fields_t;
|
||||
fields_t fields;
|
||||
bool atomic, atomic_set;
|
||||
@@ -70,7 +70,7 @@ struct PDBGroupMonitor;
|
||||
void pdb_group_event(void *user_arg, struct dbChannel *chan,
|
||||
int eventsRemaining, struct db_field_log *pfl);
|
||||
|
||||
struct epicsShareClass PDBGroupPV : public PDBPV
|
||||
struct QSRV_API PDBGroupPV : public PDBPV
|
||||
{
|
||||
POINTER_DEFINITIONS(PDBGroupPV);
|
||||
weak_pointer weakself;
|
||||
@@ -133,12 +133,15 @@ struct epicsShareClass PDBGroupPV : public PDBPV
|
||||
virtual void show(int lvl) OVERRIDE;
|
||||
};
|
||||
|
||||
struct epicsShareClass PDBGroupChannel : public BaseChannel,
|
||||
struct QSRV_API PDBGroupChannel : public BaseChannel,
|
||||
public std::tr1::enable_shared_from_this<PDBGroupChannel>
|
||||
{
|
||||
POINTER_DEFINITIONS(PDBGroupChannel);
|
||||
|
||||
PDBGroupPV::shared_pointer pv;
|
||||
std::vector<ASCLIENT> aspvt;
|
||||
// storage referenced from aspvt
|
||||
ASCred cred;
|
||||
|
||||
static size_t num_instances;
|
||||
|
||||
|
||||
+22
-5
@@ -5,15 +5,16 @@
|
||||
#include <dbAccess.h>
|
||||
#include <dbChannel.h>
|
||||
#include <dbStaticLib.h>
|
||||
#include <epicsAtomic.h>
|
||||
#include <errlog.h>
|
||||
#include <dbNotify.h>
|
||||
#include <osiSock.h>
|
||||
#include <epicsAtomic.h>
|
||||
|
||||
#include <pv/epicsException.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/security.h>
|
||||
#include <pv/configuration.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "helper.h"
|
||||
#include "pdbsingle.h"
|
||||
#include "pdb.h"
|
||||
@@ -128,6 +129,11 @@ PDBSinglePV::connect(const std::tr1::shared_ptr<PDBProvider>& prov,
|
||||
const pva::ChannelRequester::shared_pointer& req)
|
||||
{
|
||||
PDBSingleChannel::shared_pointer ret(new PDBSingleChannel(shared_from_this(), req));
|
||||
|
||||
ret->cred.update(req);
|
||||
|
||||
ret->aspvt.add(chan, ret->cred);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -145,7 +151,7 @@ void PDBSinglePV::addMonitor(PDBSingleMonitor* mon)
|
||||
db_post_single_event(evt_VALUE.subscript);
|
||||
db_post_single_event(evt_PROPERTY.subscript);
|
||||
|
||||
} if(hadevent_VALUE && hadevent_PROPERTY) {
|
||||
} else if(hadevent_VALUE && hadevent_PROPERTY) {
|
||||
// new subscriber and already had initial update
|
||||
mon->post(G);
|
||||
} // else new subscriber, but no initial update. so just wait
|
||||
@@ -200,7 +206,15 @@ PDBSingleChannel::~PDBSingleChannel()
|
||||
|
||||
void PDBSingleChannel::printInfo(std::ostream& out)
|
||||
{
|
||||
out<<"PDBSingleChannel";
|
||||
if(aspvt.canWrite())
|
||||
out << "RW ";
|
||||
else
|
||||
out << "RO ";
|
||||
out<<(&cred.user[0])<<'@'<<(&cred.host[0]);
|
||||
for(size_t i=0, N=cred.groups.size(); i<N; i++) {
|
||||
out<<", "<<(&cred.groups[i][0]);
|
||||
}
|
||||
out<<"\n";
|
||||
}
|
||||
|
||||
pva::ChannelPut::shared_pointer
|
||||
@@ -337,7 +351,10 @@ void PDBSinglePut::put(pvd::PVStructure::shared_pointer const & value,
|
||||
dbFldDes *fld = dbChannelFldDes(chan);
|
||||
|
||||
pvd::Status ret;
|
||||
if(dbChannelFieldType(chan)>=DBF_INLINK && dbChannelFieldType(chan)<=DBF_FWDLINK) {
|
||||
if(!channel->aspvt.canWrite()) {
|
||||
ret = pvd::Status::error("Put not permitted");
|
||||
|
||||
} else if(dbChannelFieldType(chan)>=DBF_INLINK && dbChannelFieldType(chan)<=DBF_FWDLINK) {
|
||||
try{
|
||||
std::string lval(value->getSubFieldT<pvd::PVScalar>("value")->getAs<std::string>());
|
||||
long status = dbChannelPutField(chan, DBF_STRING, lval.c_str(), 1);
|
||||
|
||||
+5
-1
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <dbAccess.h>
|
||||
#include <dbNotify.h>
|
||||
#include <asLib.h>
|
||||
|
||||
#include <dbEvent.h>
|
||||
|
||||
@@ -19,7 +20,7 @@
|
||||
|
||||
struct PDBSingleMonitor;
|
||||
|
||||
struct epicsShareClass PDBSinglePV : public PDBPV
|
||||
struct QSRV_API PDBSinglePV : public PDBPV
|
||||
{
|
||||
POINTER_DEFINITIONS(PDBSinglePV);
|
||||
weak_pointer weakself;
|
||||
@@ -78,6 +79,9 @@ struct PDBSingleChannel : public BaseChannel,
|
||||
POINTER_DEFINITIONS(PDBSingleChannel);
|
||||
|
||||
PDBSinglePV::shared_pointer pv;
|
||||
// storage referenced from aspvt
|
||||
ASCred cred;
|
||||
ASCLIENT aspvt;
|
||||
|
||||
static size_t num_instances;
|
||||
|
||||
|
||||
+28
-6
@@ -2,7 +2,6 @@
|
||||
#define PV_QSRV_H
|
||||
|
||||
#include <epicsVersion.h>
|
||||
#include <shareLib.h>
|
||||
|
||||
#ifndef VERSION_INT
|
||||
# define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P))
|
||||
@@ -15,6 +14,29 @@
|
||||
|
||||
#define QSRV_ABI_VERSION_INT VERSION_INT(EPICS_QSRV_ABI_MAJOR_VERSION, EPICS_QSRV_ABI_MINOR_VERSION, 0, 0)
|
||||
|
||||
#if defined(QSRV_API_BUILDING) && defined(epicsExportSharedSymbols)
|
||||
# error Use QSRV_API or shareLib.h not both
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||
|
||||
# if defined(QSRV_API_BUILDING) && defined(EPICS_BUILD_DLL)
|
||||
/* building library as dll */
|
||||
# define QSRV_API __declspec(dllexport)
|
||||
# elif !defined(QSRV_API_BUILDING) && defined(EPICS_CALL_DLL)
|
||||
/* calling library in dll form */
|
||||
# define QSRV_API __declspec(dllimport)
|
||||
# endif
|
||||
|
||||
#elif __GNUC__ >= 4
|
||||
# define QSRV_API __attribute__ ((visibility("default")))
|
||||
#endif
|
||||
|
||||
#ifndef QSRV_API
|
||||
# define QSRV_API
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -22,12 +44,12 @@ extern "C" {
|
||||
struct link; /* aka. DBLINK from link.h */
|
||||
|
||||
/** returns QSRV_VERSION_INT captured at compilation time */
|
||||
epicsShareExtern unsigned qsrvVersion(void);
|
||||
QSRV_API unsigned qsrvVersion(void);
|
||||
|
||||
/** returns QSRV_ABI_VERSION_INT captured at compilation time */
|
||||
epicsShareExtern unsigned qsrvABIVersion(void);
|
||||
QSRV_API unsigned qsrvABIVersion(void);
|
||||
|
||||
epicsShareFunc void testqsrvWaitForLinkEvent(struct link *plink);
|
||||
QSRV_API void testqsrvWaitForLinkEvent(struct link *plink);
|
||||
|
||||
/** Call before testIocShutdownOk()
|
||||
@code
|
||||
@@ -41,7 +63,7 @@ epicsShareFunc void testqsrvWaitForLinkEvent(struct link *plink);
|
||||
testdbCleanup();
|
||||
@endcode
|
||||
*/
|
||||
epicsShareExtern void testqsrvShutdownOk(void);
|
||||
QSRV_API void testqsrvShutdownOk(void);
|
||||
|
||||
/** Call after testIocShutdownOk() and before testdbCleanup()
|
||||
@code
|
||||
@@ -55,7 +77,7 @@ epicsShareExtern void testqsrvShutdownOk(void);
|
||||
testdbCleanup();
|
||||
@endcode
|
||||
*/
|
||||
epicsShareExtern void testqsrvCleanup(void);
|
||||
QSRV_API void testqsrvCleanup(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
+1
-2
@@ -26,13 +26,12 @@
|
||||
#include <pv/reftrack.h>
|
||||
#include <pva/client.h>
|
||||
|
||||
#include <epicsExport.h> /* defines epicsExportSharedSymbols */
|
||||
|
||||
#include "pv/qsrv.h"
|
||||
#include "helper.h"
|
||||
#include "pvif.h"
|
||||
#include "pvalink.h"
|
||||
|
||||
#include <epicsExport.h> /* defines epicsExportSharedSymbols */
|
||||
|
||||
int pvaLinkDebug;
|
||||
int pvaLinkIsolate;
|
||||
|
||||
+3
-13
@@ -4,11 +4,6 @@
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define pvalinkEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#define EPICS_DBCA_PRIVATE_API
|
||||
#include <epicsGuard.h>
|
||||
#include <dbAccess.h>
|
||||
@@ -44,19 +39,14 @@
|
||||
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#ifdef pvalinkEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef pvalinkEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include "helper.h"
|
||||
#include "pvif.h"
|
||||
#include "tpool.h"
|
||||
|
||||
extern "C" {
|
||||
epicsShareExtern int pvaLinkDebug;
|
||||
epicsShareExtern int pvaLinkIsolate;
|
||||
epicsShareExtern int pvaLinkNWorkers;
|
||||
QSRV_API extern int pvaLinkDebug;
|
||||
QSRV_API extern int pvaLinkIsolate;
|
||||
QSRV_API extern int pvaLinkNWorkers;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
|
||||
#include <pv/reftrack.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <shareLib.h>
|
||||
|
||||
#include "pvalink.h"
|
||||
|
||||
int pvaLinkNWorkers = 1;
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
#include <epicsStdio.h> // redirects stdout/stderr
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <shareLib.h>
|
||||
|
||||
#include "pvalink.h"
|
||||
|
||||
namespace pvalink {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
#include <pv/reftrack.h>
|
||||
#include <alarm.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <shareLib.h>
|
||||
|
||||
#include "pvalink.h"
|
||||
|
||||
namespace pvalink {
|
||||
|
||||
@@ -6,9 +6,6 @@
|
||||
|
||||
#include <pv/current_function.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <shareLib.h>
|
||||
|
||||
#include "pvalink.h"
|
||||
|
||||
|
||||
|
||||
+132
-20
@@ -12,14 +12,16 @@
|
||||
#include <errSymTbl.h>
|
||||
#include <epicsVersion.h>
|
||||
#include <errlog.h>
|
||||
#include <osiSock.h>
|
||||
|
||||
#include <pv/status.h>
|
||||
#include <pv/bitSet.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/anyscalar.h>
|
||||
#include <pv/reftrack.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/security.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "sb.h"
|
||||
#include "pvif.h"
|
||||
|
||||
@@ -34,6 +36,7 @@
|
||||
#endif
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
namespace pva = epics::pvAccess;
|
||||
|
||||
DBCH::DBCH(dbChannel *ch) :chan(ch)
|
||||
{
|
||||
@@ -66,6 +69,97 @@ void DBCH::swap(DBCH& o)
|
||||
std::swap(chan, o.chan);
|
||||
}
|
||||
|
||||
void ASCred::update(const pva::ChannelRequester::shared_pointer& req)
|
||||
{
|
||||
pva::PeerInfo::const_shared_pointer info(req->getPeerInfo());
|
||||
std::string usertemp, hosttemp;
|
||||
|
||||
if(info && info->identified) {
|
||||
hosttemp = info->peer;
|
||||
if(info->authority=="ca") {
|
||||
usertemp = info->account;
|
||||
size_t sep = usertemp.find_last_of('/');
|
||||
if(sep != std::string::npos) {
|
||||
// prevent CA auth from claiming to be eg. "krb/someone.special"
|
||||
usertemp = usertemp.substr(sep+1);
|
||||
}
|
||||
|
||||
} else {
|
||||
usertemp = info->authority + "/" + info->account;
|
||||
}
|
||||
|
||||
const char role[] = "role/";
|
||||
|
||||
groups.resize(info->roles.size());
|
||||
size_t idx = 0u;
|
||||
for(pva::PeerInfo::roles_t::const_iterator it(info->roles.begin()), end(info->roles.end()); it!=end; ++it, idx++) {
|
||||
groups[idx].resize((*it).size()+sizeof(role)); // sizeof(role) includes trailing nil
|
||||
std::copy(role,
|
||||
role+sizeof(role)-1,
|
||||
groups[idx].begin());
|
||||
std::copy(it->begin(),
|
||||
it->end(),
|
||||
groups[idx].begin()+sizeof(role)-1);
|
||||
groups[idx][groups[idx].size()-1] = '\0';
|
||||
}
|
||||
|
||||
} else {
|
||||
// legacy and anonymous
|
||||
hosttemp = req->getRequesterName();
|
||||
}
|
||||
|
||||
// remote names have the form "IP:port"
|
||||
size_t sep = hosttemp.find_first_of(':');
|
||||
if(sep == std::string::npos) {
|
||||
sep = hosttemp.size();
|
||||
}
|
||||
hosttemp.resize(sep);
|
||||
|
||||
host.resize(hosttemp.size()+1);
|
||||
std::copy(hosttemp.begin(),
|
||||
hosttemp.end(),
|
||||
host.begin());
|
||||
host[hosttemp.size()] = '\0';
|
||||
|
||||
user.resize(usertemp.size()+1);
|
||||
std::copy(usertemp.begin(),
|
||||
usertemp.end(),
|
||||
user.begin());
|
||||
user[usertemp.size()] = '\0';
|
||||
}
|
||||
|
||||
ASCLIENT::~ASCLIENT()
|
||||
{
|
||||
asRemoveClient(&aspvt);
|
||||
for(size_t i=0, N=grppvt.size(); i<N; i++) {
|
||||
asRemoveClient(&grppvt[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void ASCLIENT::add(dbChannel* chan, ASCred& cred)
|
||||
{
|
||||
asRemoveClient(&aspvt);
|
||||
/* asAddClient() fails secure to no-permission */
|
||||
(void)asAddClient(&aspvt, dbChannelRecord(chan)->asp, dbChannelFldDes(chan)->as_level, &cred.user[0], &cred.host[0]);
|
||||
|
||||
grppvt.resize(cred.groups.size(), 0);
|
||||
|
||||
for(size_t i=0, N=grppvt.size(); i<N; i++) {
|
||||
asRemoveClient(&grppvt[i]);
|
||||
(void)asAddClient(&grppvt[i], dbChannelRecord(chan)->asp, dbChannelFldDes(chan)->as_level, &cred.groups[i][0], &cred.host[0]);
|
||||
}
|
||||
}
|
||||
|
||||
bool ASCLIENT::canWrite() {
|
||||
if(!asActive || (aspvt && asCheckPut(aspvt)))
|
||||
return true;
|
||||
for(size_t i=0, N=grppvt.size(); i<N; i++) {
|
||||
if(grppvt[i] && asCheckPut(grppvt[i]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
PVIF::PVIF(dbChannel *ch)
|
||||
:chan(ch)
|
||||
{}
|
||||
@@ -602,15 +696,21 @@ struct PVIFScalarNumeric : public PVIF
|
||||
}
|
||||
}
|
||||
|
||||
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc) OVERRIDE FINAL
|
||||
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc, bool permit) OVERRIDE FINAL
|
||||
{
|
||||
pvd::Status ret;
|
||||
bool newval = mask.logical_and(pvmeta.maskVALUEPut);
|
||||
if(newval) {
|
||||
getValue(pvmeta.chan, pvmeta.value.get());
|
||||
if(permit)
|
||||
getValue(pvmeta.chan, pvmeta.value.get());
|
||||
else
|
||||
ret = pvd::Status::error("Put not permitted");
|
||||
}
|
||||
if(newval || proc==PVIF::ProcForce) {
|
||||
ret = PVIF::get(mask, proc);
|
||||
if(permit)
|
||||
ret = PVIF::get(mask, proc);
|
||||
else
|
||||
ret = pvd::Status::error("Process not permitted");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -682,6 +782,7 @@ ScalarBuilder::dtype(dbChannel *channel)
|
||||
if(dbr==DBR_ENUM)
|
||||
builder = builder->setId("epics:nt/NTEnum:1.0")
|
||||
->addNestedStructure("value")
|
||||
->setId("enum_t")
|
||||
->add("index", pvd::pvInt)
|
||||
->addArray("choices", pvd::pvString)
|
||||
->endNested();
|
||||
@@ -795,7 +896,7 @@ struct PVIFPlain : public PVIF
|
||||
|
||||
virtual ~PVIFPlain() {}
|
||||
|
||||
virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl)
|
||||
virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
|
||||
{
|
||||
if(dbe&DBE_VALUE) {
|
||||
putValue(channel, field.get(), pfl);
|
||||
@@ -803,20 +904,26 @@ struct PVIFPlain : public PVIF
|
||||
}
|
||||
}
|
||||
|
||||
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc)
|
||||
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc, bool permit) OVERRIDE FINAL
|
||||
{
|
||||
pvd::Status ret;
|
||||
bool newval = mask.get(fieldOffset);
|
||||
if(newval) {
|
||||
getValue(channel, field.get());
|
||||
if(permit)
|
||||
getValue(channel, field.get());
|
||||
else
|
||||
ret = pvd::Status::error("Put not permitted");
|
||||
}
|
||||
if(newval || proc==PVIF::ProcForce) {
|
||||
ret = PVIF::get(mask, proc);
|
||||
if(permit)
|
||||
ret = PVIF::get(mask, proc);
|
||||
else
|
||||
ret = pvd::Status::error("Process not permitted");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual unsigned dbe(const epics::pvData::BitSet& mask)
|
||||
virtual unsigned dbe(const epics::pvData::BitSet& mask) OVERRIDE FINAL
|
||||
{
|
||||
// TODO: figure out how to handle various intermidiate compressed
|
||||
// bitSet and enclosing.
|
||||
@@ -940,7 +1047,7 @@ struct PVIFMeta : public PVIF
|
||||
|
||||
virtual ~PVIFMeta() {}
|
||||
|
||||
virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl)
|
||||
virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
|
||||
{
|
||||
mask |= meta.maskALWAYS;
|
||||
if(dbe&DBE_ALARM)
|
||||
@@ -949,13 +1056,15 @@ struct PVIFMeta : public PVIF
|
||||
putTime(meta, dbe, pfl);
|
||||
}
|
||||
|
||||
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc)
|
||||
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc, bool permit) OVERRIDE FINAL
|
||||
{
|
||||
// can't put time/alarm
|
||||
if(mask.logical_and(meta.maskALARM))
|
||||
return pvd::Status::warn("Put to meta field ignored");
|
||||
return pvd::Status::Ok;
|
||||
}
|
||||
|
||||
virtual unsigned dbe(const epics::pvData::BitSet& mask)
|
||||
virtual unsigned dbe(const epics::pvData::BitSet& mask) OVERRIDE FINAL
|
||||
{
|
||||
if(mask.logical_and(meta.maskALARM))
|
||||
return DBE_ALARM;
|
||||
@@ -1010,18 +1119,18 @@ struct PVIFProc : public PVIF
|
||||
{
|
||||
PVIFProc(dbChannel *channel) :PVIF(channel) {}
|
||||
|
||||
virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl)
|
||||
virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
|
||||
{
|
||||
// nothing to get
|
||||
}
|
||||
|
||||
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc)
|
||||
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc, bool permit) OVERRIDE FINAL
|
||||
{
|
||||
// always process
|
||||
return PVIF::get(mask, PVIF::ProcForce);
|
||||
// always process (if permitted)
|
||||
return PVIF::get(mask, PVIF::ProcForce, permit);
|
||||
}
|
||||
|
||||
virtual unsigned dbe(const epics::pvData::BitSet& mask)
|
||||
virtual unsigned dbe(const epics::pvData::BitSet& mask) OVERRIDE FINAL
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -1036,7 +1145,7 @@ struct ProcBuilder : public PVIFBuilder
|
||||
|
||||
virtual epics::pvData::FieldBuilderPtr dtype(epics::pvData::FieldBuilderPtr& builder,
|
||||
const std::string& fld,
|
||||
dbChannel *channel)
|
||||
dbChannel *channel) OVERRIDE FINAL
|
||||
{
|
||||
// invisible
|
||||
return builder;
|
||||
@@ -1054,7 +1163,7 @@ struct ProcBuilder : public PVIFBuilder
|
||||
|
||||
}//namespace
|
||||
|
||||
pvd::Status PVIF::get(const epics::pvData::BitSet& mask, proc_t proc)
|
||||
pvd::Status PVIF::get(const epics::pvData::BitSet& mask, proc_t proc, bool permit)
|
||||
{
|
||||
dbCommon *precord = dbChannelRecord(chan);
|
||||
|
||||
@@ -1066,7 +1175,10 @@ pvd::Status PVIF::get(const epics::pvData::BitSet& mask, proc_t proc)
|
||||
pvd::Status ret;
|
||||
|
||||
if (tryproc) {
|
||||
if (precord->pact) {
|
||||
if (!permit) {
|
||||
return pvd::Status::error("Process not permitted");
|
||||
|
||||
} else if (precord->pact) {
|
||||
if (precord->tpro)
|
||||
printf("%s: Active %s\n",
|
||||
epicsThreadGetNameSelf(), precord->name);
|
||||
|
||||
+35
-11
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <asLib.h>
|
||||
#include <dbAccess.h>
|
||||
#include <dbChannel.h>
|
||||
#include <dbStaticLib.h>
|
||||
@@ -15,7 +16,7 @@
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/anyscalar.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
#include <pv/qsrv.h>
|
||||
|
||||
#ifndef VERSION_INT
|
||||
# define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P))
|
||||
@@ -29,14 +30,20 @@
|
||||
# define USE_MULTILOCK
|
||||
#endif
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
class ChannelRequester;
|
||||
}
|
||||
}
|
||||
|
||||
short PVD2DBR(epics::pvData::ScalarType pvt);
|
||||
|
||||
// copy from PVField (.value sub-field) to DBF buffer
|
||||
epicsShareExtern
|
||||
QSRV_API
|
||||
long copyPVD2DBF(const epics::pvData::PVField::const_shared_pointer& in,
|
||||
void *outbuf, short outdbf, long *outnReq);
|
||||
// copy from DBF buffer to PVField (.value sub-field)
|
||||
epicsShareExtern
|
||||
QSRV_API
|
||||
long copyDBF2PVD(const epics::pvData::shared_vector<const void>& buf,
|
||||
const epics::pvData::PVField::shared_pointer& out,
|
||||
epics::pvData::BitSet &changed,
|
||||
@@ -62,7 +69,7 @@ union dbrbuf {
|
||||
char dbf_STRING[MAX_STRING_SIZE];
|
||||
};
|
||||
|
||||
struct epicsShareClass DBCH {
|
||||
struct QSRV_API DBCH {
|
||||
dbChannel *chan;
|
||||
DBCH() :chan(0) {}
|
||||
explicit DBCH(dbChannel *ch); // calls dbChannelOpen()
|
||||
@@ -81,6 +88,23 @@ private:
|
||||
void prepare();
|
||||
};
|
||||
|
||||
struct ASCred {
|
||||
// string storage must be safely mutable. cf. asAddClient()
|
||||
std::vector<char> user, host;
|
||||
std::vector<std::vector<char> > groups;
|
||||
void update(const std::tr1::shared_ptr<epics::pvAccess::ChannelRequester>& request);
|
||||
};
|
||||
|
||||
struct ASCLIENT {
|
||||
ASCLIENTPVT aspvt;
|
||||
std::vector<ASCLIENTPVT> grppvt;
|
||||
ASCLIENT() :aspvt(0) {}
|
||||
~ASCLIENT();
|
||||
// ASCred storage must remain valid
|
||||
void add(dbChannel* chan, ASCred& cred);
|
||||
bool canWrite();
|
||||
};
|
||||
|
||||
struct pdbRecordInfo {
|
||||
DBENTRY ent;
|
||||
pdbRecordInfo(const char *name)
|
||||
@@ -254,7 +278,7 @@ struct DBManyLock
|
||||
dbLocker *plock;
|
||||
DBManyLock() :plock(NULL) {}
|
||||
DBManyLock(const std::vector<dbCommon*>& recs, unsigned flags=0)
|
||||
:plock(dbLockerAlloc((dbCommon**)&recs[0], recs.size(), flags))
|
||||
:plock(dbLockerAlloc( (recs.size() > 0 ? (dbCommon**)&recs[0] : NULL), recs.size(), flags))
|
||||
{
|
||||
if(!plock) throw std::invalid_argument("Failed to create locker");
|
||||
}
|
||||
@@ -285,7 +309,7 @@ struct DBManyLocker
|
||||
};
|
||||
#endif
|
||||
|
||||
struct epicsShareClass FieldName
|
||||
struct QSRV_API FieldName
|
||||
{
|
||||
struct Component {
|
||||
std::string name;
|
||||
@@ -328,7 +352,7 @@ private:
|
||||
FieldName& operator=(const FieldName&);
|
||||
};
|
||||
|
||||
struct epicsShareClass PVIF {
|
||||
struct QSRV_API PVIF {
|
||||
PVIF(dbChannel *ch);
|
||||
virtual ~PVIF() {}
|
||||
|
||||
@@ -343,9 +367,9 @@ struct epicsShareClass PVIF {
|
||||
//! Copy from PDB record to pvalue (call dbChannelGet())
|
||||
//! caller must lock record
|
||||
virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl) =0;
|
||||
//! Copy from pvalue to PDB record (call dbChannelPut())
|
||||
//! May copy from pvalue to PDB record (call dbChannelPut())
|
||||
//! caller must lock record
|
||||
virtual epics::pvData::Status get(const epics::pvData::BitSet& mask, proc_t proc=ProcInhibit) =0;
|
||||
virtual epics::pvData::Status get(const epics::pvData::BitSet& mask, proc_t proc=ProcInhibit, bool permit=true) =0;
|
||||
//! Calculate DBE mask from changed bitset
|
||||
virtual unsigned dbe(const epics::pvData::BitSet& mask) =0;
|
||||
|
||||
@@ -354,7 +378,7 @@ private:
|
||||
PVIF& operator=(const PVIF&);
|
||||
};
|
||||
|
||||
struct epicsShareClass PVIFBuilder {
|
||||
struct QSRV_API PVIFBuilder {
|
||||
|
||||
virtual ~PVIFBuilder() {}
|
||||
|
||||
@@ -378,7 +402,7 @@ private:
|
||||
PVIFBuilder& operator=(const PVIFBuilder&);
|
||||
};
|
||||
|
||||
struct epicsShareClass ScalarBuilder : public PVIFBuilder
|
||||
struct QSRV_API ScalarBuilder : public PVIFBuilder
|
||||
{
|
||||
virtual ~ScalarBuilder() {}
|
||||
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
#include <pv/serverContext.h>
|
||||
#include <pv/iocshelper.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
|
||||
#include "pv/qsrv.h"
|
||||
#include "pvahelper.h"
|
||||
#include "pvif.h"
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "helper.h"
|
||||
#include "tpool.h"
|
||||
|
||||
|
||||
+23
-23
@@ -1,8 +1,8 @@
|
||||
|
||||
#include <dbUnitTest.h>
|
||||
#include <testMain.h>
|
||||
#include <longinRecord.h>
|
||||
#include <longoutRecord.h>
|
||||
#include <int64inRecord.h>
|
||||
#include <int64outRecord.h>
|
||||
|
||||
#include <pv/qsrv.h>
|
||||
#include "utilities.h"
|
||||
@@ -15,50 +15,50 @@ void testGet()
|
||||
{
|
||||
testDiag("==== testGet ====");
|
||||
|
||||
longinRecord *li1 = (longinRecord*)testdbRecordPtr("src:i1");
|
||||
int64inRecord *i1 = (int64inRecord*)testdbRecordPtr("src:i1");
|
||||
|
||||
while(!dbIsLinkConnected(&li1->inp))
|
||||
testqsrvWaitForLinkEvent(&li1->inp);
|
||||
while(!dbIsLinkConnected(&i1->inp))
|
||||
testqsrvWaitForLinkEvent(&i1->inp);
|
||||
|
||||
testdbGetFieldEqual("target:i.VAL", DBF_INT64, 42);
|
||||
testdbGetFieldEqual("target:i.VAL", DBF_INT64, 42LL);
|
||||
|
||||
testdbGetFieldEqual("src:i1.VAL", DBF_INT64, 0); // value before first process
|
||||
testdbGetFieldEqual("src:i1.VAL", DBF_INT64, 0LL); // value before first process
|
||||
|
||||
testdbGetFieldEqual("src:i1.INP", DBF_STRING, "{\"pva\":\"target:i\"}");
|
||||
|
||||
testdbPutFieldOk("src:i1.PROC", DBF_INT64, 1);
|
||||
testdbPutFieldOk("src:i1.PROC", DBF_INT64, 1LL);
|
||||
|
||||
testdbGetFieldEqual("src:i1.VAL", DBF_INT64, 42);
|
||||
testdbGetFieldEqual("src:i1.VAL", DBF_INT64, 42LL);
|
||||
|
||||
testdbPutFieldOk("src:i1.INP", DBF_STRING, "{\"pva\":\"target:ai\"}");
|
||||
|
||||
while(!dbIsLinkConnected(&li1->inp))
|
||||
testqsrvWaitForLinkEvent(&li1->inp);
|
||||
while(!dbIsLinkConnected(&i1->inp))
|
||||
testqsrvWaitForLinkEvent(&i1->inp);
|
||||
|
||||
testdbGetFieldEqual("src:i1.VAL", DBF_INT64, 42); // changing link doesn't automatically process
|
||||
testdbGetFieldEqual("src:i1.VAL", DBF_INT64, 42LL); // changing link doesn't automatically process
|
||||
|
||||
testdbPutFieldOk("src:i1.PROC", DBF_INT64, 1);
|
||||
testdbPutFieldOk("src:i1.PROC", DBF_INT64, 1LL);
|
||||
|
||||
testdbGetFieldEqual("src:i1.VAL", DBF_INT64, 4); // now it's changed
|
||||
testdbGetFieldEqual("src:i1.VAL", DBF_INT64, 4LL); // now it's changed
|
||||
}
|
||||
|
||||
void testPut()
|
||||
{
|
||||
testDiag("==== testPut ====");
|
||||
|
||||
longoutRecord *lo2 = (longoutRecord*)testdbRecordPtr("src:lo2");
|
||||
int64outRecord *o2 = (int64outRecord*)testdbRecordPtr("src:o2");
|
||||
|
||||
while(!dbIsLinkConnected(&lo2->out))
|
||||
testqsrvWaitForLinkEvent(&lo2->out);
|
||||
while(!dbIsLinkConnected(&o2->out))
|
||||
testqsrvWaitForLinkEvent(&o2->out);
|
||||
|
||||
testdbGetFieldEqual("target:i2.VAL", DBF_INT64, 43);
|
||||
testdbGetFieldEqual("src:lo2.VAL", DBF_INT64, 0);
|
||||
testdbGetFieldEqual("src:lo2.OUT", DBF_STRING, "{\"pva\":\"target:i2\"}");
|
||||
testdbGetFieldEqual("target:i2.VAL", DBF_INT64, 43LL);
|
||||
testdbGetFieldEqual("src:o2.VAL", DBF_INT64, 0LL);
|
||||
testdbGetFieldEqual("src:o2.OUT", DBF_STRING, "{\"pva\":\"target:i2\"}");
|
||||
|
||||
testdbPutFieldOk("src:lo2.VAL", DBF_INT64, 14);
|
||||
testdbPutFieldOk("src:o2.VAL", DBF_INT64, 14LL);
|
||||
|
||||
testdbGetFieldEqual("target:i2.VAL", DBF_INT64, 14);
|
||||
testdbGetFieldEqual("src:lo2.VAL", DBF_INT64, 14);
|
||||
testdbGetFieldEqual("target:i2.VAL", DBF_INT64, 14LL);
|
||||
testdbGetFieldEqual("src:o2.VAL", DBF_INT64, 14LL);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -16,6 +16,6 @@ record(int64in, "target:i2") {
|
||||
field(VAL, "43")
|
||||
}
|
||||
|
||||
record(int64out, "src:lo2") {
|
||||
record(int64out, "src:o2") {
|
||||
field(OUT, {pva:"target:i2"})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user