This is Unofficial EPICS BASE Doxygen Site
pvalink_lset.cpp
Go to the documentation of this file.
1 
2 #include <epicsString.h>
3 #include <alarm.h>
4 #include <recGbl.h>
5 #include <epicsStdio.h> // redirect stdout/stderr
6 
7 #include <pv/current_function.h>
8 
9 #include "pvalink.h"
10 
11 
12 namespace {
13 
14 using namespace pvalink;
15 
16 #define TRY pvaLink *self = static_cast<pvaLink*>(plink->value.json.jlink); assert(self->alive); try
17 #define CATCH() catch(std::exception& e) { \
18  errlogPrintf("pvaLink %s fails %s: %s\n", CURRENT_FUNCTION, plink->precord->name, e.what()); \
19 }
20 
21 #define CHECK_VALID() if(!self->valid()) { DEBUG(self, <<CURRENT_FUNCTION<<" "<<self->channelName<<" !valid"); return -1;}
22 
23 void pvaOpenLink(DBLINK *plink)
24 {
25  try {
26  pvaLink* self((pvaLink*)plink->value.json.jlink);
27 
28  // workaround for Base not propagating info(base:lsetDebug to us
29  {
30  pdbRecordIterator rec(plink->precord);
31 
32  if(epicsStrCaseCmp(rec.info("base:lsetDebug", "NO"), "YES")==0) {
33  self->debug = 1;
34  }
35  }
36 
37  DEBUG(self, <<plink->precord->name<<" OPEN "<<self->channelName);
38 
39  // still single threaded at this point.
40  // also, no pvaLinkChannel::lock yet
41 
42  self->plink = plink;
43 
44  if(self->channelName.empty())
45  return; // nothing to do...
46 
47  pvd::PVStructure::const_shared_pointer pvRequest(self->makeRequest());
49 
50  {
51  std::ostringstream strm;
52  strm<<*pvRequest; // print the request as a convient key for our channel cache
53 
54  key = std::make_pair(self->channelName, strm.str());
55  }
56 
57  std::tr1::shared_ptr<pvaLinkChannel> chan;
58  bool doOpen = false;
59  {
60  Guard G(pvaGlobal->lock);
61 
62  pvaGlobal_t::channels_t::iterator it(pvaGlobal->channels.find(key));
63 
64  if(it!=pvaGlobal->channels.end()) {
65  // re-use existing channel
66  chan = it->second.lock();
67  }
68 
69  if(!chan) {
70  // open new channel
71 
72  chan.reset(new pvaLinkChannel(key, pvRequest));
73  pvaGlobal->channels.insert(std::make_pair(key, chan));
74  doOpen = true;
75  }
76 
77  doOpen &= pvaGlobal->running; // if not running, then open from initHook
78  }
79 
80  if(doOpen) {
81  chan->open(); // start subscription
82  }
83 
84  if(!self->local || chan->providerName=="QSRV"){
85  Guard G(chan->lock);
86 
87  chan->links.insert(self);
88  chan->links_changed = true;
89 
90  self->lchan.swap(chan); // we are now attached
91 
92  self->lchan->debug |= !!self->debug;
93  } else {
94  // TODO: only print duing iocInit()?
95  fprintf(stderr, "%s Error: local:true link to '%s' can't be fulfilled\n",
96  plink->precord->name, self->channelName.c_str());
97  plink->lset = NULL;
98  }
99 
100  return;
101  }CATCH()
102  // on error, prevent any further calls to our lset functions
103  plink->lset = NULL;
104 }
105 
106 void pvaRemoveLink(struct dbLocker *locker, DBLINK *plink)
107 {
108  try {
109  p2p::auto_ptr<pvaLink> self((pvaLink*)plink->value.json.jlink);
110  DEBUG(self, <<plink->precord->name<<" "<<CURRENT_FUNCTION<<" "<<self->channelName);
111  assert(self->alive);
112 
113  }CATCH()
114 }
115 
116 int pvaIsConnected(const DBLINK *plink)
117 {
118  TRY {
119  Guard G(self->lchan->lock);
120 
121  bool ret = self->valid();
122  DEBUG(self, <<plink->precord->name<<" "<<CURRENT_FUNCTION<<" "<<self->channelName<<" "<<ret);
123  return ret;
124 
125  }CATCH()
126  return 0;
127 }
128 
129 int pvaGetDBFtype(const DBLINK *plink)
130 {
131  TRY {
132  Guard G(self->lchan->lock);
133  CHECK_VALID();
134 
135  // if fieldName is empty, use top struct value
136  // if fieldName not empty
137  // if sub-field is struct, use sub-struct .value
138  // if sub-field not struct, treat as value
139 
140  pvd::PVField::const_shared_pointer value(self->getSubField("value"));
141 
142  pvd::ScalarType ftype = pvd::pvInt; // default for un-mapable types.
143  if(!value) {
144  // no-op
145  } else if(value->getField()->getType()==pvd::scalar)
146  ftype = static_cast<const pvd::Scalar*>(value->getField().get())->getScalarType();
147  else if(value->getField()->getType()==pvd::scalarArray)
148  ftype = static_cast<const pvd::ScalarArray*>(value->getField().get())->getElementType();
149 
150  int ret;
151  switch(ftype) {
152 #define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case pvd::pv##PVACODE: ret = DBF_##DBFTYPE;
153 #define CASE_REAL_INT64
154 #include "pv/typemap.h"
155 #undef CASE_REAL_INT64
156 #undef CASE
157  case pvd::pvString: ret = DBF_STRING; // TODO: long string?
158  }
159 
160  DEBUG(self, <<plink->precord->name<<" "<<CURRENT_FUNCTION<<" "<<self->channelName<<" "<<dbGetFieldTypeString(ret));
161  return ret;
162 
163  }CATCH()
164  return -1;
165 }
166 
167 long pvaGetElements(const DBLINK *plink, long *nelements)
168 {
169  TRY {
170  Guard G(self->lchan->lock);
171  CHECK_VALID();
172 
173  long ret = 0;
174  if(self->fld_value && self->fld_value->getField()->getType()==pvd::scalarArray)
175  ret = static_cast<const pvd::PVScalarArray*>(self->fld_value.get())->getLength();
176 
177  DEBUG(self, <<plink->precord->name<<" "<<CURRENT_FUNCTION<<" "<<self->channelName<<" "<<ret);
178 
179  return ret;
180  }CATCH()
181  return -1;
182 }
183 
184 long pvaGetValue(DBLINK *plink, short dbrType, void *pbuffer,
185  long *pnRequest)
186 {
187  TRY {
188  Guard G(self->lchan->lock);
189 
190  if(!self->valid()) {
191  // disconnected
192  if(self->ms != pvaLink::NMS) {
193  recGblSetSevr(plink->precord, LINK_ALARM, self->snap_severity);
194  }
195  // TODO: better capture of disconnect time
196  epicsTimeGetCurrent(&self->snap_time);
197  if(self->time) {
198  plink->precord->time = self->snap_time;
199  }
200  DEBUG(self, <<CURRENT_FUNCTION<<" "<<self->channelName<<" !valid");
201  return -1;
202  }
203 
204  if(self->fld_value) {
205  long status = copyPVD2DBF(self->fld_value, pbuffer, dbrType, pnRequest);
206  if(status) {
207  DEBUG(self, <<plink->precord->name<<" "<<CURRENT_FUNCTION<<" "<<self->channelName<<" "<<status);
208  return status;
209  }
210  }
211 
212  if(self->fld_seconds) {
213  self->snap_time.secPastEpoch = self->fld_seconds->getAs<pvd::uint32>() - POSIX_TIME_AT_EPICS_EPOCH;
214  if(self->fld_nanoseconds) {
215  self->snap_time.nsec = self->fld_nanoseconds->getAs<pvd::uint32>();
216  } else {
217  self->snap_time.nsec = 0u;
218  }
219  } else {
220  self->snap_time.secPastEpoch = 0u;
221  self->snap_time.nsec = 0u;
222  }
223 
224  if(self->fld_severity) {
225  self->snap_severity = self->fld_severity->getAs<pvd::uint16>();
226  } else {
227  self->snap_severity = NO_ALARM;
228  }
229 
230  if((self->snap_severity!=NO_ALARM && self->ms == pvaLink::MS) ||
231  (self->snap_severity==INVALID_ALARM && self->ms == pvaLink::MSI))
232  {
233  recGblSetSevr(plink->precord, LINK_ALARM, self->snap_severity);
234  }
235 
236  if(self->time) {
237  plink->precord->time = self->snap_time;
238  }
239 
240  DEBUG(self, <<plink->precord->name<<" "<<CURRENT_FUNCTION<<" "<<self->channelName<<" OK");
241  return 0;
242  }CATCH()
243  return -1;
244 }
245 
246 long pvaGetControlLimits(const DBLINK *plink, double *lo, double *hi)
247 {
248  TRY {
249  Guard G(self->lchan->lock);
250  CHECK_VALID();
251 
252  if(self->fld_control) {
253  pvd::PVScalar::const_shared_pointer value;
254  if(lo) {
255  value = std::tr1::static_pointer_cast<const pvd::PVScalar>(self->fld_control->getSubField("limitLow"));
256  *lo = value ? value->getAs<double>() : 0.0;
257  }
258  if(hi) {
259  value = std::tr1::static_pointer_cast<const pvd::PVScalar>(self->fld_control->getSubField("limitHigh"));
260  *hi = value ? value->getAs<double>() : 0.0;
261  }
262  } else {
263  *lo = *hi = 0.0;
264  }
265  DEBUG(self, <<plink->precord->name<<" "<<CURRENT_FUNCTION<<" "<<self->channelName<<" "<<(lo ? *lo : 0)<<" "<<(hi ? *hi : 0));
266  return 0;
267  }CATCH()
268  return -1;
269 }
270 
271 long pvaGetGraphicLimits(const DBLINK *plink, double *lo, double *hi)
272 {
273  TRY {
274  Guard G(self->lchan->lock);
275  CHECK_VALID();
276 
277  if(self->fld_display) {
278  pvd::PVScalar::const_shared_pointer value;
279  if(lo) {
280  value = std::tr1::static_pointer_cast<const pvd::PVScalar>(self->fld_display->getSubField("limitLow"));
281  *lo = value ? value->getAs<double>() : 0.0;
282  }
283  if(hi) {
284  value = std::tr1::static_pointer_cast<const pvd::PVScalar>(self->fld_display->getSubField("limitHigh"));
285  *hi = value ? value->getAs<double>() : 0.0;
286  }
287  } else {
288  *lo = *hi = 0.0;
289  }
290  DEBUG(self, <<plink->precord->name<<" "<<CURRENT_FUNCTION<<" "<<self->channelName<<" "<<(lo ? *lo : 0)<<" "<<(hi ? *hi : 0));
291  return 0;
292  }CATCH()
293  return -1;
294 }
295 
296 long pvaGetAlarmLimits(const DBLINK *plink, double *lolo, double *lo,
297  double *hi, double *hihi)
298 {
299  TRY {
300  //Guard G(self->lchan->lock);
301  //CHECK_VALID();
302  *lolo = *lo = *hi = *hihi = 0.0;
303  DEBUG(self, <<plink->precord->name<<" "<<CURRENT_FUNCTION<<" "<<self->channelName<<" "<<(lolo ? *lolo : 0)<<" "<<(lo ? *lo : 0)<<" "<<(hi ? *hi : 0)<<" "<<(hihi ? *hihi : 0));
304  return 0;
305  }CATCH()
306  return -1;
307 }
308 
309 long pvaGetPrecision(const DBLINK *plink, short *precision)
310 {
311  TRY {
312  //Guard G(self->lchan->lock);
313  //CHECK_VALID();
314 
315  // No sane way to recover precision from display.format string.
316  *precision = 0;
317  DEBUG(self, <<plink->precord->name<<" "<<CURRENT_FUNCTION<<" "<<self->channelName<<" "<<precision);
318  return 0;
319  }CATCH()
320  return -1;
321 }
322 
323 long pvaGetUnits(const DBLINK *plink, char *units, int unitsSize)
324 {
325  TRY {
326  Guard G(self->lchan->lock);
327  CHECK_VALID();
328 
329  if(unitsSize==0) return 0;
330 
331  if(units && self->fld_display) {
332  pvd::PVString::const_shared_pointer value(std::tr1::static_pointer_cast<const pvd::PVString>(self->fld_display->getSubField("units")));
333  if(value) {
334  const std::string& egu = value->get();
335  strncpy(units, egu.c_str(), unitsSize);
336  }
337  } else if(units) {
338  units[0] = '\0';
339  }
340  units[unitsSize-1] = '\0';
341  DEBUG(self, <<plink->precord->name<<" "<<CURRENT_FUNCTION<<" "<<self->channelName<<" "<<units);
342  return 0;
343  }CATCH()
344  return -1;
345 }
346 
347 long pvaGetAlarm(const DBLINK *plink, epicsEnum16 *status,
348  epicsEnum16 *severity)
349 {
350  TRY {
351  Guard G(self->lchan->lock);
352  CHECK_VALID();
353 
354  if(severity) {
355  *severity = self->snap_severity;
356  }
357  if(status) {
358  *status = self->snap_severity ? LINK_ALARM : NO_ALARM;
359  }
360 
361  DEBUG(self, <<plink->precord->name<<" "<<CURRENT_FUNCTION<<" "<<self->channelName<<" "<<(severity ? *severity : 0)<<" "<<(status ? *status : 0));
362  return 0;
363  }CATCH()
364  return -1;
365 }
366 
367 long pvaGetTimeStamp(const DBLINK *plink, epicsTimeStamp *pstamp)
368 {
369  TRY {
370  Guard G(self->lchan->lock);
371  CHECK_VALID();
372 
373  if(pstamp) {
374  *pstamp = self->snap_time;
375  }
376 
377  DEBUG(self, <<plink->precord->name<<" "<<CURRENT_FUNCTION<<" "<<self->channelName<<" "<<(pstamp ? pstamp->secPastEpoch : 0)<<":"<<(pstamp ? pstamp->nsec: 0));
378  return 0;
379  }CATCH()
380  return -1;
381 }
382 
383 // note that we handle DBF_ENUM differently than in pvif.cpp
384 pvd::ScalarType DBR2PVD(short dbr)
385 {
386  switch(dbr) {
387 #define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case DBR_##DBFTYPE: return pvd::pv##PVACODE;
388 #define CASE_SKIP_BOOL
389 #define CASE_REAL_INT64
390 #include "pv/typemap.h"
391 #undef CASE_SKIP_BOOL
392 #undef CASE_REAL_INT64
393 #undef CASE
394  case DBF_ENUM: return pvd::pvUShort;
395  case DBF_STRING: return pvd::pvString;
396  }
397  throw std::invalid_argument("Unsupported DBR code");
398 }
399 
400 long pvaPutValue(DBLINK *plink, short dbrType,
401  const void *pbuffer, long nRequest)
402 {
403  TRY {
404  (void)self;
405  Guard G(self->lchan->lock);
406 
407  if(nRequest < 0) return -1;
408 
409  if(!self->retry && !self->valid()) {
410  DEBUG(self, <<CURRENT_FUNCTION<<" "<<self->channelName<<" !valid");
411  return -1;
412  }
413 
414  pvd::ScalarType stype = DBR2PVD(dbrType);
415 
417 
418  if(dbrType == DBF_STRING) {
419  const char *sbuffer = (const char*)pbuffer;
420  pvd::shared_vector<std::string> sval(nRequest);
421 
422  for(long n=0; n<nRequest; n++, sbuffer += MAX_STRING_SIZE) {
423  sval[n] = std::string(sbuffer, epicsStrnLen(sbuffer, MAX_STRING_SIZE));
424  }
425 
426  self->put_scratch = pvd::static_shared_vector_cast<const void>(pvd::freeze(sval));
427 
428  } else {
429  pvd::shared_vector<void> val(pvd::ScalarTypeFunc::allocArray(stype, size_t(nRequest)));
430 
431  assert(size_t(dbValueSize(dbrType)*nRequest) == val.size());
432 
433  memcpy(val.data(), pbuffer, val.size());
434 
435  self->put_scratch = pvd::freeze(val);
436  }
437 
438  self->used_scratch = true;
439 
440  if(!self->defer) self->lchan->put();
441 
442  DEBUG(self, <<plink->precord->name<<" "<<CURRENT_FUNCTION<<" "<<self->channelName<<" "<<self->lchan->op_put.valid());
443  return 0;
444  }CATCH()
445  return -1;
446 }
447 
448 void pvaScanForward(DBLINK *plink)
449 {
450  TRY {
451  Guard G(self->lchan->lock);
452 
453  if(!self->retry && !self->valid()) {
454  return;
455  }
456 
457  // FWD_LINK is never deferred, and always results in a Put
458  self->lchan->put(true);
459 
460  DEBUG(self, <<plink->precord->name<<" "<<CURRENT_FUNCTION<<" "<<self->channelName<<" "<<self->lchan->op_put.valid());
461  }CATCH()
462 }
463 
464 #undef TRY
465 #undef CATCH
466 
467 } //namespace
468 
469 namespace pvalink {
470 
471 lset pva_lset = {
472  0, 1, // non-const, volatile
473  &pvaOpenLink,
474  &pvaRemoveLink,
475  NULL, NULL, NULL,
476  &pvaIsConnected,
477  &pvaGetDBFtype,
478  &pvaGetElements,
479  &pvaGetValue,
480  &pvaGetControlLimits,
481  &pvaGetGraphicLimits,
482  &pvaGetAlarmLimits,
483  &pvaGetPrecision,
484  &pvaGetUnits,
485  &pvaGetAlarm,
486  &pvaGetTimeStamp,
487  &pvaPutValue,
488  NULL,
489  &pvaScanForward
490  //&pvaReportLink,
491 };
492 
493 } //namespace pvalink
Definition: link.h:174
PVScalar is the base class for each scalar field.
Definition: pvData.h:272
#define assert(exp)
Declare that a condition should be true.
Definition: epicsAssert.h:70
pointer data() const
Return Base pointer.
Definition: sharedVector.h:616
pvd::Status status
uint16_t uint16
Definition: pvType.h:95
ScalarType getScalarType(const string &pvalue)
Definition: TypeFunc.cpp:69
const char * info(const char *key, const char *def=0)
Definition: pvif.h:185
shared_ptr< T > static_pointer_cast(shared_ptr< U > const &r) BOOST_NOEXCEPT
Definition: shared_ptr.hpp:788
long copyPVD2DBF(const pvd::PVField::const_shared_pointer &inraw, void *outbuf, short outdbf, long *outnReq)
Definition: dbf_copy.cpp:38
#define NULL
Definition: catime.c:38
shared_vector< void > allocArray(ScalarType id, size_t len)
Allocate an untyped array based on ScalarType.
Definition: TypeFunc.cpp:104
int epicsStrCaseCmp(const char *s1, const char *s2)
Definition: epicsString.c:191
epicsUInt32 secPastEpoch
seconds since 0000 Jan 1, 1990
Definition: epicsTime.h:34
int epicsStdCall epicsTimeGetCurrent(epicsTimeStamp *pDest)
Get current time into *pDest.
#define NO_ALARM
The NO_ALARM value can be used as both a severity and a status.
Definition: alarm.h:32
#define POSIX_TIME_AT_EPICS_EPOCH
The EPICS Epoch is 00:00:00 Jan 1, 1990 UTC.
Definition: epicsTime.h:26
#define LINK_ALARM
Definition: alarm.h:105
size_t size() const
Number of elements visible through this vector.
Definition: sharedVector.h:220
char * pbuffer
Definition: errlog.c:85
epicsUInt16 epicsEnum16
Definition: epicsTypes.h:47
size_t epicsStrnLen(const char *s, size_t maxlen)
Definition: epicsString.c:268
#define MAX_STRING_SIZE
Definition: epicsTypes.h:65
EPICS time stamp, for use from C code.
Definition: epicsTime.h:33
const char * dbGetFieldTypeString(int dbfType)
Definition: dbStaticLib.c:2988
#define stderr
Definition: epicsStdio.h:32
#define CURRENT_FUNCTION
struct json_link json
Definition: link.h:177
#define INVALID_ALARM
Definition: alarm.h:53
T getAs() const
Definition: pvData.h:302
epicsUInt32 nsec
nanoseconds within second
Definition: epicsTime.h:35
uint32_t uint32
Definition: pvType.h:99