3 #include <pv/pvIntrospect.h> 4 #include <pv/standardField.h> 8 #include <dbStaticLib.h> 12 #include <errSymTbl.h> 13 #include <epicsVersion.h> 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> 28 #include <epicsExport.h> 30 #ifdef EPICS_VERSION_INT 31 # if EPICS_VERSION_INT>=VERSION_INT(3,16,1,0) 34 # define CASE_REAL_INT64 38 namespace pvd = epics::pvData;
41 DBCH::DBCH(dbChannel *ch) :chan(ch)
46 DBCH::DBCH(
const std::string& name)
47 :chan(dbChannelCreate(name.c_str()))
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));
64 if(chan) dbChannelDelete(chan);
67 void DBCH::swap(
DBCH& o)
69 std::swap(chan, o.chan);
72 void ASCred::update(
const pva::ChannelRequester::shared_pointer& req)
74 pva::PeerInfo::const_shared_pointer info(req->getPeerInfo());
75 std::string usertemp, hosttemp;
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) {
84 usertemp = usertemp.substr(sep+1);
88 usertemp = info->authority +
"/" + info->account;
91 const char role[] =
"role/";
93 groups.resize(info->roles.size());
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));
100 std::copy(it->begin(),
102 groups[idx].begin()+
sizeof(role)-1);
103 groups[idx][groups[idx].size()-1] =
'\0';
108 hosttemp = req->getRequesterName();
112 size_t sep = hosttemp.find_first_of(
':');
113 if(sep == std::string::npos) {
114 sep = hosttemp.size();
116 hosttemp.resize(sep);
118 host.resize(hosttemp.size()+1);
119 std::copy(hosttemp.begin(),
122 host[hosttemp.size()] =
'\0';
124 user.resize(usertemp.size()+1);
125 std::copy(usertemp.begin(),
128 user[usertemp.size()] =
'\0';
131 ASCLIENT::~ASCLIENT()
133 asRemoveClient(&aspvt);
134 for(
size_t i=0, N=grppvt.size(); i<N; i++) {
135 asRemoveClient(&grppvt[i]);
139 void ASCLIENT::add(dbChannel* chan,
ASCred& cred)
141 asRemoveClient(&aspvt);
143 (void)asAddClient(&aspvt, dbChannelRecord(chan)->asp, dbChannelFldDes(chan)->as_level, &cred.user[0], &cred.host[0]);
145 grppvt.resize(cred.groups.size(), 0);
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]);
153 bool ASCLIENT::canWrite() {
154 if(!asActive || (aspvt && asCheckPut(aspvt)))
156 for(
size_t i=0, N=grppvt.size(); i<N; i++) {
157 if(grppvt[i] && asCheckPut(grppvt[i]))
163 PVIF::PVIF(dbChannel *ch)
172 pvd::uint32 nsecMask;
174 pvd::BitSet maskALWAYS, maskALARM;
177 pvd::PVIntPtr status, severity, nsec, userTag;
178 pvd::PVStringPtr message;
180 pvTimeAlarm() :chan(NULL), nsecMask(0) {}
183 struct pvCommon :
public pvTimeAlarm {
185 pvd::BitSet maskVALUE, maskPROPERTY, maskVALUEPut;
187 pvd::PVDoublePtr displayLow, displayHigh, controlLow, controlHigh;
188 pvd::PVStringPtr egu, desc;
189 pvd::PVIntPtr fmt, prec;
191 pvd::PVScalarPtr warnLow, warnHigh, alarmLow, alarmHigh;
193 pvd::PVStringArrayPtr enumopts;
196 struct pvScalar :
public pvCommon {
197 typedef pvd::PVScalar pvd_type;
198 pvd::PVScalarPtr value;
201 struct pvArray :
public pvCommon {
202 typedef pvd::PVScalarArray pvd_type;
203 pvd::PVScalarArrayPtr value;
212 enum {mask = DBR_STATUS | DBR_AMSG | DBR_TIME | DBR_UTAG};
229 enum {mask = DBR_STATUS | DBR_AMSG | DBR_UNITS | DBR_PRECISION | DBR_TIME | DBR_UTAG | DBR_GR_DOUBLE | DBR_CTRL_DOUBLE | DBR_AL_DOUBLE};
246 enum {mask = DBR_STATUS | DBR_AMSG | DBR_TIME | DBR_UTAG | DBR_ENUM_STRS};
263 enum {mask = DBR_STATUS | DBR_AMSG | DBR_TIME | DBR_UTAG};
266 void attachTime(pvTimeAlarm& pvm,
const pvd::PVStructurePtr& pv)
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);
276 FMAP(userTag, PVInt,
"timeStamp.userTag", ALWAYS);
282 pvd::shared_vector<const std::string> buildFormats()
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);
296 pvd::shared_vector<const std::string> displayForms(buildFormats());
299 void attachMeta(pvCommon& pvm,
const pvd::PVStructurePtr& pv)
302 pvd::PVStructurePtr fmt(pv->getSubField<pvd::PVStructure>(
"display.form"));
304 fmt->getSubFieldT<pvd::PVStringArray>(
"choices")->replace(displayForms);
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);
326 template<
typename PVX>
327 void attachAll(PVX& pvm,
const pvd::PVStructurePtr& pv)
329 pvm.value = pv->getSubField<
typename PVX::pvd_type>(
"value.index");
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()) {
336 pvm.maskVALUEPut.set(fld->getFieldOffset());
338 pvm.maskVALUEPut.set(0);
342 template<
typename Meta>
343 void mapStatus(
const Meta& meta, pvd::PVInt* status, pvd::PVString* message)
346 if(meta.amsg[0]!=
'\0') {
347 message->put(meta.amsg);
350 if(meta.status<ALARM_NSTATUS)
351 message->put(epicsAlarmConditionStrings[meta.status]);
357 switch(meta.status) {
386 case READ_ACCESS_ALARM:
387 case WRITE_ACCESS_ALARM:
398 template<
typename META>
399 void putMetaImpl(
const pvTimeAlarm& pv,
const META& meta)
401 pvd::int32 nsec = meta.time.nsec;
403 pv.userTag->put(nsec&pv.nsecMask);
404 nsec &= ~pv.nsecMask;
407 pv.userTag->put(meta.utag);
410 pv.nsec->put(nsec); pv.sec->put(meta.time.secPastEpoch+POSIX_TIME_AT_EPICS_EPOCH);
413 void putTime(
const pvTimeAlarm& pv,
unsigned dbe, db_field_log *pfl)
416 long options = (int)metaTIME::mask, nReq = 0;
418 long status = dbChannelGet(pv.chan, dbChannelFinalFieldType(pv.chan), &meta, &options, &nReq, pfl);
420 throw std::runtime_error(
"dbGet for meta fails");
422 putMetaImpl(pv, meta);
424 mapStatus(meta, pv.status.get(), pv.message.get());
425 pv.severity->put(meta.severity);
429 void putValue(dbChannel *chan, pvd::PVScalar* value, db_field_log *pfl)
434 long status = dbChannelGet(chan, dbChannelFinalFieldType(chan), &buf, NULL, &nReq, pfl);
436 throw std::runtime_error(
"dbGet for meta fails");
440 memset(&buf, 0,
sizeof(buf));
443 switch(dbChannelFinalFieldType(chan)) {
444 #define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case DBR_##DBFTYPE: value->putFrom<PVATYPE>(buf.dbf_##DBFTYPE); break; 446 #define CASE_SKIP_BOOL 447 #include "pv/typemap.h" 449 #undef CASE_SKIP_BOOL 452 buf.dbf_STRING[
sizeof(buf.dbf_STRING)-1] =
'\0';
453 value->putFrom<std::string>(buf.dbf_STRING);
456 throw std::runtime_error(
"putValue unsupported DBR code");
460 void getValue(dbChannel *chan, pvd::PVScalar* value)
464 switch(dbChannelFinalFieldType(chan)) {
465 #define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case DBR_##DBFTYPE: buf.dbf_##DBFTYPE = value->getAs<PVATYPE>(); break; 467 #define CASE_SKIP_BOOL 468 #include "pv/typemap.h" 470 #undef CASE_SKIP_BOOL 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';
480 throw std::runtime_error(
"getValue unsupported DBR code");
483 long status = dbChannelPut(chan, dbChannelFinalFieldType(chan), &buf, 1);
485 throw std::runtime_error(
"dbPut for meta fails");
488 void getValue(dbChannel *chan, pvd::PVScalarArray* value)
490 short dbr = dbChannelFinalFieldType(chan);
492 if(dbr!=DBR_STRING) {
493 pvd::shared_vector<const void> buf;
496 long nReq = buf.size()/pvd::ScalarTypeFunc::elementSize(value->getScalarArray()->getElementType());
498 long status = dbChannelPut(chan, dbr, buf.data(), nReq);
500 throw std::runtime_error(
"dbChannelPut fails");
503 pvd::shared_vector<const std::string> buf;
507 std::vector<char> temp(buf.size()*MAX_STRING_SIZE);
509 for(
size_t i=0, N=buf.size(); i<N; i++)
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';
515 long status = dbChannelPut(chan, dbr, &temp[0], buf.size());
517 throw std::runtime_error(
"dbChannelPut fails");
521 void putValue(dbChannel *chan, pvd::PVScalarArray* value, db_field_log *pfl)
523 const short dbr = dbChannelFinalFieldType(chan);
525 long nReq = dbChannelFinalElements(chan);
526 const pvd::ScalarType etype = value->getScalarArray()->getElementType();
528 if(dbr!=DBR_STRING) {
530 pvd::shared_vector<void> buf(pvd::ScalarTypeFunc::allocArray(etype, nReq));
532 long status = dbChannelGet(chan, dbr, buf.data(), NULL, &nReq, pfl);
534 throw std::runtime_error(
"dbChannelGet for value fails");
536 buf.slice(0, nReq*pvd::ScalarTypeFunc::elementSize(etype));
538 value->putFrom(pvd::freeze(buf));
541 std::vector<char> temp(nReq*MAX_STRING_SIZE);
543 long status = dbChannelGet(chan, dbr, &temp[0], NULL, &nReq, pfl);
545 throw std::runtime_error(
"dbChannelGet for value fails");
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]);
553 value->putFrom(pvd::freeze(buf));
556 template<
typename META>
557 void putMeta(
const pvCommon& pv,
unsigned dbe, db_field_log *pfl)
560 long options = (int)META::mask, nReq = 0;
561 dbCommon *prec = dbChannelRecord(pv.chan);
563 long status = dbChannelGet(pv.chan, dbChannelFinalFieldType(pv.chan), &meta, &options, &nReq, pfl);
565 throw std::runtime_error(
"dbGet for meta fails");
567 putMetaImpl(pv, meta);
568 #define FMAP(MNAME, FNAME) pv.MNAME->put(meta.FNAME) 570 mapStatus(meta, pv.status.get(), pv.message.get());
571 FMAP(severity, severity);
573 if(dbe&DBE_PROPERTY) {
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);
583 if(META::mask&DBR_PRECISION && pv.prec) {
584 pv.prec->put(pvd::int32(meta.precision.dp));
586 #define FMAP(MASK, MNAME, FNAME) if(META::mask&(MASK) && pv.MNAME) pv.MNAME->putFrom(meta.FNAME) 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);
595 pvd::shared_vector<std::string> strs(meta.no_str);
596 for(
size_t i=0; i<strs.size(); i++)
598 meta.strs[i][
sizeof(meta.strs[i])-1] =
'\0';
599 strs[i] = meta.strs[i];
601 pv.enumopts->replace(pvd::freeze(strs));
606 template<
typename PVC,
typename META>
607 void putAll(
const PVC &pv,
unsigned dbe, db_field_log *pfl)
609 if(dbe&(DBE_VALUE|DBE_ARCHIVE)) {
610 putValue(pv.chan, pv.value.get(), pfl);
612 if(!(dbe&DBE_PROPERTY)) {
613 putTime(pv, dbe, pfl);
615 putMeta<META>(pv, dbe, pfl);
619 void findNSMask(pvTimeAlarm& pvmeta,
pdbRecordIterator& info,
const epics::pvData::PVStructurePtr& pvalue)
621 const char *UT = info.info(
"Q:time:tag");
622 if(UT && strncmp(UT,
"nsec:lsb:", 9)==0) {
624 pvmeta.nsecMask = epics::pvData::castUnsafe<pvd::uint32>(std::string(&UT[9]));
625 }
catch(std::exception& e){
627 std::cerr<<info.name()<<
" : Q:time:tag nsec:lsb: requires a number not '"<<UT[9]<<
"'\n";
630 if(pvmeta.nsecMask>0 && pvmeta.nsecMask<=32) {
631 pvmeta.userTag = pvalue->getSubField<pvd::PVInt>(
"timeStamp.userTag");
632 if(!pvmeta.userTag) {
635 pvd::uint64 mask = (1<<pvmeta.nsecMask)-1;
636 pvmeta.nsecMask = mask;
637 pvmeta.maskALWAYS.set(pvmeta.userTag->getFieldOffset());
643 void findFormat(pvTimeAlarm& pvmeta,
pdbRecordIterator& info,
const epics::pvData::PVStructurePtr& pvalue)
645 const char *FMT = info.info(
"Q:form");
647 pvd::PVScalarPtr fmt(pvalue->getSubField<pvd::PVScalar>(
"display.form.index"));
650 for(
size_t i=0; !found && i<displayForms.size(); i++) {
651 if((found=(displayForms[i]==FMT)))
652 fmt->putFrom<pvd::uint32>(i);
656 fmt->putFrom(std::string(FMT));
657 }
catch(std::exception& e){
658 errlogPrintf(
"%s: info(Q:form, \"%s\") is not known format: %s\n", info.name(), FMT, e.what());
665 pvd::Status checkDISP(dbChannel *chan)
667 dbCommon *prec = dbChannelRecord(chan);
669 if(prec->disp && dbChannelField(chan)!=&prec->disp)
670 ret = pvd::Status::error(
"Put Disabled");
674 template<
typename PVX,
typename META>
675 struct PVIFScalarNumeric :
public PVIF 678 const epics::pvData::PVStructurePtr pvalue;
680 PVIFScalarNumeric(dbChannel *ch,
const epics::pvData::PVFieldPtr& p, pvd::PVField *enclosing)
682 ,pvalue(std::tr1::dynamic_pointer_cast<pvd::PVStructure>(p))
685 throw std::runtime_error(
"Must attach to structure");
688 attachAll<PVX>(pvmeta, pvalue);
690 size_t bit = enclosing->getFieldOffset();
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);
705 findNSMask(pvmeta, info, pvalue);
706 findFormat(pvmeta, info, pvalue);
708 virtual ~PVIFScalarNumeric() {}
710 virtual void put(epics::pvData::BitSet& mask,
unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
713 putAll<PVX, META>(pvmeta, dbe, pfl);
714 mask |= pvmeta.maskALWAYS;
715 if(dbe&(DBE_VALUE|DBE_ARCHIVE))
716 mask |= pvmeta.maskVALUE;
718 mask |= pvmeta.maskALARM;
720 mask |= pvmeta.maskPROPERTY;
722 pvmeta.severity->put(3);
723 mask |= pvmeta.maskALARM;
728 virtual pvd::Status
get(
const epics::pvData::BitSet& mask, proc_t proc,
bool permit) OVERRIDE FINAL
730 pvd::Status ret = checkDISP(chan);
734 bool newval = mask.logical_and(pvmeta.maskVALUEPut);
737 getValue(pvmeta.chan, pvmeta.value.get());
739 ret = pvd::Status::error(
"Put not permitted");
741 if(newval || proc==PVIF::ProcForce) {
745 ret = pvd::Status::error(
"Process not permitted");
750 virtual unsigned dbe(
const epics::pvData::BitSet& mask) OVERRIDE FINAL
753 if(mask.logical_and(pvmeta.maskVALUE))
755 if(mask.logical_and(pvmeta.maskALARM))
757 if(mask.logical_and(pvmeta.maskPROPERTY))
766 pvd::ScalarType DBR2PVD(
short dbr)
769 #define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case DBR_##DBFTYPE: return pvd::pv##PVACODE; 771 #define CASE_SKIP_BOOL 772 #include "pv/typemap.h" 774 #undef CASE_SKIP_BOOL 776 case DBF_STRING:
return pvd::pvString;
778 throw std::invalid_argument(
"Unsupported DBR code");
781 short PVD2DBR(pvd::ScalarType pvt)
784 #define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case pvd::pv##PVACODE: return DBR_##DBFTYPE; 786 # define CASE_SQUEEZE_INT64 788 #include "pv/typemap.h" 790 # undef CASE_SQUEEZE_INT64 793 case pvd::pvString:
return DBF_STRING;
799 pvd::StructureConstPtr buildTimeStamp()
801 return pvd::FieldBuilder::begin()
802 ->add(
"secondsPastEpoch", pvd::pvLong)
803 ->add(
"nanoseconds", pvd::pvInt)
804 ->add(
"userTag", pvd::pvInt)
808 epics::pvData::FieldConstPtr
809 ScalarBuilder::dtype()
812 throw std::runtime_error(
"+type:\"scalar\" requires +channel:");
814 short dbr = dbChannelFinalFieldType(channel);
815 const long maxelem = dbChannelFinalElements(channel);
816 const pvd::ScalarType pvt = DBR2PVD(dbr);
818 if(INVALID_DB_REQ(dbr))
819 throw std::invalid_argument(
"DBF code out of range");
821 if(maxelem!=1 && dbr==DBR_ENUM)
824 pvd::FieldBuilderPtr builder(pvd::getFieldCreate()->createFieldBuilder());
825 pvd::StandardFieldPtr standard(pvd::getStandardField());
828 builder = builder->setId(
"epics:nt/NTEnum:1.0")
829 ->addNestedStructure(
"value")
831 ->add(
"index", pvd::pvInt)
832 ->addArray(
"choices", pvd::pvString)
835 builder = builder->setId(
"epics:nt/NTScalar:1.0")
838 builder = builder->setId(
"epics:nt/NTScalarArray:1.0")
839 ->addArray(
"value", pvt);
841 builder = builder->add(
"alarm", standard->alarm())
842 ->add(
"timeStamp", buildTimeStamp());
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")
853 ->add(
"index", pvd::pvInt)
854 ->addArray(
"choices", pvd::pvString)
857 ->add(
"control", standard->control());
860 builder = builder->add(
"valueAlarm", standard->doubleAlarm());
863 return builder->createStructure();
867 ScalarBuilder::attach(
const epics::pvData::PVStructurePtr& root,
const FieldName& fldname)
870 throw std::runtime_error(
"+type:\"scalar\" requires +channel:");
871 pvd::PVField *enclosing = 0;
872 pvd::PVFieldPtr fld(fldname.lookup(root, &enclosing));
874 const short dbr = dbChannelFinalFieldType(channel);
875 const long maxelem = dbChannelFinalElements(channel);
889 return new PVIFScalarNumeric<pvScalar, metaDOUBLE>(channel, fld, enclosing);
892 return new PVIFScalarNumeric<pvScalar, metaDOUBLE>(channel, fld, enclosing);
894 return new PVIFScalarNumeric<pvScalar, metaENUM>(channel, fld, enclosing);
896 return new PVIFScalarNumeric<pvScalar, metaSTRING>(channel, fld, enclosing);
914 return new PVIFScalarNumeric<pvArray, metaDOUBLE>(channel, fld, enclosing);
918 throw std::invalid_argument(
"Channel has invalid/unsupported DBR type");
923 struct PVIFPlain :
public PVIF 925 const typename PVD::shared_pointer field;
927 dbChannel *
const channel;
929 PVIFPlain(dbChannel *channel,
const epics::pvData::PVFieldPtr& fld, epics::pvData::PVField* enclosing=0)
931 ,field(std::tr1::static_pointer_cast<PVD>(fld))
935 throw std::logic_error(
"PVIFPlain attached type mis-match");
937 fieldOffset = enclosing->getFieldOffset();
939 fieldOffset = field->getFieldOffset();
942 virtual ~PVIFPlain() {}
944 virtual void put(epics::pvData::BitSet& mask,
unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
947 putValue(channel, field.get(), pfl);
948 mask.set(fieldOffset);
952 virtual pvd::Status
get(
const epics::pvData::BitSet& mask, proc_t proc,
bool permit) OVERRIDE FINAL
954 pvd::Status ret = checkDISP(chan);
958 bool newval = mask.get(fieldOffset);
961 getValue(channel, field.get());
963 ret = pvd::Status::error(
"Put not permitted");
965 if(newval || proc==PVIF::ProcForce) {
969 ret = pvd::Status::error(
"Process not permitted");
974 virtual unsigned dbe(
const epics::pvData::BitSet& mask) OVERRIDE FINAL
979 if(mask.get(fieldOffset) || mask.get(0))
987 explicit PlainBuilder(dbChannel* chan) :
PVIFBuilder(chan) {}
988 virtual ~PlainBuilder() {}
991 virtual epics::pvData::FieldConstPtr dtype() OVERRIDE FINAL {
993 throw std::runtime_error(
"+type:\"plain\" requires +channel:");
995 const short dbr = dbChannelFinalFieldType(channel);
996 const long maxelem = dbChannelFinalElements(channel);
997 const pvd::ScalarType pvt = DBR2PVD(dbr);
999 if(INVALID_DB_REQ(dbr))
1000 throw std::invalid_argument(
"DBF code out of range");
1003 return pvd::getFieldCreate()->createScalar(pvt);
1005 return pvd::getFieldCreate()->createScalarArray(pvt);
1011 virtual PVIF* attach(
const epics::pvData::PVStructurePtr& root,
1012 const FieldName& fldname) OVERRIDE FINAL
1015 throw std::runtime_error(
"+type:\"plain\" requires +channel:");
1016 const long maxelem = dbChannelFinalElements(channel);
1018 pvd::PVField *enclosing = 0;
1019 pvd::PVFieldPtr fld(fldname.lookup(root, &enclosing));
1022 return new PVIFPlain<pvd::PVScalar>(channel, fld, enclosing);
1024 return new PVIFPlain<pvd::PVScalarArray>(channel, fld, enclosing);
1030 explicit AnyScalarBuilder(dbChannel* chan) :
PVIFBuilder(chan) {}
1031 virtual ~AnyScalarBuilder() {}
1034 virtual epics::pvData::FieldConstPtr dtype() OVERRIDE FINAL {
1036 return pvd::getFieldCreate()->createVariantUnion();
1042 virtual PVIF* attach(
const epics::pvData::PVStructurePtr& root,
1043 const FieldName& fldname) OVERRIDE FINAL
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);
1052 pvd::PVField *enclosing = 0;
1053 pvd::PVFieldPtr fld(fldname.lookup(root, &enclosing));
1055 pvd::PVUnion *value =
dynamic_cast<pvd::PVUnion*
>(fld.get());
1057 throw std::logic_error(
"Mis-matched attachment point");
1059 pvd::PVFieldPtr arr(value->get());
1062 arr = create->createPVScalar(pvt);
1064 arr = create->createPVScalarArray(pvt);
1069 return new PVIFPlain<pvd::PVScalar>(channel, arr, enclosing ? enclosing : arr.get());
1071 return new PVIFPlain<pvd::PVScalarArray>(channel, arr, enclosing ? enclosing : arr.get());
1076 struct PVIFMeta :
public PVIF 1080 PVIFMeta(dbChannel *channel,
const epics::pvData::PVFieldPtr& fld, epics::pvData::PVField* enclosing=0)
1083 pvd::PVStructurePtr field(std::tr1::dynamic_pointer_cast<pvd::PVStructure>(fld));
1085 throw std::logic_error(
"PVIFMeta attached type mis-match");
1086 meta.chan = channel;
1088 attachTime(meta, field);
1089 findNSMask(meta, info, field);
1090 findFormat(meta, info, field);
1092 meta.maskALWAYS.clear();
1093 meta.maskALWAYS.set(enclosing->getFieldOffset());
1094 meta.maskALARM.clear();
1095 meta.maskALARM.set(enclosing->getFieldOffset());
1099 virtual ~PVIFMeta() {}
1101 virtual void put(epics::pvData::BitSet& mask,
unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
1103 mask |= meta.maskALWAYS;
1105 mask |= meta.maskALARM;
1107 putTime(meta, dbe, pfl);
1110 virtual pvd::Status
get(
const epics::pvData::BitSet& mask, proc_t proc,
bool permit) OVERRIDE FINAL
1113 if(mask.logical_and(meta.maskALARM))
1114 return pvd::Status::warn(
"Put to meta field ignored");
1115 return pvd::Status::Ok;
1118 virtual unsigned dbe(
const epics::pvData::BitSet& mask) OVERRIDE FINAL
1120 if(mask.logical_and(meta.maskALARM))
1128 explicit MetaBuilder(dbChannel* chan) :
PVIFBuilder(chan) {}
1129 virtual ~MetaBuilder() {}
1132 virtual epics::pvData::FieldConstPtr dtype() OVERRIDE FINAL {
1133 throw std::logic_error(
"Don't call me");
1136 virtual epics::pvData::FieldBuilderPtr dtype(epics::pvData::FieldBuilderPtr& builder,
1137 const std::string& fld) OVERRIDE FINAL
1139 pvd::StandardFieldPtr std(pvd::getStandardField());
1141 return builder->add(
"alarm", std->alarm())
1142 ->add(
"timeStamp", buildTimeStamp());
1144 return builder->addNestedStructure(fld)
1145 ->add(
"alarm", std->alarm())
1146 ->add(
"timeStamp", buildTimeStamp())
1154 virtual PVIF* attach(
const epics::pvData::PVStructurePtr& root,
1155 const FieldName& fldname) OVERRIDE FINAL
1158 throw std::runtime_error(
"+type:\"meta\" requires +channel:");
1160 pvd::PVField *enclosing = 0;
1161 pvd::PVFieldPtr fld(fldname.lookup(root, &enclosing));
1163 return new PVIFMeta(channel, fld, enclosing);
1168 struct PVIFProc :
public PVIF 1170 PVIFProc(dbChannel *channel) :
PVIF(channel) {}
1172 virtual void put(epics::pvData::BitSet& mask,
unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
1177 virtual pvd::Status
get(
const epics::pvData::BitSet& mask, proc_t proc,
bool permit) OVERRIDE FINAL
1180 return PVIF::get(mask, PVIF::ProcForce, permit);
1183 virtual unsigned dbe(
const epics::pvData::BitSet& mask) OVERRIDE FINAL
1191 explicit ProcBuilder(dbChannel* chan) :
PVIFBuilder(chan) {}
1192 virtual ~ProcBuilder() {}
1195 virtual epics::pvData::FieldConstPtr dtype() OVERRIDE FINAL {
1196 throw std::logic_error(
"Don't call me");
1199 virtual epics::pvData::FieldBuilderPtr dtype(epics::pvData::FieldBuilderPtr& builder,
1200 const std::string& fld) OVERRIDE FINAL
1205 virtual PVIF* attach(
const epics::pvData::PVStructurePtr& root,
1206 const FieldName& fldname) OVERRIDE FINAL
1209 throw std::runtime_error(
"+type:\"proc\" requires +channel:");
1211 return new PVIFProc(channel);
1215 struct PVIFNoOp :
public PVIF 1217 PVIFNoOp(dbChannel *channel) :
PVIF(channel) {}
1219 virtual void put(epics::pvData::BitSet& mask,
unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
1222 virtual pvd::Status
get(
const epics::pvData::BitSet& mask, proc_t proc,
bool permit) OVERRIDE FINAL
1224 return pvd::Status();
1227 virtual unsigned dbe(
const epics::pvData::BitSet& mask) OVERRIDE FINAL
1235 explicit IDBuilder(dbChannel* chan) :
PVIFBuilder(chan) {}
1236 virtual ~IDBuilder() {}
1239 virtual epics::pvData::FieldConstPtr dtype() OVERRIDE FINAL {
1240 throw std::logic_error(
"Don't call me");
1243 virtual epics::pvData::FieldBuilderPtr dtype(epics::pvData::FieldBuilderPtr& builder,
1244 const std::string& fld) OVERRIDE FINAL
1249 virtual PVIF* attach(
const epics::pvData::PVStructurePtr& root,
1250 const FieldName& fldname) OVERRIDE FINAL
1252 return new PVIFNoOp(channel);
1258 pvd::Status
PVIF::get(
const epics::pvData::BitSet& mask, proc_t proc,
bool permit)
1260 dbCommon *precord = dbChannelRecord(chan);
1262 bool tryproc = proc!=ProcPassive ? proc==ProcForce :
1263 dbChannelField(chan) == &precord->proc ||
1264 (dbChannelFldDes(chan)->process_passive &&
1265 precord->scan == 0);
1271 return pvd::Status::error(
"Process not permitted");
1273 }
else if (precord->pact) {
1275 printf(
"%s: Active %s\n",
1276 epicsThreadGetNameSelf(), precord->name);
1277 precord->rpro = TRUE;
1280 precord->putf = TRUE;
1281 long err = dbProcess(precord);
1284 errSymLookup(err, buf,
sizeof(buf));
1285 std::ostringstream msg;
1286 msg<<
"process error : "<<buf;
1287 ret = pvd::Status::error(msg.str());
1295 epics::pvData::FieldBuilderPtr
1296 PVIFBuilder::dtype(epics::pvData::FieldBuilderPtr& builder,
1297 const std::string &fld)
1300 throw std::runtime_error(
SB()<<
"Can't attach +type "<<
typeid(*this).name()<<
" to root");
1302 epics::pvData::FieldConstPtr ftype(this->dtype());
1304 builder = builder->add(fld, ftype);
1309 PVIFBuilder* PVIFBuilder::create(
const std::string& type, dbChannel* chan)
1311 if(type.empty() || type==
"scalar")
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);
1324 throw std::runtime_error(std::string(
"Unknown +type=")+type);
virtual epics::pvData::Status get(const epics::pvData::BitSet &mask, proc_t proc=ProcInhibit, bool permit=true)=0