pvalink: lset behavior
This commit is contained in:
@ -239,8 +239,6 @@ This option controls whether reading a value from an input PVA link
|
||||
has the addition effect of propagating any alarm via the Maximize
|
||||
Severity process.
|
||||
|
||||
@warning Not yet implemented
|
||||
|
||||
@li false - Do not maximize severity.
|
||||
@li true - Maximize alarm severity
|
||||
@li "MSI" - Maximize only if the remote severity is INVALID.
|
||||
|
@ -74,7 +74,6 @@ struct pvaLinkConfig : public jlink
|
||||
enum ms_t {
|
||||
NMS,
|
||||
MS,
|
||||
MSS,
|
||||
MSI,
|
||||
} ms;
|
||||
|
||||
@ -126,11 +125,12 @@ struct pvaLinkChannel : public pvac::ClientChannel::MonitorCallback,
|
||||
pvac::Monitor op_mon;
|
||||
pvac::Operation op_put;
|
||||
|
||||
size_t num_disconnect;
|
||||
size_t num_disconnect, num_type_change;
|
||||
bool connected;
|
||||
bool connected_latched; // connection status at the run()
|
||||
bool isatomic;
|
||||
bool queued; // added to WorkQueue
|
||||
std::tr1::shared_ptr<const void> previous_root;
|
||||
|
||||
struct LinkSort {
|
||||
bool operator()(const pvaLink *L, const pvaLink *R) const;
|
||||
@ -181,6 +181,22 @@ struct pvaLink : public pvaLinkConfig
|
||||
bool used_scratch, used_queue;
|
||||
pvd::shared_vector<const void> put_scratch, put_queue;
|
||||
|
||||
// cached fields from channel op_mon
|
||||
// updated in onTypeChange()
|
||||
epics::pvData::PVField::const_shared_pointer fld_value;
|
||||
epics::pvData::PVScalar::const_shared_pointer fld_severity,
|
||||
fld_seconds,
|
||||
fld_nanoseconds;
|
||||
epics::pvData::PVStructure::const_shared_pointer fld_display,
|
||||
fld_control,
|
||||
fld_valueAlarm;
|
||||
|
||||
// cached snapshot of alarm and timestamp
|
||||
// captured in pvaGetValue().
|
||||
// we choose not to ensure consistency with display/control meta-data
|
||||
epicsTimeStamp snap_time;
|
||||
short snap_severity;
|
||||
|
||||
pvaLink();
|
||||
virtual ~pvaLink();
|
||||
|
||||
@ -193,6 +209,7 @@ struct pvaLink : public pvaLinkConfig
|
||||
pvd::PVField::const_shared_pointer getSubField(const char *name);
|
||||
|
||||
void onDisconnect();
|
||||
void onTypeChange();
|
||||
};
|
||||
|
||||
|
||||
|
@ -42,6 +42,7 @@ pvaLinkChannel::pvaLinkChannel(const pvaGlobal_t::channels_key_t &key, const pvd
|
||||
:key(key)
|
||||
,pvRequest(pvRequest)
|
||||
,num_disconnect(0u)
|
||||
,num_type_change(0u)
|
||||
,connected(false)
|
||||
,connected_latched(false)
|
||||
,isatomic(false)
|
||||
@ -208,6 +209,8 @@ void pvaLinkChannel::run()
|
||||
if(connected && !op_mon.poll())
|
||||
return; // monitor queue is empty, nothing more to do here
|
||||
|
||||
assert(!connected || !!op_mon.root);
|
||||
|
||||
if(!connected) {
|
||||
num_disconnect++;
|
||||
|
||||
@ -219,6 +222,21 @@ void pvaLinkChannel::run()
|
||||
pvaLink *link = *it;
|
||||
link->onDisconnect();
|
||||
}
|
||||
|
||||
// Don't clear previous_root on disconnect.
|
||||
// We will usually re-connect with the same type,
|
||||
// and may get back the same PVStructure.
|
||||
|
||||
} else if(previous_root.get() != (const void*)op_mon.root.get()) {
|
||||
num_type_change++;
|
||||
|
||||
for(links_t::iterator it(links.begin()), end(links.end()); it!=end; ++it)
|
||||
{
|
||||
pvaLink *link = *it;
|
||||
link->onTypeChange();
|
||||
}
|
||||
|
||||
previous_root = std::tr1::static_pointer_cast<const void>(op_mon.root);
|
||||
}
|
||||
|
||||
// at this point we know we will re-queue, but not immediately
|
||||
@ -257,6 +275,8 @@ void pvaLinkChannel::run()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test op_mon.changed with link::fld_value to only process on value change
|
||||
|
||||
if(scan_records.empty()) {
|
||||
// Nothing to do, so don't bother locking
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include <sstream>
|
||||
|
||||
#include <epicsStdio.h> // redirects stdout/stderr
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <shareLib.h>
|
||||
|
||||
@ -30,8 +32,8 @@ using namespace pvalink;
|
||||
* "field":"blah.foo",
|
||||
* "Q":5,
|
||||
* "pipeline":false,
|
||||
* "proc":true, // false, true, none, "CP", "CPP"
|
||||
* "sevr":true, // false, true, "MSI", "MSS"
|
||||
* "proc":true, // false, true, none, "", "NPP", "PP", "CP", "CPP"
|
||||
* "sevr":true, // false, true, "NMS", "MS", "MSI", "MSS"
|
||||
* "monorder":#,// order of processing during CP scan
|
||||
* "defer":true,// whether to immediately start Put, or only queue value to be sent
|
||||
* }
|
||||
@ -143,7 +145,9 @@ jlif_result pva_parse_string(jlink *pjlink, const char *val, size_t len)
|
||||
pvt->fieldName = sval;
|
||||
|
||||
} else if(pvt->jkey=="proc") {
|
||||
if(sval=="CP") {
|
||||
if(sval.empty()) {
|
||||
pvt->pp = pvaLinkConfig::Default;
|
||||
} else if(sval=="CP") {
|
||||
pvt->pp = pvaLinkConfig::CP;
|
||||
} else if(sval=="CPP") {
|
||||
pvt->pp = pvaLinkConfig::CPP;
|
||||
@ -157,10 +161,17 @@ jlif_result pva_parse_string(jlink *pjlink, const char *val, size_t len)
|
||||
}
|
||||
|
||||
} else if(pvt->jkey=="sevr") {
|
||||
if(sval=="MSS") {
|
||||
pvt->ms = pvaLinkConfig::MSS;
|
||||
if(sval=="NMS") {
|
||||
pvt->ms = pvaLinkConfig::NMS;
|
||||
} else if(sval=="MS") {
|
||||
pvt->ms = pvaLinkConfig::MS;
|
||||
} else if(sval=="MSI") {
|
||||
pvt->ms = pvaLinkConfig::MSI;
|
||||
} else if(sval=="MSS") {
|
||||
// not sure how to handle mapping severity for MSS.
|
||||
// leave room for this to happen compatibly later by
|
||||
// handling as alias for MS until then.
|
||||
pvt->ms = pvaLinkConfig::MS;
|
||||
} else if(pvt->debug) {
|
||||
printf("pva link parsing unknown sevr depth=%u key=\"%s\" value=\"%s\"\n",
|
||||
pvt->parseDepth, pvt->jkey.c_str(), sval.c_str());
|
||||
@ -235,7 +246,6 @@ void pva_report(const jlink *rpjlink, int lvl, int indent)
|
||||
switch(pval->ms) {
|
||||
case pvaLinkConfig::NMS: printf(" NMS"); break;
|
||||
case pvaLinkConfig::MS: printf(" MS"); break;
|
||||
case pvaLinkConfig::MSS: printf(" MSS"); break;
|
||||
case pvaLinkConfig::MSI: printf(" MSI"); break;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <pv/reftrack.h>
|
||||
#include <alarm.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <shareLib.h>
|
||||
@ -15,6 +16,10 @@ pvaLink::pvaLink()
|
||||
{
|
||||
REFTRACE_INCREMENT(num_instances);
|
||||
|
||||
snap_severity = INVALID_ALARM;
|
||||
snap_time.secPastEpoch = 0;
|
||||
snap_time.nsec = 0;
|
||||
|
||||
//TODO: valgrind tells me these aren't initialized by Base, but probably should be.
|
||||
parseDepth = 0;
|
||||
parent = 0;
|
||||
@ -87,7 +92,7 @@ pvd::PVStructurePtr pvaLink::makeRequest()
|
||||
// caller must lock lchan->lock
|
||||
bool pvaLink::valid() const
|
||||
{
|
||||
return lchan->connected && lchan->op_mon.root;
|
||||
return lchan->connected_latched && lchan->op_mon.root;
|
||||
}
|
||||
|
||||
// caller must lock lchan->lock
|
||||
@ -119,9 +124,24 @@ pvd::PVField::const_shared_pointer pvaLink::getSubField(const char *name)
|
||||
// call with channel lock held
|
||||
void pvaLink::onDisconnect()
|
||||
{
|
||||
TRACE(<<"");
|
||||
// TODO: option to remain queue'd while disconnected
|
||||
|
||||
used_queue = used_scratch = false;
|
||||
}
|
||||
|
||||
void pvaLink::onTypeChange()
|
||||
{
|
||||
TRACE(<<"");
|
||||
assert(lchan->connected_latched && !!lchan->op_mon.root); // we should only be called when connected
|
||||
|
||||
fld_value = getSubField("value");
|
||||
fld_seconds = std::tr1::dynamic_pointer_cast<const pvd::PVScalar>(getSubField("timeStamp.secondsPastEpoch"));
|
||||
fld_nanoseconds = std::tr1::dynamic_pointer_cast<const pvd::PVScalar>(getSubField("timeStamp.nanoseconds"));
|
||||
fld_severity = std::tr1::dynamic_pointer_cast<const pvd::PVScalar>(getSubField("alarm.severity"));
|
||||
fld_display = std::tr1::dynamic_pointer_cast<const pvd::PVStructure>(getSubField("display"));
|
||||
fld_control = std::tr1::dynamic_pointer_cast<const pvd::PVStructure>(getSubField("control"));
|
||||
fld_valueAlarm = std::tr1::dynamic_pointer_cast<const pvd::PVStructure>(getSubField("valueAlarm"));
|
||||
}
|
||||
|
||||
} // namespace pvalink
|
||||
|
@ -1,5 +1,7 @@
|
||||
|
||||
#include <epicsString.h>
|
||||
#include <alarm.h>
|
||||
#include <recGbl.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <shareLib.h>
|
||||
@ -147,10 +149,8 @@ long pvaGetElements(const DBLINK *plink, long *nelements)
|
||||
Guard G(self->lchan->lock);
|
||||
if(!self->valid()) return -1;
|
||||
|
||||
pvd::PVField::const_shared_pointer value(self->getSubField("value"));
|
||||
|
||||
if(value && value->getField()->getType()==pvd::scalarArray)
|
||||
return static_cast<const pvd::PVScalarArray*>(value.get())->getLength();
|
||||
if(self->fld_value && self->fld_value->getField()->getType()==pvd::scalarArray)
|
||||
return static_cast<const pvd::PVScalarArray*>(self->fld_value.get())->getLength();
|
||||
else
|
||||
return 0;
|
||||
}CATCH(pvaIsConnected)
|
||||
@ -164,14 +164,43 @@ long pvaGetValue(DBLINK *plink, short dbrType, void *pbuffer,
|
||||
TRACE(<<plink->precord->name<<" "<<self->channelName);
|
||||
Guard G(self->lchan->lock);
|
||||
|
||||
pvd::PVField::const_shared_pointer value(self->getSubField("value"));
|
||||
if(!self->valid()) {
|
||||
// disconnected
|
||||
if(self->ms != pvaLink::NMS) {
|
||||
recGblSetSevr(plink->precord, self->snap_severity, LINK_ALARM);
|
||||
}
|
||||
// TODO: better capture of disconnect time
|
||||
epicsTimeGetCurrent(&self->snap_time);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// copy from 'value' into 'pbuffer'
|
||||
long status = copyPVD2DBF(value, pbuffer, dbrType, pnRequest);
|
||||
if(status) return status;
|
||||
if(self->fld_value) {
|
||||
long status = copyPVD2DBF(self->fld_value, pbuffer, dbrType, pnRequest);
|
||||
if(status) return status;
|
||||
}
|
||||
|
||||
if(self->ms != pvaLink::NMS) {
|
||||
// TODO
|
||||
if(self->fld_seconds) {
|
||||
self->snap_time.secPastEpoch = self->fld_seconds->getAs<pvd::uint32>() - POSIX_TIME_AT_EPICS_EPOCH;
|
||||
if(self->fld_nanoseconds) {
|
||||
self->snap_time.nsec = self->fld_nanoseconds->getAs<pvd::uint32>();
|
||||
} else {
|
||||
self->snap_time.nsec = 0u;
|
||||
}
|
||||
} else {
|
||||
self->snap_time.secPastEpoch = 0u;
|
||||
self->snap_time.nsec = 0u;
|
||||
}
|
||||
|
||||
if(self->fld_severity) {
|
||||
self->snap_severity = self->fld_severity->getAs<pvd::uint16>();
|
||||
} else {
|
||||
self->snap_severity = NO_ALARM;
|
||||
}
|
||||
|
||||
if((self->snap_severity!=NO_ALARM && self->ms == pvaLink::MS) ||
|
||||
(self->snap_severity==INVALID_ALARM && self->ms == pvaLink::MSI))
|
||||
{
|
||||
recGblSetSevr(plink->precord, self->snap_severity, LINK_ALARM);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -186,14 +215,14 @@ long pvaGetControlLimits(const DBLINK *plink, double *lo, double *hi)
|
||||
Guard G(self->lchan->lock);
|
||||
if(!self->valid()) return -1;
|
||||
|
||||
if(self->lchan->connected_latched) {
|
||||
if(self->fld_control) {
|
||||
pvd::PVScalar::const_shared_pointer value;
|
||||
if(lo) {
|
||||
value = std::tr1::static_pointer_cast<const pvd::PVScalar>(self->getSubField("control.limitLow"));
|
||||
value = std::tr1::static_pointer_cast<const pvd::PVScalar>(self->fld_control->getSubField("limitLow"));
|
||||
*lo = value ? value->getAs<double>() : 0.0;
|
||||
}
|
||||
if(hi) {
|
||||
value = std::tr1::static_pointer_cast<const pvd::PVScalar>(self->getSubField("control.limitHigh"));
|
||||
value = std::tr1::static_pointer_cast<const pvd::PVScalar>(self->fld_control->getSubField("limitHigh"));
|
||||
*hi = value ? value->getAs<double>() : 0.0;
|
||||
}
|
||||
} else {
|
||||
@ -208,9 +237,22 @@ long pvaGetGraphicLimits(const DBLINK *plink, double *lo, double *hi)
|
||||
{
|
||||
TRY {
|
||||
TRACE(<<plink->precord->name<<" "<<self->channelName);
|
||||
Guard G(self->lchan->lock);
|
||||
if(!self->valid()) return -1;
|
||||
//Guard G(self->lchan->lock);
|
||||
*lo = *hi = 0.0;
|
||||
|
||||
if(self->fld_display) {
|
||||
pvd::PVScalar::const_shared_pointer value;
|
||||
if(lo) {
|
||||
value = std::tr1::static_pointer_cast<const pvd::PVScalar>(self->fld_display->getSubField("limitLow"));
|
||||
*lo = value ? value->getAs<double>() : 0.0;
|
||||
}
|
||||
if(hi) {
|
||||
value = std::tr1::static_pointer_cast<const pvd::PVScalar>(self->fld_display->getSubField("limitHigh"));
|
||||
*hi = value ? value->getAs<double>() : 0.0;
|
||||
}
|
||||
} else {
|
||||
*lo = *hi = 0.0;
|
||||
}
|
||||
return 0;
|
||||
}CATCH(pvaIsConnected)
|
||||
return -1;
|
||||
@ -235,6 +277,8 @@ long pvaGetPrecision(const DBLINK *plink, short *precision)
|
||||
TRACE(<<plink->precord->name<<" "<<self->channelName);
|
||||
if(!self->valid()) return -1;
|
||||
//Guard G(self->lchan->lock);
|
||||
|
||||
// No sane way to recover precision from display.format string.
|
||||
*precision = 0;
|
||||
return 0;
|
||||
}CATCH(pvaIsConnected)
|
||||
@ -245,8 +289,21 @@ long pvaGetUnits(const DBLINK *plink, char *units, int unitsSize)
|
||||
{
|
||||
TRY {
|
||||
TRACE(<<plink->precord->name<<" "<<self->channelName);
|
||||
Guard G(self->lchan->lock);
|
||||
if(!self->valid()) return -1;
|
||||
//Guard G(self->lchan->lock);
|
||||
|
||||
if(unitsSize==0) return 0;
|
||||
|
||||
if(units && self->fld_display) {
|
||||
pvd::PVString::const_shared_pointer value(std::tr1::static_pointer_cast<const pvd::PVString>(self->fld_display->getSubField("units")));
|
||||
if(value) {
|
||||
const std::string& egu = value->get();
|
||||
strncpy(units, egu.c_str(), unitsSize);
|
||||
}
|
||||
} else if(units) {
|
||||
units[0] = '\0';
|
||||
}
|
||||
units[unitsSize-1] = '\0';
|
||||
return 0;
|
||||
}CATCH(pvaIsConnected)
|
||||
return -1;
|
||||
@ -257,19 +314,14 @@ long pvaGetAlarm(const DBLINK *plink, epicsEnum16 *status,
|
||||
{
|
||||
TRY {
|
||||
TRACE(<<plink->precord->name<<" "<<self->channelName);
|
||||
if(!self->valid()) return -1;
|
||||
Guard G(self->lchan->lock);
|
||||
epicsEnum16 stat = NO_ALARM;
|
||||
if(!self->valid()) return -1;
|
||||
|
||||
pvd::PVScalar::const_shared_pointer afld;
|
||||
|
||||
if(severity && (afld = std::tr1::static_pointer_cast<const pvd::PVScalar>(self->getSubField("alarm.severity")))) {
|
||||
*severity = afld->getAs<pvd::uint16>();
|
||||
// no direct translation for NT alarm status codes
|
||||
stat = LINK_ALARM;
|
||||
if(severity) {
|
||||
*severity = self->snap_severity;
|
||||
}
|
||||
if(status) {
|
||||
*status = stat;
|
||||
*status = self->snap_severity ? LINK_ALARM : NO_ALARM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -283,18 +335,9 @@ long pvaGetTimeStamp(const DBLINK *plink, epicsTimeStamp *pstamp)
|
||||
TRACE(<<plink->precord->name<<" "<<self->channelName);
|
||||
if(!self->valid()) return -1;
|
||||
Guard G(self->lchan->lock);
|
||||
pvd::PVScalar::const_shared_pointer afld;
|
||||
|
||||
if(afld = std::tr1::static_pointer_cast<const pvd::PVScalar>(self->getSubField("timeStamp.secondsPastEpoch"))) {
|
||||
pstamp->secPastEpoch = afld->getAs<pvd::uint32>()-POSIX_TIME_AT_EPICS_EPOCH;
|
||||
} else {
|
||||
return S_time_noProvider;
|
||||
}
|
||||
|
||||
if(afld = std::tr1::static_pointer_cast<const pvd::PVScalar>(self->getSubField("timeStamp.nanoseconds"))) {
|
||||
pstamp->nsec = afld->getAs<pvd::uint32>();
|
||||
} else {
|
||||
pstamp->nsec = 0u;
|
||||
if(pstamp) {
|
||||
*pstamp = self->snap_time;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
Reference in New Issue
Block a user