pvalink: lset behavior

This commit is contained in:
Michael Davidsaver
2018-04-18 12:03:53 -07:00
parent 2934c8a1e4
commit e165919e84
6 changed files with 155 additions and 47 deletions

View File

@ -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.

View File

@ -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();
};

View File

@ -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

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;