Compare commits
24 Commits
1.0.0
..
1.1.0-pre1
| Author | SHA1 | Date | |
|---|---|---|---|
| 701d96fde1 | |||
| 8db7a4a75e | |||
| 266f2f9d2d | |||
| 19602bb06d | |||
| 26d369c042 | |||
| 1d0be31d3f | |||
| 4a0275a476 | |||
| 9c10c82e82 | |||
| 770152e621 | |||
| 272b4fb9cb | |||
| a22399fdf4 | |||
| d229efbb66 | |||
| 392a44b791 | |||
| 6982fa2d8f | |||
| b7de8ba8b0 | |||
| a1cb3d302a | |||
| b6b6ac3305 | |||
| 1635e75c86 | |||
| 6e77fc85b6 | |||
| 8a9a3550b9 | |||
| 95a74aa134 | |||
| 9cc417103b | |||
| 399f6b311e | |||
| 3be469e5c2 |
@@ -7,6 +7,9 @@ DIRS += configure
|
||||
DIRS += $(wildcard *App)
|
||||
DIRS += $(wildcard iocBoot)
|
||||
|
||||
# pdbApp depends on configure for CONFIG_QSRV_VERSION
|
||||
pdbApp_DEPEND_DIRS = configure
|
||||
|
||||
# iocBoot depends on all *App dirs
|
||||
iocBoot_DEPEND_DIRS += $(filter %App,$(DIRS))
|
||||
testApp_DEPEND_DIRS += p2pApp pdbApp
|
||||
|
||||
@@ -86,15 +86,15 @@ A full list of `info(Q:group` options.
|
||||
record(...) {
|
||||
info(Q:group, {
|
||||
"<group_name>":{
|
||||
+id:"some/NT:1.0", // top level ID
|
||||
+meta:"FLD", // map top level alarm/timeStamp
|
||||
+atomic:true, // whether monitors default to multi-locking atomicity
|
||||
+id:"some/NT:1.0", # top level ID
|
||||
+meta:"FLD", # map top level alarm/timeStamp
|
||||
+atomic:true, # whether monitors default to multi-locking atomicity
|
||||
"<field.name>":{
|
||||
+type:"scalar", // controls how map VAL mapped onto <field.name>
|
||||
+type:"scalar", # controls how map VAL mapped onto <field.name>
|
||||
+channel:"VAL",
|
||||
+id:"some/NT:1.0",
|
||||
+trigger:"*", // "*" or comma seperated list of <field.name>s
|
||||
+putorder:0, // set for fields where put is allowed, processing done in increasing order
|
||||
+trigger:"*", # "*" or comma seperated list of <field.name>s
|
||||
+putorder:0, # set for fields where put is allowed, processing done in increasing order
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
+86
-68
@@ -74,8 +74,9 @@ struct BaseMonitor : public epics::pvAccess::Monitor
|
||||
|
||||
typedef epics::pvAccess::MonitorRequester requester_t;
|
||||
|
||||
mutable epicsMutex lock; // not held during any callback
|
||||
epicsMutex& lock; // not held during any callback
|
||||
typedef epicsGuard<epicsMutex> guard_t;
|
||||
typedef epicsGuardRelease<epicsMutex> unguard_t;
|
||||
|
||||
private:
|
||||
const requester_t::weak_pointer requester;
|
||||
@@ -90,9 +91,11 @@ private:
|
||||
buffer_t inuse, empty;
|
||||
|
||||
public:
|
||||
BaseMonitor(const requester_t::weak_pointer& requester,
|
||||
BaseMonitor(epicsMutex& lock,
|
||||
const requester_t::weak_pointer& requester,
|
||||
const epics::pvData::PVStructure::shared_pointer& pvReq)
|
||||
:requester(requester)
|
||||
:lock(lock)
|
||||
,requester(requester)
|
||||
,inoverflow(false)
|
||||
,running(false)
|
||||
,nbuffers(2)
|
||||
@@ -104,121 +107,136 @@ public:
|
||||
|
||||
//! Must call before first post(). Sets .complete and calls monitorConnect()
|
||||
//! @note that value will never by accessed except by post() and requestUpdate()
|
||||
void connect(const epics::pvData::PVStructurePtr& value)
|
||||
void connect(guard_t& guard, const epics::pvData::PVStructurePtr& value)
|
||||
{
|
||||
guard.assertIdenticalMutex(lock);
|
||||
epics::pvData::StructureConstPtr dtype(value->getStructure());
|
||||
epics::pvData::PVDataCreatePtr create(epics::pvData::getPVDataCreate());
|
||||
BaseMonitor::shared_pointer self(shared_from_this());
|
||||
requester_t::shared_pointer req(requester.lock());
|
||||
{
|
||||
guard_t G(lock);
|
||||
assert(!complete); // can't call twice
|
||||
|
||||
complete = value;
|
||||
empty.resize(nbuffers);
|
||||
for(size_t i=0; i<empty.size(); i++) {
|
||||
empty[i].reset(new epics::pvAccess::MonitorElement(create->createPVStructure(dtype)));
|
||||
}
|
||||
assert(!complete); // can't call twice
|
||||
|
||||
complete = value;
|
||||
empty.resize(nbuffers);
|
||||
for(size_t i=0; i<empty.size(); i++) {
|
||||
empty[i].reset(new epics::pvAccess::MonitorElement(create->createPVStructure(dtype)));
|
||||
}
|
||||
epics::pvData::Status sts;
|
||||
if(req)
|
||||
|
||||
if(req) {
|
||||
unguard_t U(guard);
|
||||
epics::pvData::Status sts;
|
||||
req->monitorConnect(sts, self, dtype);
|
||||
}
|
||||
}
|
||||
|
||||
struct no_overflow {};
|
||||
|
||||
//! post update if queue not full, if full return false w/o overflow
|
||||
bool post(const epics::pvData::BitSet& updated, no_overflow)
|
||||
bool post(guard_t& guard, const epics::pvData::BitSet& updated, no_overflow)
|
||||
{
|
||||
guard.assertIdenticalMutex(lock);
|
||||
requester_t::shared_pointer req;
|
||||
{
|
||||
guard_t G(lock);
|
||||
if(!complete || !running) return false;
|
||||
|
||||
changed |= updated;
|
||||
if(!complete || !running) return false;
|
||||
|
||||
if(empty.empty()) return false;
|
||||
changed |= updated;
|
||||
|
||||
if(p_postone())
|
||||
req = requester.lock();
|
||||
inoverflow = false;
|
||||
if(empty.empty()) return false;
|
||||
|
||||
if(p_postone())
|
||||
req = requester.lock();
|
||||
inoverflow = false;
|
||||
|
||||
if(req) {
|
||||
unguard_t U(guard);
|
||||
req->monitorEvent(shared_from_this());
|
||||
}
|
||||
if(req) req->monitorEvent(shared_from_this());
|
||||
return true;
|
||||
}
|
||||
|
||||
//! post update of pending changes. eg. call from requestUpdate()
|
||||
bool post()
|
||||
bool post(guard_t& guard)
|
||||
{
|
||||
guard.assertIdenticalMutex(lock);
|
||||
bool oflow;
|
||||
requester_t::shared_pointer req;
|
||||
{
|
||||
guard_t G(lock);
|
||||
if(!complete || !running) return false;
|
||||
|
||||
if(empty.empty()) {
|
||||
oflow = inoverflow = true;
|
||||
if(!complete || !running) return false;
|
||||
|
||||
} else {
|
||||
if(empty.empty()) {
|
||||
oflow = inoverflow = true;
|
||||
|
||||
if(p_postone())
|
||||
req = requester.lock();
|
||||
oflow = inoverflow = false;
|
||||
}
|
||||
} else {
|
||||
|
||||
if(p_postone())
|
||||
req = requester.lock();
|
||||
oflow = inoverflow = false;
|
||||
}
|
||||
|
||||
if(req) {
|
||||
unguard_t U(guard);
|
||||
req->monitorEvent(shared_from_this());
|
||||
}
|
||||
if(req) req->monitorEvent(shared_from_this());
|
||||
return !oflow;
|
||||
}
|
||||
|
||||
//! post update with changed and overflowed masks (eg. when updates were lost in some upstream queue)
|
||||
bool post(const epics::pvData::BitSet& updated, const epics::pvData::BitSet& overflowed)
|
||||
bool post(guard_t& guard,
|
||||
const epics::pvData::BitSet& updated,
|
||||
const epics::pvData::BitSet& overflowed)
|
||||
{
|
||||
guard.assertIdenticalMutex(lock);
|
||||
bool oflow;
|
||||
requester_t::shared_pointer req;
|
||||
{
|
||||
guard_t G(lock);
|
||||
if(!complete || !running) return false;
|
||||
|
||||
if(empty.empty()) {
|
||||
oflow = inoverflow = true;
|
||||
overflow |= overflowed;
|
||||
overflow.or_and(updated, changed);
|
||||
changed |= updated;
|
||||
if(!complete || !running) return false;
|
||||
|
||||
} else {
|
||||
if(empty.empty()) {
|
||||
oflow = inoverflow = true;
|
||||
overflow |= overflowed;
|
||||
overflow.or_and(updated, changed);
|
||||
changed |= updated;
|
||||
|
||||
changed |= updated;
|
||||
if(p_postone())
|
||||
req = requester.lock();
|
||||
oflow = inoverflow = false;
|
||||
}
|
||||
} else {
|
||||
|
||||
changed |= updated;
|
||||
if(p_postone())
|
||||
req = requester.lock();
|
||||
oflow = inoverflow = false;
|
||||
}
|
||||
|
||||
if(req) {
|
||||
unguard_t U(guard);
|
||||
req->monitorEvent(shared_from_this());
|
||||
}
|
||||
if(req) req->monitorEvent(shared_from_this());
|
||||
return !oflow;
|
||||
}
|
||||
|
||||
//! post update with changed
|
||||
bool post(const epics::pvData::BitSet& updated) {
|
||||
bool post(guard_t& guard, const epics::pvData::BitSet& updated) {
|
||||
bool oflow;
|
||||
requester_t::shared_pointer req;
|
||||
{
|
||||
guard_t G(lock);
|
||||
if(!complete || !running) return false;
|
||||
|
||||
if(empty.empty()) {
|
||||
oflow = inoverflow = true;
|
||||
overflow.or_and(updated, changed);
|
||||
changed |= updated;
|
||||
if(!complete || !running) return false;
|
||||
|
||||
} else {
|
||||
if(empty.empty()) {
|
||||
oflow = inoverflow = true;
|
||||
overflow.or_and(updated, changed);
|
||||
changed |= updated;
|
||||
|
||||
changed |= updated;
|
||||
if(p_postone())
|
||||
req = requester.lock();
|
||||
oflow = inoverflow = false;
|
||||
}
|
||||
} else {
|
||||
|
||||
changed |= updated;
|
||||
if(p_postone())
|
||||
req = requester.lock();
|
||||
oflow = inoverflow = false;
|
||||
}
|
||||
|
||||
if(req) {
|
||||
unguard_t U(guard);
|
||||
req->monitorEvent(shared_from_this());
|
||||
}
|
||||
if(req) req->monitorEvent(shared_from_this());
|
||||
return !oflow;
|
||||
}
|
||||
|
||||
@@ -250,7 +268,7 @@ public:
|
||||
virtual void onStop() {}
|
||||
//! called when within release() when the opportunity exists to end the overflow condition
|
||||
//! May do nothing, or lock and call post()
|
||||
virtual void requestUpdate() {guard_t G(lock); post();}
|
||||
virtual void requestUpdate() {guard_t G(lock); post(G);}
|
||||
|
||||
virtual void destroy()
|
||||
{
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#ifndef SB_H
|
||||
#define SB_H
|
||||
|
||||
#include <sstream>
|
||||
|
||||
// in-line string builder (eg. for exception messages)
|
||||
// throw std::runtime_error(SB()<<"Answer: !"<<42);
|
||||
struct SB {
|
||||
std::ostringstream strm;
|
||||
SB() {}
|
||||
operator std::string() const { return strm.str(); }
|
||||
template<typename T>
|
||||
SB& operator<<(T i) { strm<<i; return *this; }
|
||||
};
|
||||
|
||||
#endif // SB_H
|
||||
@@ -1,8 +1,8 @@
|
||||
# Module (source) version
|
||||
EPICS_QSRV_MAJOR_VERSION = 1
|
||||
EPICS_QSRV_MINOR_VERSION = 0
|
||||
EPICS_QSRV_MAINTENANCE_VERSION = 0
|
||||
EPICS_QSRV_DEVELOPMENT_FLAG = 0
|
||||
EPICS_QSRV_MAINTENANCE_VERSION = 1
|
||||
EPICS_QSRV_DEVELOPMENT_FLAG = 1
|
||||
|
||||
# ABI version
|
||||
EPICS_QSRV_ABI_MAJOR_VERSION = 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 = 0
|
||||
PROJECT_NUMBER = 1.0.1-DEV
|
||||
|
||||
# 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
|
||||
|
||||
@@ -5,11 +5,15 @@
|
||||
|
||||
@section qsrv QSRV
|
||||
|
||||
QSRV is a network server using the PVAccess protocol which
|
||||
@ref qsrv_page is a network server using the PVAccess protocol which
|
||||
runs inside an EPICS IOC process and allows clients
|
||||
to make requests to access the Process Variables (PVs)
|
||||
within.
|
||||
|
||||
Documentation of @ref qsrv_config including @ref qsrv_group_def.
|
||||
|
||||
- @ref release_notes
|
||||
|
||||
@subsection qsrv_build Building
|
||||
|
||||
To build the latest from version control
|
||||
|
||||
+133
-2
@@ -4,10 +4,141 @@
|
||||
@section qsrv_config QSRV Configuration
|
||||
|
||||
By default QSRV exposes all Process Variables (fields of process database records).
|
||||
In addition to these "single" PVs, special "group" PVs.
|
||||
In addition to these "single" PVs are special "group" PVs.
|
||||
|
||||
@subsection qsrv_group_sym Group PV semantics
|
||||
@subsection qsrv_single Single PVs
|
||||
|
||||
"single" PVs are the same set of names server by the Channel Access server (RSRV).
|
||||
This is all accessible record fields.
|
||||
So all data which is accessible via Channel Access is also accessible via PVAccess.
|
||||
|
||||
QSRV presents all "single" PVs as Structures conforming to the
|
||||
Normative Types NTScalar, NTScalarArray, or NTEnum depending on the native DBF field type.
|
||||
|
||||
@subsection qsrv_group_def Group PV definitions
|
||||
|
||||
A group is defined using a JSON syntax.
|
||||
Groups are defined with respect to a Group Name,
|
||||
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.
|
||||
For example of a group including two records is:
|
||||
|
||||
@code
|
||||
record(ai, "rec:X") {
|
||||
info(Q:group, {
|
||||
"grp:name": {
|
||||
"X": {+channel:"VAL"}
|
||||
}
|
||||
})
|
||||
}
|
||||
record(ai, "rec:Y") {
|
||||
info(Q:group, {
|
||||
"grp:name": {
|
||||
"Y": {+channel:"VAL"}
|
||||
}
|
||||
})
|
||||
}
|
||||
@endcode
|
||||
|
||||
This group, named "grp:name", has two fields "X" and "Y".
|
||||
|
||||
@code
|
||||
$ pvget grp:name
|
||||
grp:name
|
||||
structure
|
||||
epics:nt/NTScalar:1.0 X
|
||||
double value 0
|
||||
alarm_t alarm INVALID DRIVER UDF
|
||||
time_t timeStamp <undefined> 0
|
||||
...
|
||||
epics:nt/NTScalar:1.0 Y
|
||||
double value 0
|
||||
alarm_t alarm INVALID DRIVER UDF
|
||||
time_t timeStamp <undefined> 0
|
||||
...
|
||||
@endcode
|
||||
|
||||
@subsection qsrv_group_ref Group PV reference
|
||||
|
||||
@code
|
||||
record(...) {
|
||||
info(Q:group, {
|
||||
"<group_name>":{
|
||||
+id:"some/NT:1.0", # top level ID
|
||||
+meta:"FLD", # map top level alarm/timeStamp
|
||||
+atomic:true, # whether monitors default to multi-locking atomicity
|
||||
"<field.name>":{
|
||||
+type:"scalar", # controls how map VAL mapped onto <field.name>
|
||||
+channel:"VAL",
|
||||
+id:"some/NT:1.0",
|
||||
+trigger:"*", # "*" or comma seperated list of <field.name>s
|
||||
+putorder:0, # set for fields where put is allowed, processing done in increasing order
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@endcode
|
||||
|
||||
@subsubsection qsrv_group_map_types Field mapping types
|
||||
|
||||
@li "scalar" or ""
|
||||
@li "plain"
|
||||
@li "any"
|
||||
@li "meta"
|
||||
@li "proc"
|
||||
|
||||
The "scalar" mapping places an NTScalar or NTScalarArray as a sub-structure.
|
||||
|
||||
The "plain" mapping ignores all meta-data and places only the "value" as a field.
|
||||
The "value" is equivalent to '.value' of the equivalent NTScalar/NTScalarArray as a field.
|
||||
|
||||
The "any" mapping places a variant union into which the "value" is placed.
|
||||
|
||||
The "meta" mapping ignores the "value" and places only the alarm and time
|
||||
meta-data as sub-fields.
|
||||
The special group level tag 'meta:""' allows these meta-data fields to be
|
||||
placed in the top-level structure.
|
||||
|
||||
The "proc" mapping uses neither "value" nor meta-data.
|
||||
Instead the target record is processed during a put.
|
||||
|
||||
@subsubsection qsrv_group_map_trig Field Update Triggers
|
||||
|
||||
The field triggers define how changes to the consitutent field
|
||||
are translated into a subscription update to the group.
|
||||
|
||||
The most use of these are "" which means that changes to the field
|
||||
are ignored, and do not result group update.
|
||||
And "*" which results in a group update containing the most recent
|
||||
values/meta-data of all fields.
|
||||
|
||||
It may be useful to specify a comma seperated list of field names
|
||||
so that changes may partially update the group.
|
||||
|
||||
@subsection qsrv_stamp QSRV Timestamp Options
|
||||
|
||||
QSRV has the ability to perform certain transformations on the timestamp before transporting it.
|
||||
The mechanism for configuring this is the "Q:time:tag" info() tag.
|
||||
|
||||
@subsubsection qsrv_stamp_nslsb Nano-seconds least significant bits
|
||||
|
||||
Setting "Q:time:tag" to a value of "nsec:lsb:#", where # is a number between 0 and 32,
|
||||
will split the nanoseconds value stored in the associated record.
|
||||
The least significant # bits are stored in the 'timeStamp.userTag' field.
|
||||
While the remaining 32-# bits are stored in 'timeStamp.nanoseconds' (without shifting).
|
||||
|
||||
For example, in the following situation 16 bits are split off.
|
||||
If the nanoseconds part of the record timestamp is 0x12345678,
|
||||
then the PVD structure would include "timeStamp.nanoseconds=0x12300000"
|
||||
and "timeStamp.userTag=0x45678".
|
||||
|
||||
@code
|
||||
record(ai, "...") {
|
||||
info(Q:time:tag, "nsec:lsb:20")
|
||||
}
|
||||
@endcode
|
||||
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
|
||||
@page release_notes Release Notes
|
||||
|
||||
Release 1.1.0 (UNRELEASED)
|
||||
==========================
|
||||
|
||||
- Incompatible changes
|
||||
- Requires pvDataCPP >= 7.1.0-pre1
|
||||
- Requires pvAccessCPP >= 6.1.0-pre1
|
||||
- Removals
|
||||
- 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.
|
||||
- Additions
|
||||
- QSRV implement channelList() (aka. 'pvlist') with list of record and group names.
|
||||
|
||||
Release 1.0.0 (Dec 2017)
|
||||
========================
|
||||
|
||||
Initial Release
|
||||
@@ -1,5 +0,0 @@
|
||||
TOP = ../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
ARCH = linux-x86_64-debug
|
||||
TARGETS = envPaths
|
||||
include $(TOP)/configure/RULES.ioc
|
||||
@@ -1,66 +0,0 @@
|
||||
record(ao, "circle:step") {
|
||||
field(VAL , "1.0")
|
||||
field(DRVL, "0.0")
|
||||
field(DRVH, "359")
|
||||
field(PINI, "YES")
|
||||
}
|
||||
|
||||
record(ao, "circle:period") {
|
||||
field(VAL , "1.0")
|
||||
field(PINI, "YES")
|
||||
field(OUT , "circle:tick.ODLY NPP")
|
||||
}
|
||||
|
||||
record(calc, "circle:angle") {
|
||||
field(PINI, "RUNNING") # bootstrap
|
||||
field(INPA, "circle:angle NPP")
|
||||
field(INPB, "circle:step NPP")
|
||||
field(INPD, "360")
|
||||
field(DESC, "Angle")
|
||||
field(EGU , "deg")
|
||||
field(LOLO, "45")
|
||||
field(LOW , "135")
|
||||
field(HIGH, "225")
|
||||
field(HIHI, "315")
|
||||
field(LLSV, "MAJOR")
|
||||
field(LSV , "MINOR")
|
||||
field(HSV , "MINOR")
|
||||
field(HHSV, "MAJOR")
|
||||
field(CALC, "C:=A+B;(C>=D)?C-D:C")
|
||||
field(FLNK, "circle:x")
|
||||
field(PREC, "3")
|
||||
info(pdbGroup0, "circle|angle=VAL")
|
||||
info(pdbGroup1, "line|a=VAL")
|
||||
alias("line:a")
|
||||
}
|
||||
|
||||
record(calc, "circle:x") {
|
||||
field(INPA, "circle:angle NPP")
|
||||
field(CALC, "cos(A*PI/180)")
|
||||
field(TSEL, "circle:angle.TIME")
|
||||
field(FLNK, "circle:y")
|
||||
field(PREC, "3")
|
||||
info(pdbGroup, "circle|x=VAL")
|
||||
}
|
||||
|
||||
record(calc, "circle:y") {
|
||||
field(INPA, "circle:angle NPP")
|
||||
field(CALC, "sin(A*PI/180)")
|
||||
field(TSEL, "circle:angle.TIME")
|
||||
field(PREC, "3")
|
||||
field(FLNK, "line:b")
|
||||
info(pdbGroup, "circle|y=VAL")
|
||||
info(pdbTrigger,"circle|y>*")
|
||||
}
|
||||
|
||||
record(ai, "line:b") {
|
||||
field(INP, "line:a NPP")
|
||||
field(FLNK, "circle:tick")
|
||||
info(pdbGroup, "line|b=VAL")
|
||||
info(pdbTrigger,"line|b>*")
|
||||
}
|
||||
|
||||
record(calcout, "circle:tick") {
|
||||
field(ODLY, "1.0")
|
||||
field(OUT , "circle:angle.PROC CA") # loop
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#!../../bin/linux-x86_64-debug/softIocPVA
|
||||
|
||||
#epicsEnvSet("EPICS_PVA_ADDR_LIST", "10.5.2.255")
|
||||
#epicsEnvSet("EPICS_PVAS_INTF_ADDR_LIST","10.5.2.1")
|
||||
#epicsEnvSet("EPICS_PVA_AUTO_ADDR_LIST","NO")
|
||||
|
||||
#epicsEnvSet("EPICS_PVA_SERVER_PORT","5085")
|
||||
#epicsEnvSet("EPICS_PVA_BROADCAST_PORT","5086")
|
||||
#epicsEnvSet("EPICS_CA_SERVER_PORT","5067")
|
||||
#epicsEnvSet("EPICS_CA_REPEATER_PORT","5068")
|
||||
#epicsEnvSet("EPICS_CA_MAX_ARRAY_BYTES","13000000")
|
||||
|
||||
dbLoadRecords("circle.db","")
|
||||
|
||||
iocInit()
|
||||
@@ -1,5 +0,0 @@
|
||||
TOP = ../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
ARCH = linux-x86_64-debug
|
||||
TARGETS = envPaths
|
||||
include $(TOP)/configure/RULES.ioc
|
||||
@@ -1,27 +0,0 @@
|
||||
record(calc, "circle2:x") {
|
||||
field(INPA, {pva:"circle.x CP MSI"})
|
||||
field(CALC, "2*A")
|
||||
field(FLNK, "circle2:y")
|
||||
info(pdbGroup, "circle2|x=VAL")
|
||||
# field(TPRO, "1")
|
||||
}
|
||||
|
||||
record(calc, "circle2:y") {
|
||||
field(INPA, {pva:"circle.y MSI"})
|
||||
field(CALC, "2*A")
|
||||
field(TSEL, "circle2:x.TIME")
|
||||
field(FLNK, "circle2:mag")
|
||||
info(pdbGroup, "circle2|y=VAL")
|
||||
# field(TPRO, "1")
|
||||
}
|
||||
|
||||
record(calc, "circle2:mag") {
|
||||
field(INPA, "circle2:x NPP MSI")
|
||||
field(INPB, "circle2:y NPP MSI")
|
||||
field(CALC, "SQRT(A*A+B*B)")
|
||||
field(MDEL, "-1")
|
||||
field(TSEL, "circle2:x.TIME")
|
||||
info(pdbGroup, "circle2|mag=VAL")
|
||||
info(pdbTrigger,"circle2|mag>*")
|
||||
# field(TPRO, "1")
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#!../../bin/linux-x86_64-debug/softIocPVA
|
||||
|
||||
#epicsEnvSet("EPICS_PVA_ADDR_LIST", "10.5.2.255")
|
||||
#epicsEnvSet("EPICS_PVAS_INTF_ADDR_LIST","10.5.2.1")
|
||||
#epicsEnvSet("EPICS_PVA_AUTO_ADDR_LIST","NO")
|
||||
|
||||
#epicsEnvSet("EPICS_PVA_SERVER_PORT","5085")
|
||||
#epicsEnvSet("EPICS_PVA_BROADCAST_PORT","5086")
|
||||
#epicsEnvSet("EPICS_CA_SERVER_PORT","5067")
|
||||
#epicsEnvSet("EPICS_CA_REPEATER_PORT","5068")
|
||||
epicsEnvSet("EPICS_CA_MAX_ARRAY_BYTES","13000000")
|
||||
|
||||
dbLoadRecords("circle2.db","")
|
||||
|
||||
var(pvaLinkDebug, 5)
|
||||
|
||||
iocInit()
|
||||
@@ -1,18 +1,20 @@
|
||||
|
||||
|
||||
record(longout, "$(N):ArraySize0_RBV") {
|
||||
field(VAL, "100")
|
||||
info(Q:group, {
|
||||
"$(N):Array":{
|
||||
"dimension[0].size":{+channel:"VAL", +type:"plain"}
|
||||
"dimension[0].size":{+channel:"VAL", +type:"plain", +putorder:0}
|
||||
}
|
||||
})
|
||||
field(FLNK, "$(N):ArraySize1_RBV")
|
||||
}
|
||||
|
||||
record(longout, "$(N):ArraySize1_RBV") {
|
||||
field(VAL, "100")
|
||||
info(Q:group, {
|
||||
"$(N):Array":{
|
||||
"dimension[1].size":{+channel:"VAL", +type:"plain"}
|
||||
"dimension[1].size":{+channel:"VAL", +type:"plain", +putorder:0}
|
||||
}
|
||||
})
|
||||
field(FLNK, "$(N):ArrayData_")
|
||||
@@ -20,6 +22,7 @@ record(longout, "$(N):ArraySize1_RBV") {
|
||||
|
||||
record(aSub, "$(N):ArrayData_") {
|
||||
field(SNAM, "QSRV_image_demo")
|
||||
field(PINI, "YES")
|
||||
field(FTA, "ULONG")
|
||||
field(FTB, "ULONG")
|
||||
field(FTVA, "USHORT")
|
||||
@@ -35,7 +38,9 @@ record(waveform, "$(N):ArrayData") {
|
||||
info(Q:group, {
|
||||
"$(N):Array":{
|
||||
+id:"epics:nt/NTNDArray:1.0",
|
||||
"value":{+type:"any", +channel:"VAL", +trigger:"*"},
|
||||
"value":{+type:"any",
|
||||
+channel:"VAL",
|
||||
+trigger:"*"},
|
||||
"":{+type:"meta", +channel:"SEVR"}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -35,11 +35,15 @@ record(aao, "$(N)B") {
|
||||
field(TPRO, "1")
|
||||
}
|
||||
|
||||
record(bo, "$(N)Save") {
|
||||
record(longout, "$(N)Save") {
|
||||
field(MDEL, "-1") # ensure we always trigger group monitor
|
||||
field(TPRO, "1")
|
||||
info(Q:group, {
|
||||
"$(N)Tbl":{
|
||||
"_save":{+type:"proc", +channel:"VAL", +putorder:2}
|
||||
"_save":{+type:"proc",
|
||||
+channel:"VAL",
|
||||
+putorder:2,
|
||||
+trigger:"*"}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
|
||||
#include <epicsAtomic.h>
|
||||
|
||||
#include <epicsTimer.h>
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsGuard.h>
|
||||
#include <epicsEndian.h>
|
||||
|
||||
#include <pv/iocshelper.h>
|
||||
|
||||
#include <pv/pvAccess.h>
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <epicsStdlib.h>
|
||||
#include <epicsGetopt.h>
|
||||
#include <iocsh.h>
|
||||
#include <epicsTimer.h>
|
||||
|
||||
#include <pv/json.h>
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <epicsAtomic.h>
|
||||
#include <epicsString.h>
|
||||
#include <epicsTimer.h>
|
||||
|
||||
#include <pv/pvIntrospect.h> /* for pvdVersion.h */
|
||||
#include <pv/epicsException.h>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <pv/epicsException.h>
|
||||
#include <pv/monitor.h>
|
||||
#include <pv/thread.h>
|
||||
#include <pv/serverContext.h>
|
||||
|
||||
#include "server.h"
|
||||
|
||||
|
||||
@@ -3,11 +3,15 @@
|
||||
|
||||
#include <dbAccess.h>
|
||||
|
||||
#include <dbAccess.h>
|
||||
#include <dbChannel.h>
|
||||
#include <dbStaticLib.h>
|
||||
#include <dbEvent.h>
|
||||
#include <dbLock.h>
|
||||
|
||||
#include <pv/pvIntrospect.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/configuration.h>
|
||||
#include <pv/json.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
|
||||
+21
-2
@@ -10,11 +10,14 @@
|
||||
#include <epicsStdio.h>
|
||||
|
||||
#include <dbAccess.h>
|
||||
#include <dbChannel.h>
|
||||
#include <dbStaticLib.h>
|
||||
#include <dbNotify.h>
|
||||
|
||||
#include <dbEvent.h>
|
||||
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/configuration.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "helper.h"
|
||||
@@ -583,8 +586,24 @@ pva::ChannelFind::shared_pointer
|
||||
PDBProvider::channelList(pva::ChannelListRequester::shared_pointer const & requester)
|
||||
{
|
||||
pva::ChannelFind::shared_pointer ret;
|
||||
requester->channelListResult(pvd::Status(pvd::Status::STATUSTYPE_ERROR, "not supported"),
|
||||
ret, pvd::PVStringArray::const_svector(), true);
|
||||
pvd::PVStringArray::svector names;
|
||||
for(pdbRecordIterator rec; !rec.done(); rec.next())
|
||||
{
|
||||
names.push_back(rec.name());
|
||||
}
|
||||
{
|
||||
epicsGuard<epicsMutex> G(transient_pv_map.mutex());
|
||||
|
||||
for(persist_pv_map_t::const_iterator it=persist_pv_map.begin(), end=persist_pv_map.end();
|
||||
it != end; ++it)
|
||||
{
|
||||
names.push_back(it->first);
|
||||
}
|
||||
}
|
||||
// check for duplicates?
|
||||
requester->channelListResult(pvd::Status::Ok,
|
||||
shared_from_this(),
|
||||
pvd::freeze(names), false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
+14
-7
@@ -31,23 +31,30 @@ struct PDBPV
|
||||
};
|
||||
|
||||
struct epicsShareClass PDBProvider : public epics::pvAccess::ChannelProvider,
|
||||
public std::tr1::enable_shared_from_this<PDBProvider>
|
||||
public epics::pvAccess::ChannelFind,
|
||||
public std::tr1::enable_shared_from_this<PDBProvider>
|
||||
{
|
||||
POINTER_DEFINITIONS(PDBProvider);
|
||||
|
||||
explicit PDBProvider(const epics::pvAccess::Configuration::const_shared_pointer& =epics::pvAccess::Configuration::const_shared_pointer());
|
||||
virtual ~PDBProvider();
|
||||
virtual void destroy();
|
||||
virtual std::string getProviderName();
|
||||
|
||||
// ChannelProvider
|
||||
virtual void destroy() OVERRIDE FINAL;
|
||||
virtual std::string getProviderName() OVERRIDE FINAL;
|
||||
virtual epics::pvAccess::ChannelFind::shared_pointer channelFind(std::string const & channelName,
|
||||
epics::pvAccess::ChannelFindRequester::shared_pointer const & channelFindRequester);
|
||||
virtual epics::pvAccess::ChannelFind::shared_pointer channelList(epics::pvAccess::ChannelListRequester::shared_pointer const & channelListRequester);
|
||||
epics::pvAccess::ChannelFindRequester::shared_pointer const & channelFindRequester) OVERRIDE FINAL;
|
||||
virtual epics::pvAccess::ChannelFind::shared_pointer channelList(epics::pvAccess::ChannelListRequester::shared_pointer const & channelListRequester) OVERRIDE FINAL;
|
||||
virtual epics::pvAccess::Channel::shared_pointer createChannel(std::string const & channelName,
|
||||
epics::pvAccess::ChannelRequester::shared_pointer const & channelRequester,
|
||||
short priority = PRIORITY_DEFAULT);
|
||||
short priority = PRIORITY_DEFAULT) OVERRIDE FINAL;
|
||||
virtual epics::pvAccess::Channel::shared_pointer createChannel(std::string const & channelName,
|
||||
epics::pvAccess::ChannelRequester::shared_pointer const & channelRequester,
|
||||
short priority, std::string const & address);
|
||||
short priority, std::string const & address) OVERRIDE FINAL;
|
||||
|
||||
// ChannelFind
|
||||
virtual std::tr1::shared_ptr<ChannelProvider> getChannelProvider() OVERRIDE FINAL { return shared_from_this(); }
|
||||
virtual void cancel() OVERRIDE FINAL {/* our channelFind() is synchronous, so nothing to cancel */}
|
||||
|
||||
typedef std::map<std::string, PDBPV::shared_pointer> persist_pv_map_t;
|
||||
persist_pv_map_t persist_pv_map;
|
||||
|
||||
+77
-80
@@ -6,6 +6,12 @@
|
||||
|
||||
#include <epicsAtomic.h>
|
||||
#include <dbAccess.h>
|
||||
#include <dbChannel.h>
|
||||
#include <dbStaticLib.h>
|
||||
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/configuration.h>
|
||||
#include <pv/epicsException.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "helper.h"
|
||||
@@ -31,30 +37,10 @@ void pdb_group_event(void *user_arg, struct dbChannel *chan,
|
||||
PDBGroupPV::shared_pointer self(std::tr1::static_pointer_cast<PDBGroupPV>(((PDBGroupPV*)evt->self)->shared_from_this()));
|
||||
PDBGroupPV::Info& info = self->members[idx];
|
||||
|
||||
PDBGroupPV::interested_remove_t temp;
|
||||
{
|
||||
bool doPost;
|
||||
{
|
||||
Guard G(self->lock);
|
||||
|
||||
if(!(evt->dbe_mask&DBE_PROPERTY)) {
|
||||
if(!info.had_initial_VALUE) {
|
||||
info.had_initial_VALUE = true;
|
||||
assert(self->initial_waits>0);
|
||||
self->initial_waits--;
|
||||
}
|
||||
} else {
|
||||
if(!info.had_initial_PROPERTY) {
|
||||
info.had_initial_PROPERTY = true;
|
||||
assert(self->initial_waits>0);
|
||||
self->initial_waits--;
|
||||
}
|
||||
}
|
||||
|
||||
doPost = self->initial_waits==0;
|
||||
|
||||
if(doPost)
|
||||
self->interested_iterating = true;
|
||||
}
|
||||
Guard G(self->lock);
|
||||
|
||||
self->scratch.clear();
|
||||
if(evt->dbe_mask&DBE_PROPERTY || !self->monatomic)
|
||||
@@ -75,35 +61,50 @@ void pdb_group_event(void *user_arg, struct dbChannel *chan,
|
||||
}
|
||||
}
|
||||
|
||||
if(!doPost) return; // don't post() until all subscriptions get initial updates
|
||||
|
||||
FOREACH(PDBGroupPV::interested_t::const_iterator, it, end, self->interested) {
|
||||
PDBGroupMonitor& mon = **it;
|
||||
mon.post(self->scratch);
|
||||
if(!(evt->dbe_mask&DBE_PROPERTY)) {
|
||||
if(!info.had_initial_VALUE) {
|
||||
info.had_initial_VALUE = true;
|
||||
assert(self->initial_waits>0);
|
||||
self->initial_waits--;
|
||||
}
|
||||
} else {
|
||||
if(!info.had_initial_PROPERTY) {
|
||||
info.had_initial_PROPERTY = true;
|
||||
assert(self->initial_waits>0);
|
||||
self->initial_waits--;
|
||||
}
|
||||
}
|
||||
|
||||
PDBGroupPV::interested_remove_t temp;
|
||||
{
|
||||
Guard G(self->lock);
|
||||
if(self->initial_waits==0) {
|
||||
self->interested_iterating = true;
|
||||
|
||||
assert(self->interested_iterating);
|
||||
|
||||
while(!self->interested_add.empty()) {
|
||||
PDBGroupPV::interested_t::iterator first(self->interested_add.begin());
|
||||
self->interested.insert(*first);
|
||||
self->interested_add.erase(first);
|
||||
FOREACH(PDBGroupPV::interested_t::const_iterator, it, end, self->interested) {
|
||||
PDBGroupMonitor& mon = **it;
|
||||
mon.post(G, self->scratch); // G unlocked
|
||||
}
|
||||
|
||||
temp.swap(self->interested_remove);
|
||||
for(PDBGroupPV::interested_remove_t::iterator it(temp.begin()),
|
||||
end(temp.end()); it != end; ++it)
|
||||
{
|
||||
self->interested.erase(static_cast<PDBGroupMonitor*>(it->get()));
|
||||
Guard G(self->lock);
|
||||
|
||||
assert(self->interested_iterating);
|
||||
|
||||
while(!self->interested_add.empty()) {
|
||||
PDBGroupPV::interested_t::iterator first(self->interested_add.begin());
|
||||
self->interested.insert(*first);
|
||||
self->interested_add.erase(first);
|
||||
}
|
||||
|
||||
temp.swap(self->interested_remove);
|
||||
for(PDBGroupPV::interested_remove_t::iterator it(temp.begin()),
|
||||
end(temp.end()); it != end; ++it)
|
||||
{
|
||||
self->interested.erase(static_cast<PDBGroupMonitor*>(it->get()));
|
||||
}
|
||||
|
||||
self->interested_iterating = false;
|
||||
|
||||
self->finalizeMonitor();
|
||||
}
|
||||
|
||||
self->interested_iterating = false;
|
||||
|
||||
self->finalizeMonitor();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,45 +145,40 @@ PDBGroupPV::connect(const std::tr1::shared_ptr<PDBProvider>& prov,
|
||||
// caller must not hold lock
|
||||
void PDBGroupPV::addMonitor(PDBGroupMonitor *mon)
|
||||
{
|
||||
bool needpost = false;
|
||||
{
|
||||
Guard G(lock);
|
||||
if(interested.empty() && interested_add.empty()) {
|
||||
// first monitor
|
||||
// start subscriptions
|
||||
Guard G(lock);
|
||||
if(interested.empty() && interested_add.empty()) {
|
||||
// first monitor
|
||||
// start subscriptions
|
||||
|
||||
size_t ievts = 0;
|
||||
for(size_t i=0; i<members.size(); i++) {
|
||||
PDBGroupPV::Info& info = members[i];
|
||||
size_t ievts = 0;
|
||||
for(size_t i=0; i<members.size(); i++) {
|
||||
PDBGroupPV::Info& info = members[i];
|
||||
|
||||
if(!!info.evt_VALUE) {
|
||||
db_event_enable(info.evt_VALUE.subscript);
|
||||
db_post_single_event(info.evt_VALUE.subscript);
|
||||
ievts++;
|
||||
info.had_initial_VALUE = false;
|
||||
} else {
|
||||
info.had_initial_VALUE = true;
|
||||
}
|
||||
assert(info.evt_PROPERTY.subscript);
|
||||
db_event_enable(info.evt_PROPERTY.subscript);
|
||||
db_post_single_event(info.evt_PROPERTY.subscript);
|
||||
if(!!info.evt_VALUE) {
|
||||
db_event_enable(info.evt_VALUE.subscript);
|
||||
db_post_single_event(info.evt_VALUE.subscript);
|
||||
ievts++;
|
||||
info.had_initial_PROPERTY = false;
|
||||
info.had_initial_VALUE = false;
|
||||
} else {
|
||||
info.had_initial_VALUE = true;
|
||||
}
|
||||
initial_waits = ievts;
|
||||
assert(info.evt_PROPERTY.subscript);
|
||||
db_event_enable(info.evt_PROPERTY.subscript);
|
||||
db_post_single_event(info.evt_PROPERTY.subscript);
|
||||
ievts++;
|
||||
info.had_initial_PROPERTY = false;
|
||||
}
|
||||
initial_waits = ievts;
|
||||
|
||||
} else if(initial_waits==0) {
|
||||
// new subscriber and already had initial update
|
||||
needpost = true;
|
||||
} // else new subscriber, but no initial update. so just wait
|
||||
} else if(initial_waits==0) {
|
||||
// new subscriber and already had initial update
|
||||
mon->post(G);
|
||||
} // else new subscriber, but no initial update. so just wait
|
||||
|
||||
if(interested_iterating)
|
||||
interested_add.insert(mon);
|
||||
else
|
||||
interested.insert(mon);
|
||||
}
|
||||
if(needpost)
|
||||
mon->post();
|
||||
if(interested_iterating)
|
||||
interested_add.insert(mon);
|
||||
else
|
||||
interested.insert(mon);
|
||||
}
|
||||
|
||||
// caller must not hold lock
|
||||
@@ -280,7 +276,8 @@ PDBGroupChannel::createMonitor(
|
||||
PDBGroupMonitor::shared_pointer ret(new PDBGroupMonitor(pv->shared_from_this(), requester, pvRequest));
|
||||
ret->weakself = ret;
|
||||
assert(!!pv->complete);
|
||||
ret->connect(pv->complete);
|
||||
guard_t G(pv->lock);
|
||||
ret->connect(G, pv->complete);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -411,7 +408,7 @@ void PDBGroupPut::get()
|
||||
PDBGroupMonitor::PDBGroupMonitor(const PDBGroupPV::shared_pointer& pv,
|
||||
const epics::pvAccess::MonitorRequester::weak_pointer &requester,
|
||||
const pvd::PVStructure::shared_pointer& pvReq)
|
||||
:BaseMonitor(requester, pvReq)
|
||||
:BaseMonitor(pv->lock, requester, pvReq)
|
||||
,pv(pv)
|
||||
{
|
||||
epics::atomic::increment(num_instances);
|
||||
@@ -446,5 +443,5 @@ void PDBGroupMonitor::onStop()
|
||||
void PDBGroupMonitor::requestUpdate()
|
||||
{
|
||||
Guard G(pv->lock);
|
||||
post();
|
||||
post(G);
|
||||
}
|
||||
|
||||
+14
-16
@@ -124,7 +124,7 @@ struct epicsShareClass PDBGroupPV : public PDBPV
|
||||
virtual
|
||||
epics::pvAccess::Channel::shared_pointer
|
||||
connect(const std::tr1::shared_ptr<PDBProvider>& prov,
|
||||
const epics::pvAccess::ChannelRequester::shared_pointer& req) OVERRIDE;
|
||||
const epics::pvAccess::ChannelRequester::shared_pointer& req) OVERRIDE FINAL;
|
||||
|
||||
void addMonitor(PDBGroupMonitor*);
|
||||
void removeMonitor(PDBGroupMonitor*);
|
||||
@@ -149,12 +149,12 @@ struct epicsShareClass PDBGroupChannel : public BaseChannel,
|
||||
|
||||
virtual epics::pvAccess::ChannelPut::shared_pointer createChannelPut(
|
||||
epics::pvAccess::ChannelPutRequester::shared_pointer const & requester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest) OVERRIDE FINAL;
|
||||
virtual epics::pvData::Monitor::shared_pointer createMonitor(
|
||||
epics::pvData::MonitorRequester::shared_pointer const & requester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest) OVERRIDE FINAL;
|
||||
|
||||
virtual void printInfo(std::ostream& out);
|
||||
virtual void printInfo(std::ostream& out) OVERRIDE FINAL;
|
||||
};
|
||||
|
||||
struct PDBGroupPut : public epics::pvAccess::ChannelPut,
|
||||
@@ -180,16 +180,14 @@ struct PDBGroupPut : public epics::pvAccess::ChannelPut,
|
||||
const epics::pvData::PVStructure::shared_pointer& pvReq);
|
||||
virtual ~PDBGroupPut();
|
||||
|
||||
virtual void destroy() { pvif.clear(); channel.reset(); requester.reset(); }
|
||||
virtual void lock() {}
|
||||
virtual void unlock() {}
|
||||
virtual std::tr1::shared_ptr<epics::pvAccess::Channel> getChannel() { return channel; }
|
||||
virtual void cancel() {}
|
||||
virtual void lastRequest() {}
|
||||
virtual void destroy() OVERRIDE FINAL { pvif.clear(); channel.reset(); requester.reset(); }
|
||||
virtual std::tr1::shared_ptr<epics::pvAccess::Channel> getChannel() OVERRIDE FINAL { return channel; }
|
||||
virtual void cancel() OVERRIDE FINAL {}
|
||||
virtual void lastRequest() OVERRIDE FINAL {}
|
||||
virtual void put(
|
||||
epics::pvData::PVStructure::shared_pointer const & pvPutStructure,
|
||||
epics::pvData::BitSet::shared_pointer const & putBitSet);
|
||||
virtual void get();
|
||||
epics::pvData::BitSet::shared_pointer const & putBitSet) OVERRIDE FINAL;
|
||||
virtual void get() OVERRIDE FINAL;
|
||||
};
|
||||
|
||||
struct PDBGroupMonitor : public BaseMonitor
|
||||
@@ -207,11 +205,11 @@ struct PDBGroupMonitor : public BaseMonitor
|
||||
const epics::pvData::PVStructure::shared_pointer& pvReq);
|
||||
virtual ~PDBGroupMonitor();
|
||||
|
||||
virtual void onStart();
|
||||
virtual void onStop();
|
||||
virtual void requestUpdate();
|
||||
virtual void onStart() OVERRIDE FINAL;
|
||||
virtual void onStop() OVERRIDE FINAL;
|
||||
virtual void requestUpdate() OVERRIDE FINAL;
|
||||
|
||||
virtual void destroy();
|
||||
virtual void destroy() OVERRIDE FINAL;
|
||||
|
||||
};
|
||||
|
||||
|
||||
+41
-51
@@ -3,10 +3,15 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <dbAccess.h>
|
||||
#include <dbChannel.h>
|
||||
#include <dbStaticLib.h>
|
||||
#include <epicsAtomic.h>
|
||||
#include <errlog.h>
|
||||
#include <dbNotify.h>
|
||||
|
||||
#include <pv/epicsException.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/configuration.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "helper.h"
|
||||
@@ -30,42 +35,31 @@ void pdb_single_event(void *user_arg, struct dbChannel *chan,
|
||||
DBEvent *evt=(DBEvent*)user_arg;
|
||||
try{
|
||||
PDBSinglePV::shared_pointer self(std::tr1::static_pointer_cast<PDBSinglePV>(((PDBSinglePV*)evt->self)->shared_from_this()));
|
||||
PDBSinglePV::interested_remove_t temp;
|
||||
{
|
||||
bool doPost;
|
||||
{
|
||||
Guard G(self->lock);
|
||||
|
||||
if(evt->dbe_mask&DBE_PROPERTY)
|
||||
self->hadevent_PROPERTY = true;
|
||||
else
|
||||
self->hadevent_VALUE = true;
|
||||
|
||||
doPost = self->hadevent_VALUE && self->hadevent_PROPERTY;
|
||||
|
||||
if(doPost)
|
||||
self->interested_iterating = true;
|
||||
}
|
||||
Guard G(self->lock);
|
||||
|
||||
// we have exclusive use of self->scratch
|
||||
self->scratch.clear();
|
||||
{
|
||||
DBScanLocker L(dbChannelRecord(self->chan));
|
||||
// dbGet() into self->complete
|
||||
self->pvif->put(self->scratch, evt->dbe_mask, pfl);
|
||||
}
|
||||
|
||||
if(!doPost)
|
||||
return;
|
||||
if(evt->dbe_mask&DBE_PROPERTY)
|
||||
self->hadevent_PROPERTY = true;
|
||||
else
|
||||
self->hadevent_VALUE = true;
|
||||
|
||||
FOREACH(PDBSinglePV::interested_t::const_iterator, it, end, self->interested) {
|
||||
PDBSingleMonitor& mon = **it;
|
||||
mon.post(self->scratch);
|
||||
}
|
||||
if(self->hadevent_VALUE && self->hadevent_PROPERTY) {
|
||||
self->interested_iterating = true;
|
||||
|
||||
PDBSinglePV::interested_remove_t temp;
|
||||
{
|
||||
Guard G(self->lock);
|
||||
|
||||
assert(self->interested_iterating);
|
||||
FOREACH(PDBSinglePV::interested_t::const_iterator, it, end, self->interested) {
|
||||
PDBSingleMonitor& mon = **it;
|
||||
// from self->complete into monitor queue element
|
||||
mon.post(G, self->scratch); // G unlocked during call
|
||||
}
|
||||
|
||||
while(!self->interested_add.empty()) {
|
||||
PDBSinglePV::interested_t::iterator first(self->interested_add.begin());
|
||||
@@ -139,33 +133,28 @@ PDBSinglePV::connect(const std::tr1::shared_ptr<PDBProvider>& prov,
|
||||
|
||||
void PDBSinglePV::addMonitor(PDBSingleMonitor* mon)
|
||||
{
|
||||
bool needpost = false;
|
||||
{
|
||||
Guard G(lock);
|
||||
if(interested.empty() && interested_add.empty()) {
|
||||
// first monitor
|
||||
// start subscription
|
||||
Guard G(lock);
|
||||
if(interested.empty() && interested_add.empty()) {
|
||||
// first monitor
|
||||
// start subscription
|
||||
|
||||
hadevent_VALUE = false;
|
||||
hadevent_PROPERTY = false;
|
||||
db_event_enable(evt_VALUE.subscript);
|
||||
db_event_enable(evt_PROPERTY.subscript);
|
||||
db_post_single_event(evt_VALUE.subscript);
|
||||
db_post_single_event(evt_PROPERTY.subscript);
|
||||
hadevent_VALUE = false;
|
||||
hadevent_PROPERTY = false;
|
||||
db_event_enable(evt_VALUE.subscript);
|
||||
db_event_enable(evt_PROPERTY.subscript);
|
||||
db_post_single_event(evt_VALUE.subscript);
|
||||
db_post_single_event(evt_PROPERTY.subscript);
|
||||
|
||||
} if(hadevent_VALUE && hadevent_PROPERTY) {
|
||||
// new subscriber and already had initial update
|
||||
needpost = true;
|
||||
} // else new subscriber, but no initial update. so just wait
|
||||
} 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
|
||||
|
||||
if(interested_iterating) {
|
||||
interested_add.insert(mon);
|
||||
} else {
|
||||
interested.insert(mon);
|
||||
}
|
||||
if(interested_iterating) {
|
||||
interested_add.insert(mon);
|
||||
} else {
|
||||
interested.insert(mon);
|
||||
}
|
||||
if(needpost)
|
||||
mon->post();
|
||||
}
|
||||
|
||||
void PDBSinglePV::removeMonitor(PDBSingleMonitor* mon)
|
||||
@@ -233,7 +222,8 @@ PDBSingleChannel::createMonitor(
|
||||
PDBSingleMonitor::shared_pointer ret(new PDBSingleMonitor(pv->shared_from_this(), requester, pvRequest));
|
||||
ret->weakself = ret;
|
||||
assert(!!pv->complete);
|
||||
ret->connect(pv->complete);
|
||||
guard_t G(pv->lock);
|
||||
ret->connect(G, pv->complete);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -428,7 +418,7 @@ void PDBSinglePut::get()
|
||||
PDBSingleMonitor::PDBSingleMonitor(const PDBSinglePV::shared_pointer& pv,
|
||||
const requester_t::shared_pointer& requester,
|
||||
const pvd::PVStructure::shared_pointer& pvReq)
|
||||
:BaseMonitor(requester, pvReq)
|
||||
:BaseMonitor(pv->lock, requester, pvReq)
|
||||
,pv(pv)
|
||||
{
|
||||
epics::atomic::increment(num_instances);
|
||||
@@ -460,5 +450,5 @@ void PDBSingleMonitor::onStop()
|
||||
void PDBSingleMonitor::requestUpdate()
|
||||
{
|
||||
guard_t G(pv->lock);
|
||||
post();
|
||||
post(G);
|
||||
}
|
||||
|
||||
+12
-13
@@ -62,9 +62,10 @@ struct epicsShareClass PDBSinglePV : public PDBPV
|
||||
|
||||
void activate();
|
||||
|
||||
virtual
|
||||
epics::pvAccess::Channel::shared_pointer
|
||||
connect(const std::tr1::shared_ptr<PDBProvider>& prov,
|
||||
const epics::pvAccess::ChannelRequester::shared_pointer& req);
|
||||
const epics::pvAccess::ChannelRequester::shared_pointer& req) OVERRIDE FINAL;
|
||||
|
||||
void addMonitor(PDBSingleMonitor*);
|
||||
void removeMonitor(PDBSingleMonitor*);
|
||||
@@ -120,16 +121,14 @@ struct PDBSinglePut : public epics::pvAccess::ChannelPut,
|
||||
const epics::pvData::PVStructure::shared_pointer& pvReq);
|
||||
virtual ~PDBSinglePut();
|
||||
|
||||
virtual void destroy() { pvif.reset(); channel.reset(); requester.reset(); }
|
||||
virtual void lock() {}
|
||||
virtual void unlock() {}
|
||||
virtual std::tr1::shared_ptr<epics::pvAccess::Channel> getChannel() { return channel; }
|
||||
virtual void cancel();
|
||||
virtual void lastRequest() {}
|
||||
virtual void destroy() OVERRIDE FINAL { pvif.reset(); channel.reset(); requester.reset(); }
|
||||
virtual std::tr1::shared_ptr<epics::pvAccess::Channel> getChannel() OVERRIDE FINAL { return channel; }
|
||||
virtual void cancel() OVERRIDE FINAL;
|
||||
virtual void lastRequest() OVERRIDE FINAL {}
|
||||
virtual void put(
|
||||
epics::pvData::PVStructure::shared_pointer const & pvPutStructure,
|
||||
epics::pvData::BitSet::shared_pointer const & putBitSet);
|
||||
virtual void get();
|
||||
epics::pvData::BitSet::shared_pointer const & putBitSet) OVERRIDE FINAL;
|
||||
virtual void get() OVERRIDE FINAL;
|
||||
};
|
||||
|
||||
struct PDBSingleMonitor : public BaseMonitor
|
||||
@@ -145,11 +144,11 @@ struct PDBSingleMonitor : public BaseMonitor
|
||||
const epics::pvData::PVStructure::shared_pointer& pvReq);
|
||||
virtual ~PDBSingleMonitor();
|
||||
|
||||
virtual void onStart();
|
||||
virtual void onStop();
|
||||
virtual void requestUpdate();
|
||||
virtual void onStart() OVERRIDE FINAL;
|
||||
virtual void onStop() OVERRIDE FINAL;
|
||||
virtual void requestUpdate() OVERRIDE FINAL;
|
||||
|
||||
virtual void destroy();
|
||||
virtual void destroy() OVERRIDE FINAL;
|
||||
};
|
||||
|
||||
#endif // PDBSINGLE_H
|
||||
|
||||
+26
-30
@@ -19,6 +19,7 @@
|
||||
#include <pv/reftrack.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "sb.h"
|
||||
#include "pvif.h"
|
||||
|
||||
#include <epicsExport.h>
|
||||
@@ -37,33 +38,22 @@ namespace pvd = epics::pvData;
|
||||
|
||||
DBCH::DBCH(dbChannel *ch) :chan(ch)
|
||||
{
|
||||
if(dbChannelOpen(chan)) {
|
||||
dbChannelDelete(chan);
|
||||
throw std::invalid_argument("Failed to open channel");
|
||||
}
|
||||
if(!chan)
|
||||
throw std::invalid_argument(std::string("Invalid channel ")+dbChannelName(ch));
|
||||
prepare();
|
||||
}
|
||||
|
||||
DBCH::DBCH(const std::string& name)
|
||||
:chan(dbChannelCreate(name.c_str()))
|
||||
{
|
||||
if(!chan)
|
||||
throw std::invalid_argument("Invalid channel");
|
||||
if(dbChannelOpen(chan)) {
|
||||
dbChannelDelete(chan);
|
||||
throw std::invalid_argument("Failed to open channel "+name);
|
||||
}
|
||||
prepare();
|
||||
}
|
||||
|
||||
DBCH::DBCH(const char *name)
|
||||
:chan(dbChannelCreate(name))
|
||||
void DBCH::prepare()
|
||||
{
|
||||
if(!chan)
|
||||
throw std::invalid_argument("Invalid channel");
|
||||
throw std::invalid_argument("NULL channel");
|
||||
if(dbChannelOpen(chan)) {
|
||||
dbChannelDelete(chan);
|
||||
throw std::invalid_argument(std::string("Failed to open channel ")+name);
|
||||
throw std::invalid_argument(SB()<<"Failed to open channel "<<dbChannelName(chan));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,6 +259,18 @@ void mapStatus(unsigned code, pvd::PVInt* status, pvd::PVString* message)
|
||||
status->put(out);
|
||||
}
|
||||
|
||||
|
||||
template<typename META>
|
||||
void putMetaImpl(const pvTimeAlarm& pv, const META& meta)
|
||||
{
|
||||
pvd::int32 nsec = meta.time.nsec;
|
||||
if(pv.nsecMask) {
|
||||
pv.userTag->put(nsec&pv.nsecMask);
|
||||
nsec &= ~pv.nsecMask;
|
||||
}
|
||||
pv.nsec->put(nsec); pv.sec->put(meta.time.secPastEpoch+POSIX_TIME_AT_EPICS_EPOCH);
|
||||
}
|
||||
|
||||
void putTime(const pvTimeAlarm& pv, unsigned dbe, db_field_log *pfl)
|
||||
{
|
||||
metaTIME meta;
|
||||
@@ -278,12 +280,7 @@ void putTime(const pvTimeAlarm& pv, unsigned dbe, db_field_log *pfl)
|
||||
if(status)
|
||||
throw std::runtime_error("dbGet for meta fails");
|
||||
|
||||
pvd::int32 nsec = meta.time.nsec;
|
||||
if(pv.nsecMask) {
|
||||
pv.userTag->put(nsec&pv.nsecMask);
|
||||
nsec &= ~pv.nsecMask;
|
||||
}
|
||||
pv.nsec->put(nsec); pv.sec->put(meta.time.secPastEpoch+POSIX_TIME_AT_EPICS_EPOCH);
|
||||
putMetaImpl(pv, meta);
|
||||
if(dbe&DBE_ALARM) {
|
||||
mapStatus(meta.status, pv.status.get(), pv.message.get());
|
||||
pv.severity->put(meta.severity);
|
||||
@@ -299,6 +296,11 @@ void putValue(dbChannel *chan, pvd::PVScalar* value, db_field_log *pfl)
|
||||
if(status)
|
||||
throw std::runtime_error("dbGet for meta fails");
|
||||
|
||||
if(nReq==0) {
|
||||
// this was an actual max length 1 array, which has zero elements now.
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
}
|
||||
|
||||
switch(dbChannelFinalFieldType(chan)) {
|
||||
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case DBR_##DBFTYPE: value->putFrom<PVATYPE>(buf.dbf_##DBFTYPE); break;
|
||||
#define CASE_ENUM
|
||||
@@ -412,7 +414,6 @@ void putValue(dbChannel *chan, pvd::PVScalarArray* value, db_field_log *pfl)
|
||||
value->putFrom(pvd::freeze(buf));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename META>
|
||||
void putMeta(const pvCommon& pv, unsigned dbe, db_field_log *pfl)
|
||||
{
|
||||
@@ -424,14 +425,8 @@ void putMeta(const pvCommon& pv, unsigned dbe, db_field_log *pfl)
|
||||
if(status)
|
||||
throw std::runtime_error("dbGet for meta fails");
|
||||
|
||||
pvd::int32 nsec = meta.time.nsec;
|
||||
if(pv.nsecMask) {
|
||||
pv.userTag->put(nsec&pv.nsecMask);
|
||||
nsec &= ~pv.nsecMask;
|
||||
}
|
||||
pv.nsec->put(nsec);
|
||||
putMetaImpl(pv, meta);
|
||||
#define FMAP(MNAME, FNAME) pv.MNAME->put(meta.FNAME)
|
||||
FMAP(sec, time.secPastEpoch+POSIX_TIME_AT_EPICS_EPOCH);
|
||||
if(dbe&DBE_ALARM) {
|
||||
mapStatus(meta.status, pv.status.get(), pv.message.get());
|
||||
FMAP(severity, severity);
|
||||
@@ -539,6 +534,7 @@ void findNSMask(pvTimeAlarm& pvmeta, dbChannel *chan, const epics::pvData::PVStr
|
||||
try{
|
||||
pvmeta.nsecMask = epics::pvData::castUnsafe<pvd::uint32>(std::string(&UT[9]));
|
||||
}catch(std::exception& e){
|
||||
pvmeta.nsecMask = 0;
|
||||
std::cerr<<dbChannelRecord(chan)->name<<" : Q:time:tag nsec:lsb: requires a number not '"<<UT[9]<<"'\n";
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -50,7 +50,6 @@ struct epicsShareClass DBCH {
|
||||
DBCH() :chan(0) {}
|
||||
explicit DBCH(dbChannel *ch); // calls dbChannelOpen()
|
||||
explicit DBCH(const std::string& name);
|
||||
explicit DBCH(const char *name);
|
||||
~DBCH();
|
||||
|
||||
void swap(DBCH&);
|
||||
@@ -62,6 +61,7 @@ struct epicsShareClass DBCH {
|
||||
private:
|
||||
DBCH(const DBCH&);
|
||||
DBCH& operator=(const DBCH&);
|
||||
void prepare();
|
||||
};
|
||||
|
||||
struct pdbRecordInfo {
|
||||
@@ -100,9 +100,13 @@ struct pdbRecordIterator {
|
||||
}
|
||||
pdbRecordIterator(const dbChannel *chan)
|
||||
{
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,16,1,0)
|
||||
dbInitEntryFromRecord(dbChannelRecord(chan), &ent);
|
||||
#else
|
||||
dbInitEntry(pdbbase, &ent);
|
||||
if(dbFindRecord(&ent, dbChannelRecord(chan)->name)!=0)
|
||||
throw std::runtime_error("Record not found");
|
||||
throw std::logic_error("Record not found");
|
||||
#endif
|
||||
m_done = false;
|
||||
}
|
||||
~pdbRecordIterator()
|
||||
|
||||
+3
-1
@@ -1,5 +1,4 @@
|
||||
|
||||
#include <epicsExport.h>
|
||||
#include <initHooks.h>
|
||||
#include <epicsExit.h>
|
||||
#include <epicsThread.h>
|
||||
@@ -12,6 +11,7 @@
|
||||
#include <dbLock.h>
|
||||
#include <dbEvent.h>
|
||||
#include <epicsVersion.h>
|
||||
#include <dbNotify.h>
|
||||
|
||||
#include <pv/reftrack.h>
|
||||
#include <pv/pvAccess.h>
|
||||
@@ -28,6 +28,8 @@
|
||||
# include "pdbgroup.h"
|
||||
#endif
|
||||
|
||||
#include <epicsExport.h>
|
||||
|
||||
namespace pva = epics::pvAccess;
|
||||
|
||||
void QSRVRegistrar_counters()
|
||||
|
||||
@@ -73,8 +73,13 @@
|
||||
|
||||
extern "C" int softIocPVA_registerRecordDeviceDriver(struct dbBase *pdbbase);
|
||||
|
||||
#ifdef __rtems__
|
||||
#define DBD_FILE "dbd/softIocPVA.dbd"
|
||||
#define EXIT_FILE "db/softIocExit.db"
|
||||
#else
|
||||
#define DBD_FILE FINAL_LOCATION "/dbd/softIocPVA.dbd"
|
||||
#define EXIT_FILE FINAL_LOCATION "/db/softIocExit.db"
|
||||
#endif
|
||||
|
||||
#ifdef VERSION_INT
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
|
||||
Reference in New Issue
Block a user