pva2pva  1.4.0
pvif.cpp
1 
2 
3 #include <pv/pvIntrospect.h> /* for pvdVersion.h */
4 #include <pv/standardField.h>
5 
6 #include <dbAccess.h>
7 #include <dbChannel.h>
8 #include <dbStaticLib.h>
9 #include <dbLock.h>
10 #include <dbEvent.h>
11 #include <alarm.h>
12 #include <errSymTbl.h>
13 #include <epicsVersion.h>
14 #include <errlog.h>
15 #include <osiSock.h>
16 
17 #include <pv/status.h>
18 #include <pv/bitSet.h>
19 #include <pv/pvData.h>
20 #include <pv/anyscalar.h>
21 #include <pv/reftrack.h>
22 #include <pv/pvAccess.h>
23 #include <pv/security.h>
24 
25 #include "sb.h"
26 #include "pvif.h"
27 
28 #include <epicsExport.h>
29 
30 #ifdef EPICS_VERSION_INT
31 # if EPICS_VERSION_INT>=VERSION_INT(3,16,1,0)
32 # define USE_INT64
33  // effects all uses of pv/typemap.h
34 # define CASE_REAL_INT64
35 # endif
36 #endif
37 
38 namespace pvd = epics::pvData;
39 namespace pva = epics::pvAccess;
40 
41 DBCH::DBCH(dbChannel *ch) :chan(ch)
42 {
43  prepare();
44 }
45 
46 DBCH::DBCH(const std::string& name)
47  :chan(dbChannelCreate(name.c_str()))
48 {
49  prepare();
50 }
51 
52 void DBCH::prepare()
53 {
54  if(!chan)
55  throw std::invalid_argument("NULL channel");
56  if(dbChannelOpen(chan)) {
57  dbChannelDelete(chan);
58  throw std::invalid_argument(SB()<<"Failed to open channel "<<dbChannelName(chan));
59  }
60 }
61 
62 DBCH::~DBCH()
63 {
64  if(chan) dbChannelDelete(chan);
65 }
66 
67 void DBCH::swap(DBCH& o)
68 {
69  std::swap(chan, o.chan);
70 }
71 
72 void ASCred::update(const pva::ChannelRequester::shared_pointer& req)
73 {
74  pva::PeerInfo::const_shared_pointer info(req->getPeerInfo());
75  std::string usertemp, hosttemp;
76 
77  if(info && info->identified) {
78  hosttemp = info->peer;
79  if(info->authority=="ca") {
80  usertemp = info->account;
81  size_t sep = usertemp.find_last_of('/');
82  if(sep != std::string::npos) {
83  // prevent CA auth from claiming to be eg. "krb/someone.special"
84  usertemp = usertemp.substr(sep+1);
85  }
86 
87  } else {
88  usertemp = info->authority + "/" + info->account;
89  }
90 
91  const char role[] = "role/";
92 
93  groups.resize(info->roles.size());
94  size_t idx = 0u;
95  for(pva::PeerInfo::roles_t::const_iterator it(info->roles.begin()), end(info->roles.end()); it!=end; ++it, idx++) {
96  groups[idx].resize((*it).size()+sizeof(role)); // sizeof(role) includes trailing nil
97  std::copy(role,
98  role+sizeof(role)-1,
99  groups[idx].begin());
100  std::copy(it->begin(),
101  it->end(),
102  groups[idx].begin()+sizeof(role)-1);
103  groups[idx][groups[idx].size()-1] = '\0';
104  }
105 
106  } else {
107  // legacy and anonymous
108  hosttemp = req->getRequesterName();
109  }
110 
111  // remote names have the form "IP:port"
112  size_t sep = hosttemp.find_first_of(':');
113  if(sep == std::string::npos) {
114  sep = hosttemp.size();
115  }
116  hosttemp.resize(sep);
117 
118  host.resize(hosttemp.size()+1);
119  std::copy(hosttemp.begin(),
120  hosttemp.end(),
121  host.begin());
122  host[hosttemp.size()] = '\0';
123 
124  user.resize(usertemp.size()+1);
125  std::copy(usertemp.begin(),
126  usertemp.end(),
127  user.begin());
128  user[usertemp.size()] = '\0';
129 }
130 
131 ASCLIENT::~ASCLIENT()
132 {
133  asRemoveClient(&aspvt);
134  for(size_t i=0, N=grppvt.size(); i<N; i++) {
135  asRemoveClient(&grppvt[i]);
136  }
137 }
138 
139 void ASCLIENT::add(dbChannel* chan, ASCred& cred)
140 {
141  asRemoveClient(&aspvt);
142  /* asAddClient() fails secure to no-permission */
143  (void)asAddClient(&aspvt, dbChannelRecord(chan)->asp, dbChannelFldDes(chan)->as_level, &cred.user[0], &cred.host[0]);
144 
145  grppvt.resize(cred.groups.size(), 0);
146 
147  for(size_t i=0, N=grppvt.size(); i<N; i++) {
148  asRemoveClient(&grppvt[i]);
149  (void)asAddClient(&grppvt[i], dbChannelRecord(chan)->asp, dbChannelFldDes(chan)->as_level, &cred.groups[i][0], &cred.host[0]);
150  }
151 }
152 
153 bool ASCLIENT::canWrite() {
154  if(!asActive || (aspvt && asCheckPut(aspvt)))
155  return true;
156  for(size_t i=0, N=grppvt.size(); i<N; i++) {
157  if(grppvt[i] && asCheckPut(grppvt[i]))
158  return true;
159  }
160  return false;
161 }
162 
163 PVIF::PVIF(dbChannel *ch)
164  :chan(ch)
165 {}
166 
167 namespace {
168 
169 struct pvTimeAlarm {
170  dbChannel *chan;
171 
172  pvd::uint32 nsecMask;
173 
174  pvd::BitSet maskALWAYS, maskALARM;
175 
176  pvd::PVLongPtr sec;
177  pvd::PVIntPtr status, severity, nsec, userTag;
178  pvd::PVStringPtr message;
179 
180  pvTimeAlarm() :chan(NULL), nsecMask(0) {}
181 };
182 
183 struct pvCommon : public pvTimeAlarm {
184 
185  pvd::BitSet maskVALUE, maskPROPERTY, maskVALUEPut;
186 
187  pvd::PVDoublePtr displayLow, displayHigh, controlLow, controlHigh;
188  pvd::PVStringPtr egu, desc;
189  pvd::PVIntPtr fmt, prec;
190 
191  pvd::PVScalarPtr warnLow, warnHigh, alarmLow, alarmHigh;
192 
193  pvd::PVStringArrayPtr enumopts;
194 };
195 
196 struct pvScalar : public pvCommon {
197  typedef pvd::PVScalar pvd_type;
198  pvd::PVScalarPtr value;
199 };
200 
201 struct pvArray : public pvCommon {
202  typedef pvd::PVScalarArray pvd_type;
203  pvd::PVScalarArrayPtr value;
204 };
205 
206 struct metaTIME {
207  DBRstatus
208  DBRamsg
209  DBRtime
210  DBRutag
211 
212  enum {mask = DBR_STATUS | DBR_AMSG | DBR_TIME | DBR_UTAG};
213 };
214 
215 struct metaDOUBLE {
216  DBRstatus
217  DBRamsg
218  DBRunits
219  DBRprecision
220  DBRtime
221  DBRutag
222  DBRgrDouble
223  DBRctrlDouble
224  DBRalDouble
225 
226  // similar junk
227  DBRenumStrs
228 
229  enum {mask = DBR_STATUS | DBR_AMSG | DBR_UNITS | DBR_PRECISION | DBR_TIME | DBR_UTAG | DBR_GR_DOUBLE | DBR_CTRL_DOUBLE | DBR_AL_DOUBLE};
230 };
231 
232 struct metaENUM {
233  DBRstatus
234  DBRamsg
235  DBRtime
236  DBRutag
237  DBRenumStrs
238 
239  // similar junk
240  DBRunits
241  DBRprecision
242  DBRgrDouble
243  DBRctrlDouble
244  DBRalDouble
245 
246  enum {mask = DBR_STATUS | DBR_AMSG | DBR_TIME | DBR_UTAG | DBR_ENUM_STRS};
247 };
248 
249 struct metaSTRING {
250  DBRstatus
251  DBRamsg
252  DBRtime
253  DBRutag
254 
255  // similar junk
256  DBRenumStrs
257  DBRunits
258  DBRprecision
259  DBRgrDouble
260  DBRctrlDouble
261  DBRalDouble
262 
263  enum {mask = DBR_STATUS | DBR_AMSG | DBR_TIME | DBR_UTAG};
264 };
265 
266 void attachTime(pvTimeAlarm& pvm, const pvd::PVStructurePtr& pv)
267 {
268 #define FMAP(MNAME, PVT, FNAME, DBE) pvm.MNAME = pv->getSubFieldT<pvd::PVT>(FNAME); \
269  pvm.mask ## DBE.set(pvm.MNAME->getFieldOffset())
270  FMAP(status, PVInt, "alarm.status", ALARM);
271  FMAP(severity, PVInt, "alarm.severity", ALARM);
272  FMAP(message, PVString, "alarm.message", ALARM);
273  FMAP(sec, PVLong, "timeStamp.secondsPastEpoch", ALWAYS);
274  FMAP(nsec, PVInt, "timeStamp.nanoseconds", ALWAYS);
275 #ifdef HAVE_UTAG
276  FMAP(userTag, PVInt, "timeStamp.userTag", ALWAYS);
277 #endif
278 #undef FMAP
279 }
280 
281 static
282 pvd::shared_vector<const std::string> buildFormats()
283 {
284  pvd::shared_vector<std::string> fmt;
285  fmt.push_back("Default");
286  fmt.push_back("String");
287  fmt.push_back("Binary");
288  fmt.push_back("Decimal");
289  fmt.push_back("Hex");
290  fmt.push_back("Exponential");
291  fmt.push_back("Engineering");
292  return pvd::freeze(fmt);
293 }
294 
295 static const
296 pvd::shared_vector<const std::string> displayForms(buildFormats());
297 
298 // lookup fields and populate pvCommon. Non-existant fields will be NULL.
299 void attachMeta(pvCommon& pvm, const pvd::PVStructurePtr& pv)
300 {
301  {
302  pvd::PVStructurePtr fmt(pv->getSubField<pvd::PVStructure>("display.form"));
303  if(fmt) {
304  fmt->getSubFieldT<pvd::PVStringArray>("choices")->replace(displayForms);
305  }
306  }
307  attachTime(pvm, pv);
308 #define FMAP(MNAME, PVT, FNAME, DBE) pvm.MNAME = pv->getSubField<pvd::PVT>(FNAME); \
309  if(pvm.MNAME) pvm.mask ## DBE.set(pvm.MNAME->getFieldOffset())
310  FMAP(displayHigh, PVDouble, "display.limitHigh", PROPERTY);
311  FMAP(displayLow, PVDouble, "display.limitLow", PROPERTY);
312  FMAP(controlHigh, PVDouble, "control.limitHigh", PROPERTY);
313  FMAP(controlLow, PVDouble, "control.limitLow", PROPERTY);
314  FMAP(egu, PVString, "display.units", PROPERTY);
315  FMAP(desc, PVString, "display.description", PROPERTY);
316  FMAP(prec, PVInt, "display.precision", PROPERTY);
317  FMAP(fmt, PVInt, "display.form.index", PROPERTY);
318  FMAP(warnHigh, PVScalar, "valueAlarm.highWarningLimit", PROPERTY);
319  FMAP(warnLow, PVScalar, "valueAlarm.lowWarningLimit", PROPERTY);
320  FMAP(alarmHigh, PVScalar, "valueAlarm.highAlarmLimit", PROPERTY);
321  FMAP(alarmLow, PVScalar, "valueAlarm.lowAlarmLimit", PROPERTY);
322  FMAP(enumopts, PVStringArray, "value.choices", PROPERTY);
323 #undef FMAP
324 }
325 
326 template<typename PVX>
327 void attachAll(PVX& pvm, const pvd::PVStructurePtr& pv)
328 {
329  pvm.value = pv->getSubField<typename PVX::pvd_type>("value.index");
330  if(!pvm.value)
331  pvm.value = pv->getSubFieldT<typename PVX::pvd_type>("value");
332  const pvd::PVField *fld = pvm.value.get();
333  pvm.maskVALUE.set(fld->getFieldOffset());
334  for(;fld; fld = fld->getParent()) {
335  // set field bit and all enclosing structure bits
336  pvm.maskVALUEPut.set(fld->getFieldOffset());
337  }
338  pvm.maskVALUEPut.set(0);
339  attachMeta(pvm, pv);
340 }
341 
342 template<typename Meta>
343 void mapStatus(const Meta& meta, pvd::PVInt* status, pvd::PVString* message)
344 {
345 #ifdef HAVE_UTAG
346  if(meta.amsg[0]!='\0') {
347  message->put(meta.amsg);
348  } else
349 #endif
350  if(meta.status<ALARM_NSTATUS)
351  message->put(epicsAlarmConditionStrings[meta.status]);
352  else
353  message->put("???");
354 
355  // Arbitrary mapping from DB status codes
356  unsigned out;
357  switch(meta.status) {
358  case NO_ALARM:
359  out = 0;
360  break;
361  case READ_ALARM:
362  case WRITE_ALARM:
363  case HIHI_ALARM:
364  case HIGH_ALARM:
365  case LOLO_ALARM:
366  case LOW_ALARM:
367  case STATE_ALARM:
368  case COS_ALARM:
369  case HW_LIMIT_ALARM:
370  out = 1; // DEVICE
371  break;
372  case COMM_ALARM:
373  case TIMEOUT_ALARM:
374  case UDF_ALARM:
375  out = 2; // DRIVER
376  break;
377  case CALC_ALARM:
378  case SCAN_ALARM:
379  case LINK_ALARM:
380  case SOFT_ALARM:
381  case BAD_SUB_ALARM:
382  out = 3; // RECORD
383  break;
384  case DISABLE_ALARM:
385  case SIMM_ALARM:
386  case READ_ACCESS_ALARM:
387  case WRITE_ACCESS_ALARM:
388  out = 4; // DB
389  break;
390  default:
391  out = 6; // UNDEFINED
392  }
393 
394  status->put(out);
395 }
396 
397 
398 template<typename META>
399 void putMetaImpl(const pvTimeAlarm& pv, const META& meta)
400 {
401  pvd::int32 nsec = meta.time.nsec;
402  if(pv.nsecMask) {
403  pv.userTag->put(nsec&pv.nsecMask);
404  nsec &= ~pv.nsecMask;
405 #ifdef HAVE_UTAG
406  } else {
407  pv.userTag->put(meta.utag);
408 #endif
409  }
410  pv.nsec->put(nsec); pv.sec->put(meta.time.secPastEpoch+POSIX_TIME_AT_EPICS_EPOCH);
411 }
412 
413 void putTime(const pvTimeAlarm& pv, unsigned dbe, db_field_log *pfl)
414 {
415  metaTIME meta;
416  long options = (int)metaTIME::mask, nReq = 0;
417 
418  long status = dbChannelGet(pv.chan, dbChannelFinalFieldType(pv.chan), &meta, &options, &nReq, pfl);
419  if(status)
420  throw std::runtime_error("dbGet for meta fails");
421 
422  putMetaImpl(pv, meta);
423  if(dbe&DBE_ALARM) {
424  mapStatus(meta, pv.status.get(), pv.message.get());
425  pv.severity->put(meta.severity);
426  }
427 }
428 
429 void putValue(dbChannel *chan, pvd::PVScalar* value, db_field_log *pfl)
430 {
431  dbrbuf buf;
432  long nReq = 1;
433 
434  long status = dbChannelGet(chan, dbChannelFinalFieldType(chan), &buf, NULL, &nReq, pfl);
435  if(status)
436  throw std::runtime_error("dbGet for meta fails");
437 
438  if(nReq==0) {
439  // this was an actual max length 1 array, which has zero elements now.
440  memset(&buf, 0, sizeof(buf));
441  }
442 
443  switch(dbChannelFinalFieldType(chan)) {
444 #define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case DBR_##DBFTYPE: value->putFrom<PVATYPE>(buf.dbf_##DBFTYPE); break;
445 #define CASE_ENUM
446 #define CASE_SKIP_BOOL
447 #include "pv/typemap.h"
448 #undef CASE_ENUM
449 #undef CASE_SKIP_BOOL
450 #undef CASE
451  case DBR_STRING:
452  buf.dbf_STRING[sizeof(buf.dbf_STRING)-1] = '\0';
453  value->putFrom<std::string>(buf.dbf_STRING);
454  break;
455  default:
456  throw std::runtime_error("putValue unsupported DBR code");
457  }
458 }
459 
460 void getValue(dbChannel *chan, pvd::PVScalar* value)
461 {
462  dbrbuf buf;
463 
464  switch(dbChannelFinalFieldType(chan)) {
465 #define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case DBR_##DBFTYPE: buf.dbf_##DBFTYPE = value->getAs<PVATYPE>(); break;
466 #define CASE_ENUM
467 #define CASE_SKIP_BOOL
468 #include "pv/typemap.h"
469 #undef CASE_ENUM
470 #undef CASE_SKIP_BOOL
471 #undef CASE
472  case DBR_STRING:
473  {
474  std::string val(value->getAs<std::string>());
475  strncpy(buf.dbf_STRING, val.c_str(), sizeof(buf.dbf_STRING));
476  buf.dbf_STRING[sizeof(buf.dbf_STRING)-1] = '\0';
477  }
478  break;
479  default:
480  throw std::runtime_error("getValue unsupported DBR code");
481  }
482 
483  long status = dbChannelPut(chan, dbChannelFinalFieldType(chan), &buf, 1);
484  if(status)
485  throw std::runtime_error("dbPut for meta fails");
486 }
487 
488 void getValue(dbChannel *chan, pvd::PVScalarArray* value)
489 {
490  short dbr = dbChannelFinalFieldType(chan);
491 
492  if(dbr!=DBR_STRING) {
493  pvd::shared_vector<const void> buf;
494 
495  value->getAs(buf);
496  long nReq = buf.size()/pvd::ScalarTypeFunc::elementSize(value->getScalarArray()->getElementType());
497 
498  long status = dbChannelPut(chan, dbr, buf.data(), nReq);
499  if(status)
500  throw std::runtime_error("dbChannelPut fails");
501 
502  } else {
503  pvd::shared_vector<const std::string> buf;
504 
505  value->getAs(buf);
506 
507  std::vector<char> temp(buf.size()*MAX_STRING_SIZE);
508 
509  for(size_t i=0, N=buf.size(); i<N; i++)
510  {
511  strncpy(&temp[i*MAX_STRING_SIZE], buf[i].c_str(), MAX_STRING_SIZE-1);
512  temp[i*MAX_STRING_SIZE + MAX_STRING_SIZE-1] = '\0';
513  }
514 
515  long status = dbChannelPut(chan, dbr, &temp[0], buf.size());
516  if(status)
517  throw std::runtime_error("dbChannelPut fails");
518  }
519 }
520 
521 void putValue(dbChannel *chan, pvd::PVScalarArray* value, db_field_log *pfl)
522 {
523  const short dbr = dbChannelFinalFieldType(chan);
524 
525  long nReq = dbChannelFinalElements(chan);
526  const pvd::ScalarType etype = value->getScalarArray()->getElementType();
527 
528  if(dbr!=DBR_STRING) {
529 
530  pvd::shared_vector<void> buf(pvd::ScalarTypeFunc::allocArray(etype, nReq)); // TODO: pool?
531 
532  long status = dbChannelGet(chan, dbr, buf.data(), NULL, &nReq, pfl);
533  if(status)
534  throw std::runtime_error("dbChannelGet for value fails");
535 
536  buf.slice(0, nReq*pvd::ScalarTypeFunc::elementSize(etype));
537 
538  value->putFrom(pvd::freeze(buf));
539 
540  } else {
541  std::vector<char> temp(nReq*MAX_STRING_SIZE);
542 
543  long status = dbChannelGet(chan, dbr, &temp[0], NULL, &nReq, pfl);
544  if(status)
545  throw std::runtime_error("dbChannelGet for value fails");
546 
547  pvd::shared_vector<std::string> buf(nReq);
548  for(long i=0; i<nReq; i++) {
549  temp[i*MAX_STRING_SIZE + MAX_STRING_SIZE-1] = '\0';
550  buf[i] = std::string(&temp[i*MAX_STRING_SIZE]);
551  }
552 
553  value->putFrom(pvd::freeze(buf));
554  }
555 }
556 template<typename META>
557 void putMeta(const pvCommon& pv, unsigned dbe, db_field_log *pfl)
558 {
559  META meta;
560  long options = (int)META::mask, nReq = 0;
561  dbCommon *prec = dbChannelRecord(pv.chan);
562 
563  long status = dbChannelGet(pv.chan, dbChannelFinalFieldType(pv.chan), &meta, &options, &nReq, pfl);
564  if(status)
565  throw std::runtime_error("dbGet for meta fails");
566 
567  putMetaImpl(pv, meta);
568 #define FMAP(MNAME, FNAME) pv.MNAME->put(meta.FNAME)
569  if(dbe&DBE_ALARM) {
570  mapStatus(meta, pv.status.get(), pv.message.get());
571  FMAP(severity, severity);
572  }
573  if(dbe&DBE_PROPERTY) {
574 #undef FMAP
575  if(pv.desc) pv.desc->put(prec->desc);
576 #define FMAP(MASK, MNAME, FNAME) if(META::mask&(MASK) && pv.MNAME) pv.MNAME->put(meta.FNAME)
577  FMAP(DBR_GR_DOUBLE, displayHigh, upper_disp_limit);
578  FMAP(DBR_GR_DOUBLE, displayLow, lower_disp_limit);
579  FMAP(DBR_CTRL_DOUBLE, controlHigh, upper_ctrl_limit);
580  FMAP(DBR_CTRL_DOUBLE, controlLow, lower_ctrl_limit);
581  FMAP(DBR_GR_DOUBLE, egu, units);
582 #undef FMAP
583  if(META::mask&DBR_PRECISION && pv.prec) {
584  pv.prec->put(pvd::int32(meta.precision.dp));
585  }
586 #define FMAP(MASK, MNAME, FNAME) if(META::mask&(MASK) && pv.MNAME) pv.MNAME->putFrom(meta.FNAME)
587  // not handling precision until I get a better idea of what 'format' is supposed to be...
588  //FMAP(prec, PVScalar, "display.form", PROPERTY);
589  FMAP(DBR_AL_DOUBLE, warnHigh, upper_warning_limit);
590  FMAP(DBR_AL_DOUBLE, warnLow, lower_warning_limit);
591  FMAP(DBR_AL_DOUBLE, alarmHigh, upper_alarm_limit);
592  FMAP(DBR_AL_DOUBLE, alarmLow, lower_alarm_limit);
593 #undef FMAP
594  if(pv.enumopts) {
595  pvd::shared_vector<std::string> strs(meta.no_str);
596  for(size_t i=0; i<strs.size(); i++)
597  {
598  meta.strs[i][sizeof(meta.strs[i])-1] = '\0';
599  strs[i] = meta.strs[i];
600  }
601  pv.enumopts->replace(pvd::freeze(strs));
602  }
603  }
604 }
605 
606 template<typename PVC, typename META>
607 void putAll(const PVC &pv, unsigned dbe, db_field_log *pfl)
608 {
609  if(dbe&(DBE_VALUE|DBE_ARCHIVE)) {
610  putValue(pv.chan, pv.value.get(), pfl);
611  }
612  if(!(dbe&DBE_PROPERTY)) {
613  putTime(pv, dbe, pfl);
614  } else {
615  putMeta<META>(pv, dbe, pfl);
616  }
617 }
618 
619 void findNSMask(pvTimeAlarm& pvmeta, pdbRecordIterator& info, const epics::pvData::PVStructurePtr& pvalue)
620 {
621  const char *UT = info.info("Q:time:tag");
622  if(UT && strncmp(UT, "nsec:lsb:", 9)==0) {
623  try{
624  pvmeta.nsecMask = epics::pvData::castUnsafe<pvd::uint32>(std::string(&UT[9]));
625  }catch(std::exception& e){
626  pvmeta.nsecMask = 0;
627  std::cerr<<info.name()<<" : Q:time:tag nsec:lsb: requires a number not '"<<UT[9]<<"'\n";
628  }
629  }
630  if(pvmeta.nsecMask>0 && pvmeta.nsecMask<=32) {
631  pvmeta.userTag = pvalue->getSubField<pvd::PVInt>("timeStamp.userTag");
632  if(!pvmeta.userTag) {
633  pvmeta.nsecMask = 0; // struct doesn't have userTag
634  } else {
635  pvd::uint64 mask = (1<<pvmeta.nsecMask)-1;
636  pvmeta.nsecMask = mask;
637  pvmeta.maskALWAYS.set(pvmeta.userTag->getFieldOffset());
638  }
639  } else
640  pvmeta.nsecMask = 0;
641 }
642 
643 void findFormat(pvTimeAlarm& pvmeta, pdbRecordIterator& info, const epics::pvData::PVStructurePtr& pvalue)
644 {
645  const char *FMT = info.info("Q:form");
646  if(FMT) {
647  pvd::PVScalarPtr fmt(pvalue->getSubField<pvd::PVScalar>("display.form.index"));
648  if(fmt) {
649  bool found = false;
650  for(size_t i=0; !found && i<displayForms.size(); i++) {
651  if((found=(displayForms[i]==FMT)))
652  fmt->putFrom<pvd::uint32>(i);
653  }
654  if(!found) {
655  try {
656  fmt->putFrom(std::string(FMT)); // attempt to parse as number
657  }catch(std::exception& e){
658  errlogPrintf("%s: info(Q:form, \"%s\") is not known format: %s\n", info.name(), FMT, e.what());
659  }
660  }
661  }
662  }
663 }
664 
665 pvd::Status checkDISP(dbChannel *chan)
666 {
667  dbCommon *prec = dbChannelRecord(chan);
668  pvd::Status ret;
669  if(prec->disp && dbChannelField(chan)!=&prec->disp)
670  ret = pvd::Status::error("Put Disabled");
671  return ret;
672 }
673 
674 template<typename PVX, typename META>
675 struct PVIFScalarNumeric : public PVIF
676 {
677  PVX pvmeta;
678  const epics::pvData::PVStructurePtr pvalue;
679 
680  PVIFScalarNumeric(dbChannel *ch, const epics::pvData::PVFieldPtr& p, pvd::PVField *enclosing)
681  :PVIF(ch)
682  ,pvalue(std::tr1::dynamic_pointer_cast<pvd::PVStructure>(p))
683  {
684  if(!pvalue)
685  throw std::runtime_error("Must attach to structure");
686 
687  pvmeta.chan = ch;
688  attachAll<PVX>(pvmeta, pvalue);
689  if(enclosing) {
690  size_t bit = enclosing->getFieldOffset();
691  // we are inside a structure array or similar with only one bit for all ours fields
692  pvmeta.maskALWAYS.clear();
693  pvmeta.maskALWAYS.set(bit);
694  pvmeta.maskVALUE.clear();
695  pvmeta.maskVALUE.set(bit);
696  pvmeta.maskALARM.clear();
697  pvmeta.maskALARM.set(bit);
698  pvmeta.maskPROPERTY.clear();
699  pvmeta.maskPROPERTY.set(bit);
700  pvmeta.maskVALUEPut.clear();
701  pvmeta.maskVALUEPut.set(0);
702  pvmeta.maskVALUEPut.set(bit);
703  }
704  pdbRecordIterator info(chan);
705  findNSMask(pvmeta, info, pvalue);
706  findFormat(pvmeta, info, pvalue);
707  }
708  virtual ~PVIFScalarNumeric() {}
709 
710  virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
711  {
712  try{
713  putAll<PVX, META>(pvmeta, dbe, pfl);
714  mask |= pvmeta.maskALWAYS;
715  if(dbe&(DBE_VALUE|DBE_ARCHIVE))
716  mask |= pvmeta.maskVALUE;
717  if(dbe&DBE_ALARM)
718  mask |= pvmeta.maskALARM;
719  if(dbe&DBE_PROPERTY)
720  mask |= pvmeta.maskPROPERTY;
721  }catch(...){
722  pvmeta.severity->put(3);
723  mask |= pvmeta.maskALARM;
724  throw;
725  }
726  }
727 
728  virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc, bool permit) OVERRIDE FINAL
729  {
730  pvd::Status ret = checkDISP(chan);
731  if(!ret)
732  return ret;
733 
734  bool newval = mask.logical_and(pvmeta.maskVALUEPut);
735  if(newval) {
736  if(permit)
737  getValue(pvmeta.chan, pvmeta.value.get());
738  else
739  ret = pvd::Status::error("Put not permitted");
740  }
741  if(newval || proc==PVIF::ProcForce) {
742  if(permit)
743  ret = PVIF::get(mask, proc);
744  else
745  ret = pvd::Status::error("Process not permitted");
746  }
747  return ret;
748  }
749 
750  virtual unsigned dbe(const epics::pvData::BitSet& mask) OVERRIDE FINAL
751  {
752  unsigned ret = 0;
753  if(mask.logical_and(pvmeta.maskVALUE))
754  ret |= DBE_VALUE;
755  if(mask.logical_and(pvmeta.maskALARM))
756  ret |= DBE_ALARM;
757  if(mask.logical_and(pvmeta.maskPROPERTY))
758  ret |= DBE_PROPERTY;
759  return ret;
760  }
761 };
762 
763 } // namespace
764 
765 static
766 pvd::ScalarType DBR2PVD(short dbr)
767 {
768  switch(dbr) {
769 #define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case DBR_##DBFTYPE: return pvd::pv##PVACODE;
770 #define CASE_ENUM
771 #define CASE_SKIP_BOOL
772 #include "pv/typemap.h"
773 #undef CASE_ENUM
774 #undef CASE_SKIP_BOOL
775 #undef CASE
776  case DBF_STRING: return pvd::pvString;
777  }
778  throw std::invalid_argument("Unsupported DBR code");
779 }
780 
781 short PVD2DBR(pvd::ScalarType pvt)
782 {
783  switch(pvt) {
784 #define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case pvd::pv##PVACODE: return DBR_##DBFTYPE;
785 #ifndef USE_INT64
786 # define CASE_SQUEEZE_INT64
787 #endif
788 #include "pv/typemap.h"
789 #ifndef USE_INT64
790 # undef CASE_SQUEEZE_INT64
791 #endif
792 #undef CASE
793  case pvd::pvString: return DBF_STRING;
794  }
795  return -1;
796 }
797 
798 static
799 pvd::StructureConstPtr buildTimeStamp()
800 {
801  return pvd::FieldBuilder::begin()
802  ->add("secondsPastEpoch", pvd::pvLong)
803  ->add("nanoseconds", pvd::pvInt)
804  ->add("userTag", pvd::pvInt)
805  ->createStructure();
806 }
807 
808 epics::pvData::FieldConstPtr
809 ScalarBuilder::dtype()
810 {
811  if(!channel)
812  throw std::runtime_error("+type:\"scalar\" requires +channel:");
813 
814  short dbr = dbChannelFinalFieldType(channel);
815  const long maxelem = dbChannelFinalElements(channel);
816  const pvd::ScalarType pvt = DBR2PVD(dbr);
817 
818  if(INVALID_DB_REQ(dbr))
819  throw std::invalid_argument("DBF code out of range");
820 
821  if(maxelem!=1 && dbr==DBR_ENUM)
822  dbr = DBF_SHORT;
823 
824  pvd::FieldBuilderPtr builder(pvd::getFieldCreate()->createFieldBuilder());
825  pvd::StandardFieldPtr standard(pvd::getStandardField());
826 
827  if(dbr==DBR_ENUM)
828  builder = builder->setId("epics:nt/NTEnum:1.0")
829  ->addNestedStructure("value")
830  ->setId("enum_t")
831  ->add("index", pvd::pvInt)
832  ->addArray("choices", pvd::pvString)
833  ->endNested();
834  else if(maxelem==1)
835  builder = builder->setId("epics:nt/NTScalar:1.0")
836  ->add("value", pvt);
837  else
838  builder = builder->setId("epics:nt/NTScalarArray:1.0")
839  ->addArray("value", pvt);
840 
841  builder = builder->add("alarm", standard->alarm())
842  ->add("timeStamp", buildTimeStamp());
843 
844  if(dbr!=DBR_ENUM) {
845  builder = builder->addNestedStructure("display")
846  ->add("limitLow", pvd::pvDouble)
847  ->add("limitHigh", pvd::pvDouble)
848  ->add("description", pvd::pvString)
849  ->add("units", pvd::pvString)
850  ->add("precision", pvd::pvInt)
851  ->addNestedStructure("form")
852  ->setId("enum_t")
853  ->add("index", pvd::pvInt)
854  ->addArray("choices", pvd::pvString)
855  ->endNested()
856  ->endNested()
857  ->add("control", standard->control());
858 
859  if(dbr!=DBR_STRING)
860  builder = builder->add("valueAlarm", standard->doubleAlarm());
861  }
862 
863  return builder->createStructure();
864 }
865 
866 PVIF*
867 ScalarBuilder::attach(const epics::pvData::PVStructurePtr& root, const FieldName& fldname)
868 {
869  if(!channel)
870  throw std::runtime_error("+type:\"scalar\" requires +channel:");
871  pvd::PVField *enclosing = 0;
872  pvd::PVFieldPtr fld(fldname.lookup(root, &enclosing));
873 
874  const short dbr = dbChannelFinalFieldType(channel);
875  const long maxelem = dbChannelFinalElements(channel);
876 
877  if(maxelem==1) {
878  switch(dbr) {
879  case DBR_CHAR:
880  case DBR_UCHAR:
881  case DBR_SHORT:
882  case DBR_USHORT:
883  case DBR_LONG:
884  case DBR_ULONG:
885 #ifdef USE_INT64
886  case DBR_INT64:
887  case DBR_UINT64:
888 #endif
889  return new PVIFScalarNumeric<pvScalar, metaDOUBLE>(channel, fld, enclosing);
890  case DBR_FLOAT:
891  case DBR_DOUBLE:
892  return new PVIFScalarNumeric<pvScalar, metaDOUBLE>(channel, fld, enclosing);
893  case DBR_ENUM:
894  return new PVIFScalarNumeric<pvScalar, metaENUM>(channel, fld, enclosing);
895  case DBR_STRING:
896  return new PVIFScalarNumeric<pvScalar, metaSTRING>(channel, fld, enclosing);
897  }
898  } else {
899  switch(dbr) {
900  case DBR_CHAR:
901  case DBR_UCHAR:
902  case DBR_SHORT:
903  case DBR_ENUM:
904  case DBR_USHORT:
905  case DBR_LONG:
906  case DBR_ULONG:
907  case DBR_STRING:
908  case DBR_FLOAT:
909 #ifdef USE_INT64
910  case DBR_INT64:
911  case DBR_UINT64:
912 #endif
913  case DBR_DOUBLE:
914  return new PVIFScalarNumeric<pvArray, metaDOUBLE>(channel, fld, enclosing);
915  }
916  }
917 
918  throw std::invalid_argument("Channel has invalid/unsupported DBR type");
919 }
920 
921 namespace {
922 template<class PVD>
923 struct PVIFPlain : public PVIF
924 {
925  const typename PVD::shared_pointer field;
926  size_t fieldOffset;
927  dbChannel * const channel;
928 
929  PVIFPlain(dbChannel *channel, const epics::pvData::PVFieldPtr& fld, epics::pvData::PVField* enclosing=0)
930  :PVIF(channel)
931  ,field(std::tr1::static_pointer_cast<PVD>(fld))
932  ,channel(channel)
933  {
934  if(!field)
935  throw std::logic_error("PVIFPlain attached type mis-match");
936  if(enclosing)
937  fieldOffset = enclosing->getFieldOffset();
938  else
939  fieldOffset = field->getFieldOffset();
940  }
941 
942  virtual ~PVIFPlain() {}
943 
944  virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
945  {
946  if(dbe&DBE_VALUE) {
947  putValue(channel, field.get(), pfl);
948  mask.set(fieldOffset);
949  }
950  }
951 
952  virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc, bool permit) OVERRIDE FINAL
953  {
954  pvd::Status ret = checkDISP(chan);
955  if(!ret)
956  return ret;
957 
958  bool newval = mask.get(fieldOffset);
959  if(newval) {
960  if(permit)
961  getValue(channel, field.get());
962  else
963  ret = pvd::Status::error("Put not permitted");
964  }
965  if(newval || proc==PVIF::ProcForce) {
966  if(permit)
967  ret = PVIF::get(mask, proc);
968  else
969  ret = pvd::Status::error("Process not permitted");
970  }
971  return ret;
972  }
973 
974  virtual unsigned dbe(const epics::pvData::BitSet& mask) OVERRIDE FINAL
975  {
976  // TODO: figure out how to handle various intermidiate compressed
977  // bitSet and enclosing.
978  // Until then check only also for wildcard bit (0).
979  if(mask.get(fieldOffset) || mask.get(0))
980  return DBE_VALUE;
981  return 0;
982  }
983 };
984 
985 struct PlainBuilder : public PVIFBuilder
986 {
987  explicit PlainBuilder(dbChannel* chan) :PVIFBuilder(chan) {}
988  virtual ~PlainBuilder() {}
989 
990  // fetch the structure description
991  virtual epics::pvData::FieldConstPtr dtype() OVERRIDE FINAL {
992  if(!channel)
993  throw std::runtime_error("+type:\"plain\" requires +channel:");
994 
995  const short dbr = dbChannelFinalFieldType(channel);
996  const long maxelem = dbChannelFinalElements(channel);
997  const pvd::ScalarType pvt = DBR2PVD(dbr);
998 
999  if(INVALID_DB_REQ(dbr))
1000  throw std::invalid_argument("DBF code out of range");
1001 
1002  if(maxelem==1)
1003  return pvd::getFieldCreate()->createScalar(pvt);
1004  else
1005  return pvd::getFieldCreate()->createScalarArray(pvt);
1006  }
1007 
1008  // Attach to a structure instance.
1009  // must be of the type returned by dtype().
1010  // need not be the root structure
1011  virtual PVIF* attach(const epics::pvData::PVStructurePtr& root,
1012  const FieldName& fldname) OVERRIDE FINAL
1013  {
1014  if(!channel)
1015  throw std::runtime_error("+type:\"plain\" requires +channel:");
1016  const long maxelem = dbChannelFinalElements(channel);
1017 
1018  pvd::PVField *enclosing = 0;
1019  pvd::PVFieldPtr fld(fldname.lookup(root, &enclosing));
1020 
1021  if(maxelem==1)
1022  return new PVIFPlain<pvd::PVScalar>(channel, fld, enclosing);
1023  else
1024  return new PVIFPlain<pvd::PVScalarArray>(channel, fld, enclosing);
1025  }
1026 };
1027 
1028 struct AnyScalarBuilder : public PVIFBuilder
1029 {
1030  explicit AnyScalarBuilder(dbChannel* chan) :PVIFBuilder(chan) {}
1031  virtual ~AnyScalarBuilder() {}
1032 
1033  // fetch the structure description
1034  virtual epics::pvData::FieldConstPtr dtype() OVERRIDE FINAL {
1035  (void)channel; //ignored
1036  return pvd::getFieldCreate()->createVariantUnion();
1037  }
1038 
1039  // Attach to a structure instance.
1040  // must be of the type returned by dtype().
1041  // need not be the root structure
1042  virtual PVIF* attach(const epics::pvData::PVStructurePtr& root,
1043  const FieldName& fldname) OVERRIDE FINAL
1044  {
1045  if(!channel)
1046  throw std::runtime_error("+type:\"any\" requires +channel:");
1047  pvd::PVDataCreatePtr create(pvd::getPVDataCreate());
1048  const short dbr = dbChannelFinalFieldType(channel);
1049  const long maxelem = dbChannelFinalElements(channel);
1050  const pvd::ScalarType pvt = DBR2PVD(dbr);
1051 
1052  pvd::PVField *enclosing = 0;
1053  pvd::PVFieldPtr fld(fldname.lookup(root, &enclosing));
1054 
1055  pvd::PVUnion *value = dynamic_cast<pvd::PVUnion*>(fld.get());
1056  if(!value)
1057  throw std::logic_error("Mis-matched attachment point");
1058 
1059  pvd::PVFieldPtr arr(value->get());
1060  if(!arr) {
1061  if(maxelem==1)
1062  arr = create->createPVScalar(pvt);
1063  else
1064  arr = create->createPVScalarArray(pvt);
1065  value->set(arr);
1066  }
1067 
1068  if(maxelem==1)
1069  return new PVIFPlain<pvd::PVScalar>(channel, arr, enclosing ? enclosing : arr.get());
1070  else
1071  return new PVIFPlain<pvd::PVScalarArray>(channel, arr, enclosing ? enclosing : arr.get());
1072  }
1073 
1074 };
1075 
1076 struct PVIFMeta : public PVIF
1077 {
1078  pvTimeAlarm meta;
1079 
1080  PVIFMeta(dbChannel *channel, const epics::pvData::PVFieldPtr& fld, epics::pvData::PVField* enclosing=0)
1081  :PVIF(channel)
1082  {
1083  pvd::PVStructurePtr field(std::tr1::dynamic_pointer_cast<pvd::PVStructure>(fld));
1084  if(!field)
1085  throw std::logic_error("PVIFMeta attached type mis-match");
1086  meta.chan = channel;
1087  pdbRecordIterator info(chan);
1088  attachTime(meta, field);
1089  findNSMask(meta, info, field);
1090  findFormat(meta, info, field);
1091  if(enclosing) {
1092  meta.maskALWAYS.clear();
1093  meta.maskALWAYS.set(enclosing->getFieldOffset());
1094  meta.maskALARM.clear();
1095  meta.maskALARM.set(enclosing->getFieldOffset());
1096  }
1097  }
1098 
1099  virtual ~PVIFMeta() {}
1100 
1101  virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
1102  {
1103  mask |= meta.maskALWAYS;
1104  if(dbe&DBE_ALARM)
1105  mask |= meta.maskALARM;
1106 
1107  putTime(meta, dbe, pfl);
1108  }
1109 
1110  virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc, bool permit) OVERRIDE FINAL
1111  {
1112  // can't put time/alarm
1113  if(mask.logical_and(meta.maskALARM))
1114  return pvd::Status::warn("Put to meta field ignored");
1115  return pvd::Status::Ok;
1116  }
1117 
1118  virtual unsigned dbe(const epics::pvData::BitSet& mask) OVERRIDE FINAL
1119  {
1120  if(mask.logical_and(meta.maskALARM))
1121  return DBE_ALARM;
1122  return 0;
1123  }
1124 };
1125 
1126 struct MetaBuilder : public PVIFBuilder
1127 {
1128  explicit MetaBuilder(dbChannel* chan) :PVIFBuilder(chan) {}
1129  virtual ~MetaBuilder() {}
1130 
1131  // fetch the structure description
1132  virtual epics::pvData::FieldConstPtr dtype() OVERRIDE FINAL {
1133  throw std::logic_error("Don't call me");
1134  }
1135 
1136  virtual epics::pvData::FieldBuilderPtr dtype(epics::pvData::FieldBuilderPtr& builder,
1137  const std::string& fld) OVERRIDE FINAL
1138  {
1139  pvd::StandardFieldPtr std(pvd::getStandardField());
1140  if(fld.empty()) {
1141  return builder->add("alarm", std->alarm())
1142  ->add("timeStamp", buildTimeStamp());
1143  } else {
1144  return builder->addNestedStructure(fld)
1145  ->add("alarm", std->alarm())
1146  ->add("timeStamp", buildTimeStamp())
1147  ->endNested();
1148  }
1149  }
1150 
1151  // Attach to a structure instance.
1152  // must be of the type returned by dtype().
1153  // need not be the root structure
1154  virtual PVIF* attach(const epics::pvData::PVStructurePtr& root,
1155  const FieldName& fldname) OVERRIDE FINAL
1156  {
1157  if(!channel)
1158  throw std::runtime_error("+type:\"meta\" requires +channel:");
1159 
1160  pvd::PVField *enclosing = 0;
1161  pvd::PVFieldPtr fld(fldname.lookup(root, &enclosing));
1162 
1163  return new PVIFMeta(channel, fld, enclosing);
1164  }
1165 
1166 };
1167 
1168 struct PVIFProc : public PVIF
1169 {
1170  PVIFProc(dbChannel *channel) :PVIF(channel) {}
1171 
1172  virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
1173  {
1174  // nothing to get
1175  }
1176 
1177  virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc, bool permit) OVERRIDE FINAL
1178  {
1179  // always process (if permitted)
1180  return PVIF::get(mask, PVIF::ProcForce, permit);
1181  }
1182 
1183  virtual unsigned dbe(const epics::pvData::BitSet& mask) OVERRIDE FINAL
1184  {
1185  return 0;
1186  }
1187 };
1188 
1189 struct ProcBuilder : public PVIFBuilder
1190 {
1191  explicit ProcBuilder(dbChannel* chan) :PVIFBuilder(chan) {}
1192  virtual ~ProcBuilder() {}
1193 
1194  // fetch the structure description
1195  virtual epics::pvData::FieldConstPtr dtype() OVERRIDE FINAL {
1196  throw std::logic_error("Don't call me");
1197  }
1198 
1199  virtual epics::pvData::FieldBuilderPtr dtype(epics::pvData::FieldBuilderPtr& builder,
1200  const std::string& fld) OVERRIDE FINAL
1201  {
1202  // invisible
1203  return builder;
1204  }
1205  virtual PVIF* attach(const epics::pvData::PVStructurePtr& root,
1206  const FieldName& fldname) OVERRIDE FINAL
1207  {
1208  if(!channel)
1209  throw std::runtime_error("+type:\"proc\" requires +channel:");
1210 
1211  return new PVIFProc(channel);
1212  }
1213 };
1214 
1215 struct PVIFNoOp : public PVIF
1216 {
1217  PVIFNoOp(dbChannel *channel) :PVIF(channel) {}
1218 
1219  virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
1220  {}
1221 
1222  virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc, bool permit) OVERRIDE FINAL
1223  {
1224  return pvd::Status();
1225  }
1226 
1227  virtual unsigned dbe(const epics::pvData::BitSet& mask) OVERRIDE FINAL
1228  {
1229  return 0;
1230  }
1231 };
1232 
1233 struct IDBuilder : public PVIFBuilder
1234 {
1235  explicit IDBuilder(dbChannel* chan) :PVIFBuilder(chan) {}
1236  virtual ~IDBuilder() {}
1237 
1238  // fetch the structure description
1239  virtual epics::pvData::FieldConstPtr dtype() OVERRIDE FINAL {
1240  throw std::logic_error("Don't call me");
1241  }
1242 
1243  virtual epics::pvData::FieldBuilderPtr dtype(epics::pvData::FieldBuilderPtr& builder,
1244  const std::string& fld) OVERRIDE FINAL
1245  {
1246  // caller has already done builder->setId(...)
1247  return builder;
1248  }
1249  virtual PVIF* attach(const epics::pvData::PVStructurePtr& root,
1250  const FieldName& fldname) OVERRIDE FINAL
1251  {
1252  return new PVIFNoOp(channel);
1253  }
1254 };
1255 
1256 }//namespace
1257 
1258 pvd::Status PVIF::get(const epics::pvData::BitSet& mask, proc_t proc, bool permit)
1259 {
1260  dbCommon *precord = dbChannelRecord(chan);
1261 
1262  bool tryproc = proc!=ProcPassive ? proc==ProcForce :
1263  dbChannelField(chan) == &precord->proc ||
1264  (dbChannelFldDes(chan)->process_passive &&
1265  precord->scan == 0);
1266 
1267  pvd::Status ret;
1268 
1269  if (tryproc) {
1270  if (!permit) {
1271  return pvd::Status::error("Process not permitted");
1272 
1273  } else if (precord->pact) {
1274  if (precord->tpro)
1275  printf("%s: Active %s\n",
1276  epicsThreadGetNameSelf(), precord->name);
1277  precord->rpro = TRUE;
1278  } else {
1279  /* indicate that dbPutField called dbProcess */
1280  precord->putf = TRUE;
1281  long err = dbProcess(precord);
1282  if(err) {
1283  char buf[32];
1284  errSymLookup(err, buf, sizeof(buf));
1285  std::ostringstream msg;
1286  msg<<"process error : "<<buf;
1287  ret = pvd::Status::error(msg.str());
1288  }
1289  }
1290  }
1291 
1292  return ret;
1293 }
1294 
1295 epics::pvData::FieldBuilderPtr
1296 PVIFBuilder::dtype(epics::pvData::FieldBuilderPtr& builder,
1297  const std::string &fld)
1298 {
1299  if(fld.empty())
1300  throw std::runtime_error(SB()<<"Can't attach +type "<<typeid(*this).name()<<" to root");
1301 
1302  epics::pvData::FieldConstPtr ftype(this->dtype());
1303  if(ftype)
1304  builder = builder->add(fld, ftype);
1305 
1306  return builder;
1307 }
1308 
1309 PVIFBuilder* PVIFBuilder::create(const std::string& type, dbChannel* chan)
1310 {
1311  if(type.empty() || type=="scalar")
1312  return new ScalarBuilder(chan);
1313  else if(type=="plain")
1314  return new PlainBuilder(chan);
1315  else if(type=="any")
1316  return new AnyScalarBuilder(chan);
1317  else if(type=="meta")
1318  return new MetaBuilder(chan);
1319  else if(type=="proc")
1320  return new ProcBuilder(chan);
1321  else if(type=="structure")
1322  return new IDBuilder(chan);
1323  else
1324  throw std::runtime_error(std::string("Unknown +type=")+type);
1325 }
Definition: pvif.h:81
Definition: pvif.h:100
Definition: sb.h:8
Definition: pvif.h:365
Definition: pvif.h:61
virtual epics::pvData::Status get(const epics::pvData::BitSet &mask, proc_t proc=ProcInhibit, bool permit=true)=0
Definition: pvif.cpp:1258