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