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