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;
39 namespace pva = epics::pvAccess;
41 DBCH::DBCH(dbChannel *ch) :chan(ch)
44 throw std::invalid_argument(
"NULL channel");
48 DBCH::DBCH(
const std::string& name)
49 :chan(dbChannelCreate(name.c_str()))
52 throw std::invalid_argument(
SB()<<
"invalid channel: "<<name);
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));
68 if(chan) dbChannelDelete(chan);
71 void DBCH::swap(
DBCH& o)
73 std::swap(chan, o.chan);
76 void ASCred::update(
const pva::ChannelRequester::shared_pointer& req)
78 pva::PeerInfo::const_shared_pointer info(req->getPeerInfo());
79 std::string usertemp, hosttemp;
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) {
88 usertemp = usertemp.substr(sep+1);
92 usertemp = info->authority +
"/" + info->account;
95 const char role[] =
"role/";
97 groups.resize(info->roles.size());
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));
103 groups[idx].begin());
104 std::copy(it->begin(),
106 groups[idx].begin()+
sizeof(role)-1);
107 groups[idx][groups[idx].size()-1] =
'\0';
112 hosttemp = req->getRequesterName();
116 size_t sep = hosttemp.find_first_of(
':');
117 if(sep == std::string::npos) {
118 sep = hosttemp.size();
120 hosttemp.resize(sep);
122 host.resize(hosttemp.size()+1);
123 std::copy(hosttemp.begin(),
126 host[hosttemp.size()] =
'\0';
128 user.resize(usertemp.size()+1);
129 std::copy(usertemp.begin(),
132 user[usertemp.size()] =
'\0';
135 ASCLIENT::~ASCLIENT()
137 asRemoveClient(&aspvt);
138 for(
size_t i=0, N=grppvt.size(); i<N; i++) {
139 asRemoveClient(&grppvt[i]);
143 void ASCLIENT::add(dbChannel* chan,
ASCred& cred)
145 asRemoveClient(&aspvt);
147 (void)asAddClient(&aspvt, dbChannelRecord(chan)->asp, dbChannelFldDes(chan)->as_level, &cred.user[0], &cred.host[0]);
149 grppvt.resize(cred.groups.size(), 0);
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]);
157 bool ASCLIENT::canWrite() {
158 if(!asActive || (aspvt && asCheckPut(aspvt)))
160 for(
size_t i=0, N=grppvt.size(); i<N; i++) {
161 if(grppvt[i] && asCheckPut(grppvt[i]))
167 PVIF::PVIF(dbChannel *ch)
176 pvd::uint32 nsecMask;
178 pvd::BitSet maskALWAYS, maskALARM;
181 pvd::PVIntPtr status, severity, nsec, userTag;
182 pvd::PVStringPtr message;
184 pvTimeAlarm() :chan(NULL), nsecMask(0) {}
187 struct pvCommon :
public pvTimeAlarm {
189 pvd::BitSet maskVALUE, maskPROPERTY, maskVALUEPut;
191 pvd::PVDoublePtr displayLow, displayHigh, controlLow, controlHigh;
192 pvd::PVStringPtr egu, desc;
193 pvd::PVIntPtr fmt, prec;
195 pvd::PVScalarPtr warnLow, warnHigh, alarmLow, alarmHigh;
197 pvd::PVStringArrayPtr enumopts;
200 struct pvScalar :
public pvCommon {
201 typedef pvd::PVScalar pvd_type;
202 pvd::PVScalarPtr value;
205 struct pvArray :
public pvCommon {
206 typedef pvd::PVScalarArray pvd_type;
207 pvd::PVScalarArrayPtr value;
216 enum {mask = DBR_STATUS | DBR_AMSG | DBR_TIME | DBR_UTAG};
233 enum {mask = DBR_STATUS | DBR_AMSG | DBR_UNITS | DBR_PRECISION | DBR_TIME | DBR_UTAG | DBR_GR_DOUBLE | DBR_CTRL_DOUBLE | DBR_AL_DOUBLE};
250 enum {mask = DBR_STATUS | DBR_AMSG | DBR_TIME | DBR_UTAG | DBR_ENUM_STRS};
267 enum {mask = DBR_STATUS | DBR_AMSG | DBR_TIME | DBR_UTAG};
270 void attachTime(pvTimeAlarm& pvm,
const pvd::PVStructurePtr& pv)
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);
280 FMAP(userTag, PVInt,
"timeStamp.userTag", ALWAYS);
286 pvd::shared_vector<const std::string> buildFormats()
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);
300 pvd::shared_vector<const std::string> displayForms(buildFormats());
303 void attachMeta(pvCommon& pvm,
const pvd::PVStructurePtr& pv)
306 pvd::PVStructurePtr fmt(pv->getSubField<pvd::PVStructure>(
"display.form"));
308 fmt->getSubFieldT<pvd::PVStringArray>(
"choices")->replace(displayForms);
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);
330 template<
typename PVX>
331 void attachAll(PVX& pvm,
const pvd::PVStructurePtr& pv)
333 pvm.value = pv->getSubField<
typename PVX::pvd_type>(
"value.index");
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()) {
340 pvm.maskVALUEPut.set(fld->getFieldOffset());
342 pvm.maskVALUEPut.set(0);
346 template<
typename Meta>
347 void mapStatus(
const Meta& meta, pvd::PVInt* status, pvd::PVString* message)
350 if(meta.amsg[0]!=
'\0') {
351 message->put(meta.amsg);
354 if(meta.status<ALARM_NSTATUS)
355 message->put(epicsAlarmConditionStrings[meta.status]);
361 switch(meta.status) {
390 case READ_ACCESS_ALARM:
391 case WRITE_ACCESS_ALARM:
402 template<
typename META>
403 void putMetaImpl(
const pvTimeAlarm& pv,
const META& meta)
405 pvd::int32 nsec = meta.time.nsec;
407 pv.userTag->put(nsec&pv.nsecMask);
408 nsec &= ~pv.nsecMask;
411 pv.userTag->put(meta.utag);
414 pv.nsec->put(nsec); pv.sec->put(meta.time.secPastEpoch+POSIX_TIME_AT_EPICS_EPOCH);
417 void putTime(
const pvTimeAlarm& pv,
unsigned dbe, db_field_log *pfl)
420 long options = (int)metaTIME::mask, nReq = 0;
422 long status = dbChannelGet(pv.chan, dbChannelFinalFieldType(pv.chan), &meta, &options, &nReq, pfl);
424 throw std::runtime_error(
"dbGet for meta fails");
426 putMetaImpl(pv, meta);
428 mapStatus(meta, pv.status.get(), pv.message.get());
429 pv.severity->put(meta.severity);
433 void putValue(dbChannel *chan, pvd::PVScalar* value, db_field_log *pfl)
438 long status = dbChannelGet(chan, dbChannelFinalFieldType(chan), &buf, NULL, &nReq, pfl);
440 throw std::runtime_error(
"dbGet for meta fails");
444 memset(&buf, 0,
sizeof(buf));
447 switch(dbChannelFinalFieldType(chan)) {
448 #define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case DBR_##DBFTYPE: value->putFrom<PVATYPE>(buf.dbf_##DBFTYPE); break;
450 #define CASE_SKIP_BOOL
451 #include "pv/typemap.h"
453 #undef CASE_SKIP_BOOL
456 buf.dbf_STRING[
sizeof(buf.dbf_STRING)-1] =
'\0';
457 value->putFrom<std::string>(buf.dbf_STRING);
460 throw std::runtime_error(
"putValue unsupported DBR code");
464 void getValue(dbChannel *chan, pvd::PVScalar* value)
468 switch(dbChannelFinalFieldType(chan)) {
469 #define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case DBR_##DBFTYPE: buf.dbf_##DBFTYPE = value->getAs<PVATYPE>(); break;
471 #define CASE_SKIP_BOOL
472 #include "pv/typemap.h"
474 #undef CASE_SKIP_BOOL
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';
484 throw std::runtime_error(
"getValue unsupported DBR code");
487 long status = dbChannelPut(chan, dbChannelFinalFieldType(chan), &buf, 1);
489 throw std::runtime_error(
"dbPut for meta fails");
492 void getValue(dbChannel *chan, pvd::PVScalarArray* value)
494 short dbr = dbChannelFinalFieldType(chan);
496 if(dbr!=DBR_STRING) {
497 pvd::shared_vector<const void> buf;
500 long nReq = buf.size()/pvd::ScalarTypeFunc::elementSize(value->getScalarArray()->getElementType());
502 long status = dbChannelPut(chan, dbr, buf.data(), nReq);
504 throw std::runtime_error(
"dbChannelPut fails");
507 pvd::shared_vector<const std::string> buf;
511 std::vector<char> temp(buf.size()*MAX_STRING_SIZE);
513 for(
size_t i=0, N=buf.size(); i<N; i++)
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';
519 long status = dbChannelPut(chan, dbr, &temp[0], buf.size());
521 throw std::runtime_error(
"dbChannelPut fails");
525 void putValue(dbChannel *chan, pvd::PVScalarArray* value, db_field_log *pfl)
527 const short dbr = dbChannelFinalFieldType(chan);
529 long nReq = dbChannelFinalElements(chan);
530 const pvd::ScalarType etype = value->getScalarArray()->getElementType();
532 if(dbr!=DBR_STRING) {
534 pvd::shared_vector<void> buf(pvd::ScalarTypeFunc::allocArray(etype, nReq));
536 long status = dbChannelGet(chan, dbr, buf.data(), NULL, &nReq, pfl);
538 throw std::runtime_error(
"dbChannelGet for value fails");
540 buf.slice(0, nReq*pvd::ScalarTypeFunc::elementSize(etype));
542 value->putFrom(pvd::freeze(buf));
545 std::vector<char> temp(nReq*MAX_STRING_SIZE);
547 long status = dbChannelGet(chan, dbr, &temp[0], NULL, &nReq, pfl);
549 throw std::runtime_error(
"dbChannelGet for value fails");
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]);
557 value->putFrom(pvd::freeze(buf));
560 template<
typename META>
561 void putMeta(
const pvCommon& pv,
unsigned dbe, db_field_log *pfl)
564 long options = (int)META::mask, nReq = 0;
565 dbCommon *prec = dbChannelRecord(pv.chan);
567 long status = dbChannelGet(pv.chan, dbChannelFinalFieldType(pv.chan), &meta, &options, &nReq, pfl);
569 throw std::runtime_error(
"dbGet for meta fails");
571 putMetaImpl(pv, meta);
572 #define FMAP(MNAME, FNAME) pv.MNAME->put(meta.FNAME)
574 mapStatus(meta, pv.status.get(), pv.message.get());
575 FMAP(severity, severity);
577 if(dbe&DBE_PROPERTY) {
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);
587 if(META::mask&DBR_PRECISION && pv.prec) {
588 pv.prec->put(pvd::int32(meta.precision.dp));
590 #define FMAP(MASK, MNAME, FNAME) if(META::mask&(MASK) && pv.MNAME) pv.MNAME->putFrom(meta.FNAME)
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);
599 pvd::shared_vector<std::string> strs(meta.no_str);
600 for(
size_t i=0; i<strs.size(); i++)
602 meta.strs[i][
sizeof(meta.strs[i])-1] =
'\0';
603 strs[i] = meta.strs[i];
605 pv.enumopts->replace(pvd::freeze(strs));
610 template<
typename PVC,
typename META>
611 void putAll(
const PVC &pv,
unsigned dbe, db_field_log *pfl)
613 if(dbe&(DBE_VALUE|DBE_ARCHIVE)) {
614 putValue(pv.chan, pv.value.get(), pfl);
616 if(!(dbe&DBE_PROPERTY)) {
617 putTime(pv, dbe, pfl);
619 putMeta<META>(pv, dbe, pfl);
623 void findNSMask(pvTimeAlarm& pvmeta,
pdbRecordIterator& info,
const epics::pvData::PVStructurePtr& pvalue)
625 const char *UT = info.info(
"Q:time:tag");
626 if(UT && strncmp(UT,
"nsec:lsb:", 9)==0) {
628 pvmeta.nsecMask = epics::pvData::castUnsafe<pvd::uint32>(std::string(&UT[9]));
629 }
catch(std::exception& e){
631 std::cerr<<info.name()<<
" : Q:time:tag nsec:lsb: requires a number not '"<<UT[9]<<
"'\n";
634 if(pvmeta.nsecMask>0 && pvmeta.nsecMask<=32) {
635 pvmeta.userTag = pvalue->getSubField<pvd::PVInt>(
"timeStamp.userTag");
636 if(!pvmeta.userTag) {
639 pvd::uint64 mask = (1<<pvmeta.nsecMask)-1;
640 pvmeta.nsecMask = mask;
641 pvmeta.maskALWAYS.set(pvmeta.userTag->getFieldOffset());
647 void findFormat(pvTimeAlarm& pvmeta,
pdbRecordIterator& info,
const epics::pvData::PVStructurePtr& pvalue)
649 const char *FMT = info.info(
"Q:form");
651 pvd::PVScalarPtr fmt(pvalue->getSubField<pvd::PVScalar>(
"display.form.index"));
654 for(
size_t i=0; !found && i<displayForms.size(); i++) {
655 if((found=(displayForms[i]==FMT)))
656 fmt->putFrom<pvd::uint32>(i);
660 fmt->putFrom(std::string(FMT));
661 }
catch(std::exception& e){
662 errlogPrintf(
"%s: info(Q:form, \"%s\") is not known format: %s\n", info.name(), FMT, e.what());
669 pvd::Status checkDISP(dbChannel *chan)
671 dbCommon *prec = dbChannelRecord(chan);
673 if(prec->disp && dbChannelField(chan)!=&prec->disp)
674 ret = pvd::Status::error(
"Put Disabled");
678 template<
typename PVX,
typename META>
679 struct PVIFScalarNumeric :
public PVIF
682 const epics::pvData::PVStructurePtr pvalue;
684 PVIFScalarNumeric(dbChannel *ch,
const epics::pvData::PVFieldPtr& p, pvd::PVField *enclosing)
686 ,pvalue(std::tr1::dynamic_pointer_cast<pvd::PVStructure>(p))
689 throw std::runtime_error(
"Must attach to structure");
692 attachAll<PVX>(pvmeta, pvalue);
694 size_t bit = enclosing->getFieldOffset();
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);
709 findNSMask(pvmeta, info, pvalue);
710 findFormat(pvmeta, info, pvalue);
712 virtual ~PVIFScalarNumeric() {}
714 virtual void put(epics::pvData::BitSet& mask,
unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
717 putAll<PVX, META>(pvmeta, dbe, pfl);
718 mask |= pvmeta.maskALWAYS;
719 if(dbe&(DBE_VALUE|DBE_ARCHIVE))
720 mask |= pvmeta.maskVALUE;
722 mask |= pvmeta.maskALARM;
724 mask |= pvmeta.maskPROPERTY;
726 pvmeta.severity->put(3);
727 mask |= pvmeta.maskALARM;
732 virtual pvd::Status
get(
const epics::pvData::BitSet& mask, proc_t proc,
bool permit) OVERRIDE FINAL
734 pvd::Status ret = checkDISP(chan);
738 bool newval = mask.logical_and(pvmeta.maskVALUEPut);
741 getValue(pvmeta.chan, pvmeta.value.get());
743 ret = pvd::Status::error(
"Put not permitted");
745 if(newval || proc==PVIF::ProcForce) {
749 ret = pvd::Status::error(
"Process not permitted");
754 virtual unsigned dbe(
const epics::pvData::BitSet& mask) OVERRIDE FINAL
757 if(mask.logical_and(pvmeta.maskVALUE))
759 if(mask.logical_and(pvmeta.maskALARM))
761 if(mask.logical_and(pvmeta.maskPROPERTY))
770 pvd::ScalarType DBR2PVD(
short dbr)
773 #define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case DBR_##DBFTYPE: return pvd::pv##PVACODE;
775 #define CASE_SKIP_BOOL
776 #include "pv/typemap.h"
778 #undef CASE_SKIP_BOOL
780 case DBF_STRING:
return pvd::pvString;
782 throw std::invalid_argument(
"Unsupported DBR code");
785 short PVD2DBR(pvd::ScalarType pvt)
788 #define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case pvd::pv##PVACODE: return DBR_##DBFTYPE;
790 # define CASE_SQUEEZE_INT64
792 #include "pv/typemap.h"
794 # undef CASE_SQUEEZE_INT64
797 case pvd::pvString:
return DBF_STRING;
803 pvd::StructureConstPtr buildTimeStamp()
805 return pvd::FieldBuilder::begin()
806 ->add(
"secondsPastEpoch", pvd::pvLong)
807 ->add(
"nanoseconds", pvd::pvInt)
808 ->add(
"userTag", pvd::pvInt)
812 epics::pvData::FieldConstPtr
813 ScalarBuilder::dtype()
816 throw std::runtime_error(
"+type:\"scalar\" requires +channel:");
818 short dbr = dbChannelFinalFieldType(channel);
819 const long maxelem = dbChannelFinalElements(channel);
820 const pvd::ScalarType pvt = DBR2PVD(dbr);
822 if(INVALID_DB_REQ(dbr))
823 throw std::invalid_argument(
"DBF code out of range");
825 if(maxelem!=1 && dbr==DBR_ENUM)
828 pvd::FieldBuilderPtr builder(pvd::getFieldCreate()->createFieldBuilder());
829 pvd::StandardFieldPtr standard(pvd::getStandardField());
832 builder = builder->setId(
"epics:nt/NTEnum:1.0")
833 ->addNestedStructure(
"value")
835 ->add(
"index", pvd::pvInt)
836 ->addArray(
"choices", pvd::pvString)
839 builder = builder->setId(
"epics:nt/NTScalar:1.0")
842 builder = builder->setId(
"epics:nt/NTScalarArray:1.0")
843 ->addArray(
"value", pvt);
845 builder = builder->add(
"alarm", standard->alarm())
846 ->add(
"timeStamp", buildTimeStamp());
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")
857 ->add(
"index", pvd::pvInt)
858 ->addArray(
"choices", pvd::pvString)
861 ->add(
"control", standard->control());
864 builder = builder->add(
"valueAlarm", standard->doubleAlarm());
867 return builder->createStructure();
871 ScalarBuilder::attach(
const epics::pvData::PVStructurePtr& root,
const FieldName& fldname)
874 throw std::runtime_error(
"+type:\"scalar\" requires +channel:");
875 pvd::PVField *enclosing = 0;
876 pvd::PVFieldPtr fld(fldname.lookup(root, &enclosing));
878 const short dbr = dbChannelFinalFieldType(channel);
879 const long maxelem = dbChannelFinalElements(channel);
893 return new PVIFScalarNumeric<pvScalar, metaDOUBLE>(channel, fld, enclosing);
896 return new PVIFScalarNumeric<pvScalar, metaDOUBLE>(channel, fld, enclosing);
898 return new PVIFScalarNumeric<pvScalar, metaENUM>(channel, fld, enclosing);
900 return new PVIFScalarNumeric<pvScalar, metaSTRING>(channel, fld, enclosing);
918 return new PVIFScalarNumeric<pvArray, metaDOUBLE>(channel, fld, enclosing);
922 throw std::invalid_argument(
"Channel has invalid/unsupported DBR type");
927 struct PVIFPlain :
public PVIF
929 const typename PVD::shared_pointer field;
931 dbChannel *
const channel;
933 PVIFPlain(dbChannel *channel,
const epics::pvData::PVFieldPtr& fld, epics::pvData::PVField* enclosing=0)
935 ,field(std::tr1::static_pointer_cast<PVD>(fld))
939 throw std::logic_error(
"PVIFPlain attached type mis-match");
941 fieldOffset = enclosing->getFieldOffset();
943 fieldOffset = field->getFieldOffset();
946 virtual ~PVIFPlain() {}
948 virtual void put(epics::pvData::BitSet& mask,
unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
951 putValue(channel, field.get(), pfl);
952 mask.set(fieldOffset);
956 virtual pvd::Status
get(
const epics::pvData::BitSet& mask, proc_t proc,
bool permit) OVERRIDE FINAL
958 pvd::Status ret = checkDISP(chan);
962 bool newval = mask.get(fieldOffset);
965 getValue(channel, field.get());
967 ret = pvd::Status::error(
"Put not permitted");
969 if(newval || proc==PVIF::ProcForce) {
973 ret = pvd::Status::error(
"Process not permitted");
978 virtual unsigned dbe(
const epics::pvData::BitSet& mask) OVERRIDE FINAL
983 if(mask.get(fieldOffset) || mask.get(0))
991 explicit PlainBuilder(dbChannel* chan) :
PVIFBuilder(chan) {}
992 virtual ~PlainBuilder() {}
995 virtual epics::pvData::FieldConstPtr dtype() OVERRIDE FINAL {
997 throw std::runtime_error(
"+type:\"plain\" requires +channel:");
999 const short dbr = dbChannelFinalFieldType(channel);
1000 const long maxelem = dbChannelFinalElements(channel);
1001 const pvd::ScalarType pvt = DBR2PVD(dbr);
1003 if(INVALID_DB_REQ(dbr))
1004 throw std::invalid_argument(
"DBF code out of range");
1007 return pvd::getFieldCreate()->createScalar(pvt);
1009 return pvd::getFieldCreate()->createScalarArray(pvt);
1015 virtual PVIF* attach(
const epics::pvData::PVStructurePtr& root,
1016 const FieldName& fldname) OVERRIDE FINAL
1019 throw std::runtime_error(
"+type:\"plain\" requires +channel:");
1020 const long maxelem = dbChannelFinalElements(channel);
1022 pvd::PVField *enclosing = 0;
1023 pvd::PVFieldPtr fld(fldname.lookup(root, &enclosing));
1026 return new PVIFPlain<pvd::PVScalar>(channel, fld, enclosing);
1028 return new PVIFPlain<pvd::PVScalarArray>(channel, fld, enclosing);
1034 explicit AnyScalarBuilder(dbChannel* chan) :
PVIFBuilder(chan) {}
1035 virtual ~AnyScalarBuilder() {}
1038 virtual epics::pvData::FieldConstPtr dtype() OVERRIDE FINAL {
1040 return pvd::getFieldCreate()->createVariantUnion();
1046 virtual PVIF* attach(
const epics::pvData::PVStructurePtr& root,
1047 const FieldName& fldname) OVERRIDE FINAL
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);
1056 pvd::PVField *enclosing = 0;
1057 pvd::PVFieldPtr fld(fldname.lookup(root, &enclosing));
1059 pvd::PVUnion *value =
dynamic_cast<pvd::PVUnion*
>(fld.get());
1061 throw std::logic_error(
"Mis-matched attachment point");
1063 pvd::PVFieldPtr arr(value->get());
1066 arr = create->createPVScalar(pvt);
1068 arr = create->createPVScalarArray(pvt);
1073 return new PVIFPlain<pvd::PVScalar>(channel, arr, enclosing ? enclosing : arr.get());
1075 return new PVIFPlain<pvd::PVScalarArray>(channel, arr, enclosing ? enclosing : arr.get());
1080 struct PVIFMeta :
public PVIF
1084 PVIFMeta(dbChannel *channel,
const epics::pvData::PVFieldPtr& fld, epics::pvData::PVField* enclosing=0)
1087 pvd::PVStructurePtr field(std::tr1::dynamic_pointer_cast<pvd::PVStructure>(fld));
1089 throw std::logic_error(
"PVIFMeta attached type mis-match");
1090 meta.chan = channel;
1092 attachTime(meta, field);
1093 findNSMask(meta, info, field);
1094 findFormat(meta, info, field);
1096 meta.maskALWAYS.clear();
1097 meta.maskALWAYS.set(enclosing->getFieldOffset());
1098 meta.maskALARM.clear();
1099 meta.maskALARM.set(enclosing->getFieldOffset());
1103 virtual ~PVIFMeta() {}
1105 virtual void put(epics::pvData::BitSet& mask,
unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
1107 mask |= meta.maskALWAYS;
1109 mask |= meta.maskALARM;
1111 putTime(meta, dbe, pfl);
1114 virtual pvd::Status
get(
const epics::pvData::BitSet& mask, proc_t proc,
bool permit) OVERRIDE FINAL
1117 if(mask.logical_and(meta.maskALARM))
1118 return pvd::Status::warn(
"Put to meta field ignored");
1119 return pvd::Status::Ok;
1122 virtual unsigned dbe(
const epics::pvData::BitSet& mask) OVERRIDE FINAL
1124 if(mask.logical_and(meta.maskALARM))
1132 explicit MetaBuilder(dbChannel* chan) :
PVIFBuilder(chan) {}
1133 virtual ~MetaBuilder() {}
1136 virtual epics::pvData::FieldConstPtr dtype() OVERRIDE FINAL {
1137 throw std::logic_error(
"Don't call me");
1140 virtual epics::pvData::FieldBuilderPtr dtype(epics::pvData::FieldBuilderPtr& builder,
1141 const std::string& fld) OVERRIDE FINAL
1143 pvd::StandardFieldPtr std(pvd::getStandardField());
1145 return builder->add(
"alarm", std->alarm())
1146 ->add(
"timeStamp", buildTimeStamp());
1148 return builder->addNestedStructure(fld)
1149 ->add(
"alarm", std->alarm())
1150 ->add(
"timeStamp", buildTimeStamp())
1158 virtual PVIF* attach(
const epics::pvData::PVStructurePtr& root,
1159 const FieldName& fldname) OVERRIDE FINAL
1162 throw std::runtime_error(
"+type:\"meta\" requires +channel:");
1164 pvd::PVField *enclosing = 0;
1165 pvd::PVFieldPtr fld(fldname.lookup(root, &enclosing));
1167 return new PVIFMeta(channel, fld, enclosing);
1172 struct PVIFProc :
public PVIF
1174 PVIFProc(dbChannel *channel) :
PVIF(channel) {}
1176 virtual void put(epics::pvData::BitSet& mask,
unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
1181 virtual pvd::Status
get(
const epics::pvData::BitSet& mask, proc_t proc,
bool permit) OVERRIDE FINAL
1184 return PVIF::get(mask, PVIF::ProcForce, permit);
1187 virtual unsigned dbe(
const epics::pvData::BitSet& mask) OVERRIDE FINAL
1195 explicit ProcBuilder(dbChannel* chan) :
PVIFBuilder(chan) {}
1196 virtual ~ProcBuilder() {}
1199 virtual epics::pvData::FieldConstPtr dtype() OVERRIDE FINAL {
1200 throw std::logic_error(
"Don't call me");
1203 virtual epics::pvData::FieldBuilderPtr dtype(epics::pvData::FieldBuilderPtr& builder,
1204 const std::string& fld) OVERRIDE FINAL
1209 virtual PVIF* attach(
const epics::pvData::PVStructurePtr& root,
1210 const FieldName& fldname) OVERRIDE FINAL
1213 throw std::runtime_error(
"+type:\"proc\" requires +channel:");
1215 return new PVIFProc(channel);
1219 struct PVIFNoOp :
public PVIF
1221 PVIFNoOp(dbChannel *channel) :
PVIF(channel) {}
1223 virtual void put(epics::pvData::BitSet& mask,
unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
1226 virtual pvd::Status
get(
const epics::pvData::BitSet& mask, proc_t proc,
bool permit) OVERRIDE FINAL
1228 return pvd::Status();
1231 virtual unsigned dbe(
const epics::pvData::BitSet& mask) OVERRIDE FINAL
1239 explicit IDBuilder(dbChannel* chan) :
PVIFBuilder(chan) {}
1240 virtual ~IDBuilder() {}
1243 virtual epics::pvData::FieldConstPtr dtype() OVERRIDE FINAL {
1244 throw std::logic_error(
"Don't call me");
1247 virtual epics::pvData::FieldBuilderPtr dtype(epics::pvData::FieldBuilderPtr& builder,
1248 const std::string& fld) OVERRIDE FINAL
1253 virtual PVIF* attach(
const epics::pvData::PVStructurePtr& root,
1254 const FieldName& fldname) OVERRIDE FINAL
1256 return new PVIFNoOp(channel);
1262 pvd::Status
PVIF::get(
const epics::pvData::BitSet& mask, proc_t proc,
bool permit)
1264 dbCommon *precord = dbChannelRecord(chan);
1266 bool tryproc = proc!=ProcPassive ? proc==ProcForce :
1267 dbChannelField(chan) == &precord->proc ||
1268 (dbChannelFldDes(chan)->process_passive &&
1269 precord->scan == 0);
1275 return pvd::Status::error(
"Process not permitted");
1277 }
else if (precord->pact) {
1279 printf(
"%s: Active %s\n",
1280 epicsThreadGetNameSelf(), precord->name);
1281 precord->rpro = TRUE;
1284 precord->putf = TRUE;
1285 long err = dbProcess(precord);
1288 errSymLookup(err, buf,
sizeof(buf));
1289 std::ostringstream msg;
1290 msg<<
"process error : "<<buf;
1291 ret = pvd::Status::error(msg.str());
1299 epics::pvData::FieldBuilderPtr
1300 PVIFBuilder::dtype(epics::pvData::FieldBuilderPtr& builder,
1301 const std::string &fld)
1304 throw std::runtime_error(
SB()<<
"Can't attach +type "<<
typeid(*this).name()<<
" to root");
1306 epics::pvData::FieldConstPtr ftype(this->dtype());
1308 builder = builder->add(fld, ftype);
1313 PVIFBuilder* PVIFBuilder::create(
const std::string& type, dbChannel* chan)
1315 if(type.empty() || type==
"scalar")
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);
1328 throw std::runtime_error(std::string(
"Unknown +type=")+type);
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