24 Commits

Author SHA1 Message Date
Michael Davidsaver 701d96fde1 start release notes 2018-04-25 09:32:38 -07:00
Michael Davidsaver 8db7a4a75e pvif handle NELM=1, NORD=0
Sometimes a DBF "scalar" isn't really a scalar.
2018-04-24 13:10:58 -07:00
Michael Davidsaver 266f2f9d2d softIocMain.cpp no FINAL_LOCATION for RTEMS 2018-04-17 17:52:43 -07:00
Michael Davidsaver 19602bb06d document info(Q:time:tag, ...) 2018-04-06 15:51:12 -07:00
Andrew Johnson 26d369c042 Add missing dependency, pdbApp must follow configure
The pdbApp/Makefile needs $(EPICS_QSRV_ABI_MAJOR_VERSION) and
$(EPICS_QSRV_ABI_MINOR_VERSION), which will only be set if the
Makefile is parsed after configure/Makefile has installed the
CONFIG_QSRV_VERSION file.
2018-04-06 15:41:05 -07:00
Michael Davidsaver 1d0be31d3f update iocimagedemo 2018-03-19 15:39:56 -07:00
Michael Davidsaver 4a0275a476 missing includes 2018-03-19 15:38:02 -07:00
Michael Davidsaver 9c10c82e82 pdb: ChannelList include groups 2018-03-08 12:24:15 -05:00
Michael Davidsaver 770152e621 more doc 2018-03-06 01:08:01 -05:00
Michael Davidsaver 272b4fb9cb update docs 2018-03-06 01:02:08 -05:00
Michael Davidsaver a22399fdf4 drop broken examples 2018-03-06 00:58:36 -05:00
Michael Davidsaver d229efbb66 pvif: DBCH cleanup
avoid some duplication
2018-02-14 11:10:09 -08:00
Michael Davidsaver 392a44b791 pvif: DBCH drop unused overload 2018-02-14 10:52:52 -08:00
Michael Davidsaver 6982fa2d8f pvif: DBCH fix NULL check 2018-02-14 10:52:37 -08:00
Michael Davidsaver b7de8ba8b0 some more OVERRIDE/FINAL 2018-02-07 11:11:25 -08:00
Michael Davidsaver a1cb3d302a QSRV implement channelList
just list record names.
2018-02-07 11:04:29 -08:00
Michael Davidsaver b6b6ac3305 fix pdb monitor locking
Guard PV*Monitor::complete to prevent
concurrent access from dbEvent callback,
and PVA server calling release().
2018-02-07 09:29:40 -08:00
Michael Davidsaver 1635e75c86 eliminate seperate mutex for BaseMonitor 2018-02-06 17:47:51 -08:00
Michael Davidsaver 6e77fc85b6 epicsExport.h must be last 2018-02-06 16:16:30 -08:00
Michael Davidsaver 8a9a3550b9 fixup dllimport/export 2018-02-06 15:29:49 -08:00
Michael Davidsaver 95a74aa134 use dbInitEntryFromRecord() from 3.16.1 2018-01-30 12:14:22 -08:00
Michael Davidsaver 9cc417103b pvif: consolidate timestamp translation 2018-01-30 08:19:31 -08:00
Michael Davidsaver 399f6b311e more doc 2018-01-15 13:09:40 -08:00
Andrew Johnson 3be469e5c2 Update version number after tagging release 2017-12-14 18:42:16 -06:00
32 changed files with 520 additions and 422 deletions
+3
View File
@@ -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
+6 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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
+2 -2
View File
@@ -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
+1 -1
View File
@@ -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 -1
View File
@@ -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
View File
@@ -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
*/
+23
View File
@@ -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
-5
View File
@@ -1,5 +0,0 @@
TOP = ../..
include $(TOP)/configure/CONFIG
ARCH = linux-x86_64-debug
TARGETS = envPaths
include $(TOP)/configure/RULES.ioc
-66
View File
@@ -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
}
-15
View File
@@ -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()
-5
View File
@@ -1,5 +0,0 @@
TOP = ../..
include $(TOP)/configure/CONFIG
ARCH = linux-x86_64-debug
TARGETS = envPaths
include $(TOP)/configure/RULES.ioc
-27
View File
@@ -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")
}
-17
View File
@@ -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()
+8 -3
View File
@@ -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"}
}
})
+6 -2
View File
@@ -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:"*"}
}
})
}
+5
View File
@@ -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>
+1
View File
@@ -12,6 +12,7 @@
#include <epicsStdlib.h>
#include <epicsGetopt.h>
#include <iocsh.h>
#include <epicsTimer.h>
#include <pv/json.h>
+1
View File
@@ -2,6 +2,7 @@
#include <epicsAtomic.h>
#include <epicsString.h>
#include <epicsTimer.h>
#include <pv/pvIntrospect.h> /* for pvdVersion.h */
#include <pv/epicsException.h>
+1
View File
@@ -7,6 +7,7 @@
#include <pv/epicsException.h>
#include <pv/monitor.h>
#include <pv/thread.h>
#include <pv/serverContext.h>
#include "server.h"
+4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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()
+5
View File
@@ -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)