diff --git a/documentation/records.dox b/documentation/records.dox index 2166878..75c465e 100644 --- a/documentation/records.dox +++ b/documentation/records.dox @@ -14,6 +14,7 @@ Device support for these record types must be written in C++. - statBin - pvstructin - tableAgg +- ndaroi All of these record types provide custom device support (dset) struct definitions. eg. 'struct svectorindset' @@ -233,4 +234,25 @@ record(tableAgg, "my:table") { When processed, the default device support will read each INPx field and populate the corresponding value with the obtained array. -*/ \ No newline at end of file +@section qsrv_ndaroi ndaroi record type + +The ndaroi record is the analog of the areaDetector plugin NDPluginROI. It performs +a ROI in a NTNDArray PVstrucuture, typically stored inside a ndain record. + +Currently, it supports only Gray scale images, with the ROI dimensions specified +by the fields: + +XMIN and XSIZE specifies the X offset and the horizontal size of the ROI, respectively + +YMIN and YSIZE specifies the Y offset and the vertical size of the ROI, respectively + +ZMIN and ZSIZE specifies the color offset and the color size of the ROI, respectively (unsupported for now) + +@subsection qsrv_ndaroi_softchannel ndaroi default device support + +During initialization, the default device support for ndaroi will read a Structure from +the INP field and then proceed to read the PVStructure from the input record. + +Type changes after the first record processing are unsupported for now. + +*/ diff --git a/iocBoot/iocimagedemo/ndaroi.bob b/iocBoot/iocimagedemo/ndaroi.bob new file mode 100644 index 0000000..72d84df --- /dev/null +++ b/iocBoot/iocimagedemo/ndaroi.bob @@ -0,0 +1,157 @@ + + + Display + 1000 + + Label + TITLE + NDAROI Example + 0 + 0 + 550 + 31 + + + + + + + + + true + + + Image + pva://TST:image3:Array + 20 + 90 + + true + X + 0.0 + 1024.0 + + + + + + + + + + + true + Y + 768.0 + 0.0 + + + + + + + + + + false + + + ROI 0 + + + + + true + true + pva://TST:image3:ArrayROI.XMIN + pva://TST:image3:ArrayROI.YMIN + pva://TST:image3:ArrayROI.XSIZE + pva://TST:image3:ArrayROI.YSIZE + + + + + + Image_1 + pva://TST:image3:ArrayROI + 510 + 90 + + true + X + 0.0 + 1024.0 + + + + + + + + + + + true + Y + 768.0 + 0.0 + + + + + + + + + + false + + + Text Update_1 + pva://TST:image3:ArrayROIStat/max + 690 + 410 + + + Label_3 + Max + 590 + 410 + + + Text Update_2 + pva://TST:image3:ArrayROIStat/min + 690 + 430 + + + Label_4 + Min + 590 + 430 + + + Text Update_3 + pva://TST:image3:ArrayROIStat/std + 690 + 450 + + + Label_5 + Std + 590 + 450 + + + Text Update_4 + pva://TST:image3:ArrayROIStat/mean + 690 + 470 + + + Label_6 + Mean + 590 + 470 + + diff --git a/iocBoot/iocimagedemo/ndaroi.db b/iocBoot/iocimagedemo/ndaroi.db new file mode 100644 index 0000000..164975a --- /dev/null +++ b/iocBoot/iocimagedemo/ndaroi.db @@ -0,0 +1,23 @@ +record(ndain, "$(N):Array") { + field(DTYP, "QSRV Demo") + field(W, "1024") + field(H, "768") + field(PINI, "YES") + field(SCAN, "1 second") + field(FLNK, "$(N):ArrayROI") +} + +record(ndaroi, "$(N):ArrayROI") { + field(DTYP, "Soft Channel") + field(INP, "$(N):Array") + field(XMIN, "0") + field(XSIZE, "100") + field(YMIN, "0") + field(YSIZE, "100") + field(FLNK, "$(N):ArrayROIStat") +} + +record(statBin, "$(N):ArrayROIStat") { + field(INP, "$(N):ArrayROI NPP") + field(TSEL, "$(N):ArrayROI.TIME NPP") +} diff --git a/iocBoot/iocimagedemo/st.cmd b/iocBoot/iocimagedemo/st.cmd index d7323b8..b6b198c 100755 --- a/iocBoot/iocimagedemo/st.cmd +++ b/iocBoot/iocimagedemo/st.cmd @@ -1,7 +1,8 @@ -#!../../bin/linux-x86_64-debug/softIocPVA +#!../../bin/linux-x86_64/softIocPVA dbLoadRecords("image.db","N=TST:image1") dbLoadRecords("ndain.db","N=TST:image2") +dbLoadRecords("ndaroi.db","N=TST:image3") dbLoadRecords("table.db","N=TST:table1") iocInit() diff --git a/pdbApp/Makefile b/pdbApp/Makefile index f74d15f..eb08cf0 100644 --- a/pdbApp/Makefile +++ b/pdbApp/Makefile @@ -35,6 +35,7 @@ DBDINC += columnarinRecord DBDINC += columnaroutRecord DBDINC += statBinRecord DBDINC += ndainRecord +DBDINC += ndaroiRecord DBDINC += tableAggRecord DBDINC += multiArrayCommon @@ -85,6 +86,7 @@ qsrv_SRCS += columnaroutRecord.cpp qsrv_SRCS += statBinRecord.cpp qsrv_SRCS += ndainRecord.cpp +qsrv_SRCS += ndaroiRecord.cpp qsrv_SRCS += tableAggRecord.cpp qsrv_SRCS += multiArrayCommon.cpp @@ -142,6 +144,7 @@ $(COMMON_DIR)/softIocPVA.dbd: ../columnarinRecord.dbd $(COMMON_DIR)/softIocPVA.dbd: ../columnaroutRecord.dbd $(COMMON_DIR)/softIocPVA.dbd: ../statBinRecord.dbd $(COMMON_DIR)/softIocPVA.dbd: ../ndainRecord.dbd +$(COMMON_DIR)/softIocPVA.dbd: ../ndaroiRecord.dbd $(COMMON_DIR)/softIocPVA.dbd: ../tableAggRecord.dbd $(COMMON_DIR)/softIocPVA.dbd: $(COMMON_DIR)/qsrv.dbd diff --git a/pdbApp/ndaroiRecord.cpp b/pdbApp/ndaroiRecord.cpp new file mode 100644 index 0000000..1e67b2b --- /dev/null +++ b/pdbApp/ndaroiRecord.cpp @@ -0,0 +1,615 @@ +/*************************************************************************\ +* Copyright (c) 2020 Michael Davidsaver +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef USE_TYPED_RSET +# define USE_TYPED_RSET +#endif +#ifndef USE_TYPED_DSET +# define USE_TYPED_DSET +#endif + +#include +#include +#include +#include +#include +#include + +// include by ndaroiRecord.h +#include "epicsTypes.h" +#include "link.h" +#include "epicsMutex.h" +#include "ellLib.h" +#include "devSup.h" +#include "epicsTime.h" + +#include "helper.h" + +#include +#include +#include +#include + +#define GEN_SIZE_OFFSET +#include +#undef GEN_SIZE_OFFSET +#include + +#include +#include +#include + +namespace pvd = epics::pvData; + +template +struct fldCached { + fldType *fld; // record field address + DBLINK *infld; // record input link field address + fldType val; // cached record value for detecting changed + + fldCached(fldType *fld, DBLINK *infld, fldType val) : fld(fld), infld(infld), val(val) {}; + fldCached(fldType *fld, DBLINK *infld) : fld(fld), infld(infld), val(0) {}; +}; + +struct ndaroiPvt { + typedef std::map> > + mapType; + typedef std::vector vecType; + // for each dimension: + // * map of NTNTArray field name: record field offset + vecType origDims; + + ndaroiPvt(vecType origDims) : origDims(origDims) {}; + ndaroiPvt() {}; +}; + +namespace { + +ELLLIST vfPVStructureList = ELLLIST_INIT; +VFieldTypeNode vfPVStructureNode[3]; + +template +static void convert_dim(const pvd::shared_vector& in, + const pvd::PVStructureArrayPtr& in_dims, + pvd::shared_vector& out, + const pvd::PVStructureArrayPtr& out_dims, + int dim) +{ + epicsInt32 inStep, outStep, inOffset; + epicsInt32 outSize; + int incDir = 1; + size_t inc; + + inOffset = (out_dims->view()[dim])->getSubFieldT("offset")->get(); + outSize = (out_dims->view()[dim])->getSubFieldT("size")->get(); + + inStep = 1; + outStep = 1; + for (int i = 0; i < dim; ++i) { + inStep *= (in_dims->view()[i])->getSubFieldT("size")->get(); + outStep *= (out_dims->view()[i])->getSubFieldT("size")->get(); + } + + inc = incDir * inStep; + + pvd::shared_vector in_offs(in); + in_offs.slice(inOffset*inStep); + pvd::shared_vector out_offs(out); + + for (epicsInt32 i = 0; i < outSize; ++i){ + if (dim > 0) { + convert_dim(in_offs, in_dims, out_offs, out_dims, dim-1); + } + else { + out_offs[0] = in_offs[0]; + } + + in_offs.slice(inc); + out_offs.slice(outStep); + } +} + +template +static void convert_dims(struct ndaroiRecord *prec, const pvd::PVStructureArrayPtr& new_dims) +{ + typedef typename arrayType::value_type valueType; + typedef typename arrayType::const_svector vectorType; + + // array value + std::tr1::shared_ptr pvarr = prec->val->getSubFieldT("value") + ->get >(); + vectorType arr = pvarr->view(); + + // dimensions + pvd::PVStructureArrayPtr dims = prec->val->getSubFieldT("dimension"); + + // alloc buffer + epicsInt32 length = 1; + for (size_t i = 0; i < new_dims->getLength(); ++i) { + length *= (new_dims->view()[i])->getSubFieldT("size")->get(); + } + pvd::shared_vector buf(length); + + convert_dim(arr, dims, buf, new_dims, dims->getLength()-1); + + // update value/dimensions + pvarr->replace(pvd::freeze(buf)); + prec->chg.set(pvarr->getFieldOffset()); + + dims->copy(*new_dims); + prec->chg.set(dims->getFieldOffset()); +} + +// convert dimensions +static void convert_dims(struct ndaroiRecord *prec, const pvd::PVStructureArrayPtr& new_dims) +{ + pvd::PVScalarArrayPtr pvarr = prec->val->getSubFieldT("value") + ->get(); + + // union can be null + if (!pvarr) { + throw std::runtime_error("empty union"); + } + + pvd::ScalarType stype = pvarr->getScalarArray() + ->getElementType(); + + switch(stype) { + case pvd::pvBoolean: convert_dims(prec, new_dims); break; + case pvd::pvByte: convert_dims (prec, new_dims); break; + case pvd::pvShort: convert_dims (prec, new_dims); break; + case pvd::pvInt: convert_dims (prec, new_dims); break; + case pvd::pvLong: convert_dims (prec, new_dims); break; + case pvd::pvUByte: convert_dims (prec, new_dims); break; + case pvd::pvUShort: convert_dims (prec, new_dims); break; + case pvd::pvUInt: convert_dims (prec, new_dims); break; + case pvd::pvULong: convert_dims (prec, new_dims); break; + case pvd::pvFloat: convert_dims (prec, new_dims); break; + case pvd::pvDouble: convert_dims (prec, new_dims); break; + default: + throw std::runtime_error("unsupported array type"); + break; + } +} + +// convert PVScalarArray to the specified dimensions +static void convert(struct ndaroiRecord *prec, const pvd::PVStructureArrayPtr& new_dims) +{ + convert_dims(prec, new_dims); +} + +long initialize() +{ + vfPVStructureNode[0].vtype = &vfStructure; + ellAdd(&vfPVStructureList, &vfPVStructureNode[0].node); + vfPVStructureNode[1].vtype = &vfPVStructure; + ellAdd(&vfPVStructureList, &vfPVStructureNode[1].node); + vfPVStructureNode[2].vtype = &vfSharedVector; + ellAdd(&vfPVStructureList, &vfPVStructureNode[2].node); + + return 0; +} + +long init_record(struct dbCommon *pcommon, int pass) +{ + ndaroiRecord *prec = (ndaroiRecord*)pcommon; + ndaroidset *pdset = (ndaroidset *)(prec->dset); + + if(pass==0) { + new (&prec->val) pvd::PVStructurePtr(); + prec->rpvt = new ndaroiPvt; + new (&prec->chg) pvd::BitSet(); + new (&prec->vld) pvd::BitSet(); + new (&prec->ptyp) pvd::StructureConstPtr(); + + if (!pdset) { + recGblSetSevrMsg(prec, READ_ALARM, INVALID_ALARM, "no DSET"); + recGblRecordError(S_dev_noDSET, prec, "ndaroi: no DSET"); + return S_dev_noDSET; + } + + // initialize dimension fields + ndaroiPvt::mapType xdim, ydim, zdim; + std::tr1::shared_ptr> xsize(new struct fldCached(&prec->xsize, &prec->inxsize, 1)); + std::tr1::shared_ptr> xmin(new struct fldCached(&prec->xmin, &prec->inxmin, 0)); + std::tr1::shared_ptr> ysize(new struct fldCached(&prec->ysize, &prec->inysize, 1)); + std::tr1::shared_ptr> ymin(new struct fldCached(&prec->ymin, &prec->inymin, 0)); + std::tr1::shared_ptr> zsize(new struct fldCached(&prec->zsize, &prec->inzsize, 1)); + std::tr1::shared_ptr> zmin(new struct fldCached(&prec->zmin, &prec->inzmin, 0)); + xdim["size"] = xsize; + xdim["offset"] = xmin; + ydim["size"] = ysize; + ydim["offset"] = ymin; + zdim["size"] = zsize; + zdim["offset"] = zmin; + prec->rpvt->origDims.push_back(xdim); + prec->rpvt->origDims.push_back(ydim); + prec->rpvt->origDims.push_back(zdim); + + } else { // pass==1 + if(pdset->common.init_record) { + long ret = pdset->common.init_record(pcommon); + if(ret) + return ret; + } + + if(!prec->val) { + recGblRecordError(S_dev_noDSET, prec, "ndaroi: init_record must set VAL to a valid PVStructure"); + return S_db_badDbrtype; + } + + prec->ptyp = prec->val->getStructure(); + } + return 0; +} + +long readValue(ndaroiRecord *prec, ndaroidset *pdset) +{ + epicsInt32 oldID = prec->id; + long status = 0; + + /* fetch ROI dimensions */ + FOREACH(ndaroiPvt::vecType::iterator, it, end, prec->rpvt->origDims) { + FOREACH(ndaroiPvt::mapType::iterator, it2, end2, *it) { + + // fetch link if not constant + if(!status && !dbLinkIsConstant(it2->second->infld)) + status = dbGetLink(it2->second->infld, DBF_ULONG, it2->second->fld, 0, 0); + + // "size" cannot be 0, fix it + if (!it2->first.compare("size") && *it2->second->fld == 0) + *it2->second->fld = 1; + + // store cached value for detecting change + it2->second->val = *it2->second->fld; + } + } + + if(status) + return status; + + prec->chg.clear(); + + // read pvstructure + status = pdset->read_pvs(prec); + + // type change + if(!status && prec->val && prec->val->getStructure() != prec->ptyp) { + prec->ptyp = prec->val->getStructure(); + } + + try { + // extract dims from new ROI dimensions + pvd::PVStructureArrayPtr dims = prec->val->getSubFieldT("dimension"); + pvd::StructureArrayConstPtr dims_type = dims->getStructureArray(); + pvd::PVStructureArrayPtr new_dims = dims_type->build(); + { + pvd::StructureConstPtr elemType = dims_type->getStructure(); + pvd::PVStructureArray::svector arr; + + FOREACH(ndaroiPvt::vecType::iterator, it, end, prec->rpvt->origDims) { + // build new dimension + std::tr1::shared_ptr dim = elemType->build(); + + FOREACH(ndaroiPvt::mapType::iterator, it2, end2, *it) + dim->getSubFieldT(it2->first)->put(*it2->second->fld); + + arr.push_back(dim); + } + + new_dims->replace(pvd::freeze(arr)); + } + + // convert to new dimensions + convert(prec, new_dims); + + /* Post events if dimensions changed */ + FOREACH(ndaroiPvt::vecType::iterator, it, end, prec->rpvt->origDims) { + FOREACH(ndaroiPvt::mapType::iterator, it2, end2, *it) { + if (it2->second->val != *it2->second->fld) + db_post_events(prec, it2->second->fld, DBE_VALUE|DBE_ARCHIVE); + } + } + + if(oldID != prec->id) { + pvd::PVIntPtr id(prec->val->getSubFieldT("uniqueId")); + id->put(prec->id); + prec->chg.set(id->getFieldOffset()); + } + + prec->vld |= prec->chg; + } catch(std::exception& e) { + errlogPrintf("%s: readValue: %s\n", prec->name, e.what()); + recGblSetSevrMsg(prec, READ_ALARM, INVALID_ALARM, "readValue: %s", e.what()); + return -1; + } + + return status; +} + +void monitor(ndaroiRecord *prec) +{ + int monitor_mask = recGblResetAlarms(prec); + + if(monitor_mask & DBE_ALARM) { + pvd::PVScalarPtr fld(prec->val->getSubFieldT("alarm.severity")); + fld->putFrom(prec->sevr); + prec->chg.set(fld->getFieldOffset()); + + //TODO: map status properly + fld = prec->val->getSubFieldT("alarm.status"); + fld->putFrom(prec->stat ? 1 : 0); + prec->chg.set(fld->getFieldOffset()); + + fld = prec->val->getSubFieldT("alarm.message"); + fld->putFrom(std::string(prec->amsg)); + prec->chg.set(fld->getFieldOffset()); + } + + { + pvd::PVScalarPtr fld(prec->val->getSubFieldT("timeStamp.secondsPastEpoch")); + fld->putFrom(prec->time.secPastEpoch + POSIX_TIME_AT_EPICS_EPOCH); + prec->chg.set(fld->getFieldOffset()); + + fld = prec->val->getSubFieldT("timeStamp.nanoseconds"); + fld->putFrom(prec->time.nsec); + prec->chg.set(fld->getFieldOffset()); + + fld = prec->val->getSubFieldT("timeStamp.userTag"); + fld->putFrom(prec->utag); + prec->chg.set(fld->getFieldOffset()); + + monitor_mask |= DBE_VALUE|DBE_ARCHIVE; + } + + db_post_events(prec, &prec->val, monitor_mask); +} + +long process(struct dbCommon *pcommon) +{ + ndaroiRecord *prec = (ndaroiRecord*)pcommon; + ndaroidset *pdset = (ndaroidset *)(prec->dset); + unsigned char pact=prec->pact; + long status; + + if( (pdset == NULL) || (pdset->read_pvs == NULL) ) { + prec->pact = TRUE; + recGblSetSevrMsg(prec, READ_ALARM, INVALID_ALARM, "no read_pvs"); + errlogPrintf("%s: process: %s\n", prec->name, "read_pvs"); + return S_dev_missingSup; + } else if(!prec->ptyp) { + prec->pact = TRUE; + recGblSetSevrMsg(prec, READ_ALARM, INVALID_ALARM, "no PTYP"); + return S_dev_NoInit; + } + + status = readValue(prec, pdset); /* read the new value */ + /* check if device support set pact */ + if (!pact && prec->pact) + return(0); + + prec->pact = TRUE; + recGblGetTimeStamp(prec); + + try { + /* check event list */ + monitor(prec); + } + catch (std::exception& e) { + errlogPrintf("%s: process: %s\n", prec->name, e.what()); + recGblSetSevrMsg(prec, READ_ALARM, INVALID_ALARM, "process: %s", e.what()); + return S_db_badField; + } + + /* process the forward scan link record */ + recGblFwdLink(prec); + + prec->pact=FALSE; + return status; +} + +long cvt_dbaddr(DBADDR *paddr) +{ + ndaroiRecord *prec = (ndaroiRecord*)paddr->precord; + + // we don't insist devsup allocate the required NELM + paddr->ro = 1; + // arbitrary limit + paddr->no_elements = 1; + // no access + paddr->field_type = DBF_NOACCESS; + + // we provide vfield access for VAL and PTYP + paddr->vfields = &vfPVStructureList; + + return 0; +} + +long get_array_info(DBADDR *paddr, long *no_elements, long *offset) +{ + return S_db_badField; +} + +long put_array_info(DBADDR *paddr, long nNew) +{ + return S_db_noMod; +} + +long get_vfield(struct dbAddr *paddr, struct VField *p) +{ + ndaroiRecord *prec = (ndaroiRecord*)paddr->precord; + + try { + if(p->vtype==&vfPVStructure) { + VSharedPVStructure *pstr = (VSharedPVStructure*)p; + if(dbGetFieldIndex(paddr)==ndaroiRecordVAL) { + if(!*pstr->value) + return S_db_notInit; + (*pstr->value)->copy(*prec->val); + *pstr->changed = prec->vld; + return 0; + } + + } else if(p->vtype==&vfStructure) { + VSharedStructure *pstr = (VSharedStructure*)p; + if(dbGetFieldIndex(paddr)==ndaroiRecordVAL) { + *pstr->value = prec->ptyp; + return 0; + } + } else if(p->vtype==&vfSharedVector) { + VSharedVector *pstr = (VSharedVector*)p; + if(dbGetFieldIndex(paddr)==ndaroiRecordVAL) { + pvd::PVScalarArrayPtr pvarr = prec->val->getSubFieldT("value") + ->get(); + if (!pvarr) { + throw std::runtime_error("empty \"value\" union"); + } + pvarr->getAs(*pstr->value); + return 0; + } + } + } + catch (std::exception& e) { + errlogPrintf("%s: get_vfield: %s\n", prec->name, e.what()); + return S_db_badChoice; + } + + return S_db_badChoice; +} + +long put_vfield(struct dbAddr *paddr, const struct VField *p) +{ + ndaroiRecord *prec = (ndaroiRecord*)paddr->precord; + + if(p->vtype==&vfPVStructure) { + const VSharedPVStructure *pstr = (const VSharedPVStructure*)p; + if(dbGetFieldIndex(paddr)==ndaroiRecordVAL) { + prec->val->copy(**pstr->value); + prec->vld |= *pstr->changed; + return 0; + } + } + return S_db_badChoice; +} + +#define report NULL +#define special NULL +#define get_value NULL +#define get_units NULL +#define get_precision NULL +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +#define get_graphic_double NULL +#define get_control_double NULL +#define get_alarm_double NULL + +rset ndaroiRSET={ + RSETNUMBER, + report, + initialize, + init_record, + process, + special, + get_value, + cvt_dbaddr, + get_array_info, + put_array_info, + get_units, + get_precision, + get_enum_str, + get_enum_strs, + put_enum_str, + get_graphic_double, + get_control_double, + get_alarm_double, + get_vfield, + put_vfield, +}; + +long readInitialPVStructure(struct link *pinp, void *raw) +{ + ndaroiRecord *prec = (ndaroiRecord *) pinp->precord; + pvd::StructureConstPtr type; + + VSharedStructure ival; + ival.vtype = &vfStructure; + ival.value = &type; + + long status = dbGetLink(pinp, DBR_VFIELD, &ival, 0, 0); + + if (status) + return status; + + prec->val = pvd::getPVDataCreate()->createPVStructure(type); + + return 0; +} + +long readLocked(struct link *pinp, void *raw) +{ + const bool* doload = static_cast(raw); + ndaroiRecord *prec = (ndaroiRecord *) pinp->precord; + + VSharedPVStructure ival; + ival.vtype = &vfPVStructure; + ival.value = &prec->val; + ival.changed = &prec->chg; + + long status = *doload ? dbLoadLink(pinp, DBR_VFIELD, &ival) : dbGetLink(pinp, DBR_VFIELD, &ival, 0, 0); + + if (status) + return status; + + if (dbLinkIsConstant(&prec->tsel) && + prec->tse == epicsTimeEventDeviceTime) + dbGetTimeStamp(pinp, &prec->time); + + return status; +} + +long init_record(struct dbCommon *pcommon) +{ + ndaroiRecord *prec = (ndaroiRecord *)pcommon; + bool doload = true; + + if (readLocked(&prec->inp, &doload)) { + // try to get at least Structure from link + readInitialPVStructure(&prec->inp, 0); + prec->udf = FALSE; + } + + return 0; +} + +long read_pvs(ndaroiRecord* prec) +{ + long status = 0; + bool doload = false; + + if(!status) + status = dbLinkDoLocked(&prec->inp, readLocked, &doload); + + if (status == S_db_noLSET) + status = readLocked(&prec->inp, &doload); + + if (!status && !dbLinkIsConstant(&prec->inp)) + prec->udf = FALSE; + + return status; +} + +ndaroidset devNDAROISoft = { + {5, NULL, NULL, &init_record, NULL}, + &read_pvs +}; + +} // namespace + +extern "C" { +epicsExportAddress(rset,ndaroiRSET); +epicsExportAddress(dset,devNDAROISoft); +} diff --git a/pdbApp/ndaroiRecord.dbd b/pdbApp/ndaroiRecord.dbd new file mode 100644 index 0000000..2bacd43 --- /dev/null +++ b/pdbApp/ndaroiRecord.dbd @@ -0,0 +1,149 @@ +#************************************************************************* +# Copyright (c) 2020 Michael Davidsaver +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + + +menu(menuColor) { + choice(menuColorGray, "GRAY") + # ... RGB8 etc. +} + +include "menuYesNo.dbd" + +recordtype(ndaroi) { + include "dbCommon.dbd" + + %/* Declare Device Support Entry Table */ + %#include + %#include + %#include + %#ifdef __cplusplus + %extern "C" { + %#endif + %typedef struct ndaroidset { + % dset common; /*init_record returns: (-1,0)=>(failure,success)*/ + % long (*read_pvs)(struct ndaroiRecord *prec); /*returns: (-1,0)=>(failure,success)*/ + %} ndaroidset; + %#define HAS_ndaroidset + %struct ndaroiPvt; + %#ifdef __cplusplus + %} + %#endif + field(VAL,DBF_NOACCESS) { + prompt("NTNDArray image") + promptgroup("40 - Input") + asl(ASL0) + pp(TRUE) + special(SPC_DBADDR) + extra("::epics::pvData::PVStructurePtr val") + } + field(PTYP,DBF_NOACCESS) { + prompt("Type") + interest(3) + special(SPC_DBADDR) + extra("::epics::pvData::StructureConstPtr ptyp") + } + field(INP,DBF_INLINK) { + prompt("Input Specification") + promptgroup("40 - Input") + interest(1) + } + field(XENA, DBF_MENU){ + prompt("Enable ROI for X dimension") + interest(2) + promptgroup("40 - Input") + menu(menuYesNo) + } + field(XMIN, DBF_ULONG){ + prompt("First pixel in X dimension") + interest(3) + } + field(INXMIN, DBF_INLINK) { + prompt("Read first pixel in X dimension") + promptgroup("40 - Input") + interest(3) + } + field(XSIZE, DBF_ULONG){ + prompt("Size of ROI in the X dimension") + interest(3) + } + field(INXSIZE, DBF_INLINK) { + prompt("Read size of ROI in the X dimension") + promptgroup("40 - Input") + interest(3) + } + field(YENA, DBF_MENU){ + prompt("Enable ROI for Y dimension") + interest(2) + promptgroup("40 - Input") + menu(menuYesNo) + } + field(YMIN, DBF_ULONG){ + prompt("First pixel in Y dimension") + interest(3) + } + field(INYMIN, DBF_INLINK) { + prompt("Read first pixel in the Y dimension") + promptgroup("40 - Input") + interest(3) + } + field(YSIZE, DBF_ULONG){ + prompt("Size of ROI in the Y dimension") + interest(3) + } + field(INYSIZE, DBF_INLINK) { + prompt("Read size of ROI in the Y dimension") + promptgroup("40 - Input") + interest(3) + } + field(ZENA, DBF_MENU){ + prompt("Enable ROI for Y dimension") + interest(2) + promptgroup("40 - Input") + menu(menuYesNo) + } + field(ZMIN, DBF_ULONG){ + prompt("First pixel in Z dimension") + interest(3) + } + field(INZMIN, DBF_INLINK) { + prompt("Read first pixel in the Z dimension") + promptgroup("40 - Input") + interest(3) + } + field(ZSIZE, DBF_ULONG){ + prompt("Size of ROI in the Z dimension") + interest(3) + } + field(INZSIZE, DBF_INLINK) { + prompt("Read size of ROI in the Z dimension") + promptgroup("40 - Input") + interest(3) + } + field(COL, DBF_MENU) { + prompt("Color Mode") + interest(3) + special(SPC_NOMOD) + menu(menuColor) + } + field(ID, DBF_LONG) { + prompt("Unique ID") + interest(3) + special(SPC_NOMOD) + } + field(RPVT, DBF_NOACCESS) { + extra("struct ndaroiPvt *rpvt") + } + field(CHG,DBF_NOACCESS) { + special(SPC_NOMOD) + extra("::epics::pvData::BitSet chg") + } + field(VLD,DBF_NOACCESS) { + special(SPC_NOMOD) + extra("::epics::pvData::BitSet vld") + } +} + +device(ndaroi, CONSTANT, devNDAROISoft, "Soft Channel") diff --git a/pdbApp/qsrv-vfield.dbd b/pdbApp/qsrv-vfield.dbd index 666415b..5c5a7e7 100644 --- a/pdbApp/qsrv-vfield.dbd +++ b/pdbApp/qsrv-vfield.dbd @@ -4,6 +4,7 @@ include "columnarinRecord.dbd" include "columnarinRecord.dbd" include "statBinRecord.dbd" include "ndainRecord.dbd" +include "ndaroiRecord.dbd" include "tableAggRecord.dbd" include "pvstructinRecord.dbd"