diff --git a/pdbApp/pdb.cpp b/pdbApp/pdb.cpp index 1ff104a..c6eab06 100644 --- a/pdbApp/pdb.cpp +++ b/pdbApp/pdb.cpp @@ -132,11 +132,32 @@ void PDBProvider::destroy() {} std::string PDBProvider::getProviderName() { return "PDBProvider"; } +namespace { +struct ChannelFindRequesterNOOP : public pva::ChannelFind +{ + const pva::ChannelProvider::weak_pointer provider; + ChannelFindRequesterNOOP(const pva::ChannelProvider::shared_pointer& prov) : provider(prov) {} + virtual ~ChannelFindRequesterNOOP() {} + virtual void destroy() {} + virtual std::tr1::shared_ptr getChannelProvider() { return provider.lock(); } + virtual void cancel() {} +}; +} + pva::ChannelFind::shared_pointer PDBProvider::channelFind(const std::string &channelName, const pva::ChannelFindRequester::shared_pointer &requester) { - pva::ChannelFind::shared_pointer ret; - requester->channelFindResult(pvd::Status(pvd::Status::STATUSTYPE_ERROR, ""), ret, false); + pva::ChannelFind::shared_pointer ret(new ChannelFindRequesterNOOP(shared_from_this())); + + bool found = false; + { + epicsGuard G(transient_pv_map.mutex()); + if(persist_pv_map.find(channelName)!=persist_pv_map.end() + || transient_pv_map.find(channelName) + || dbChannelTest(channelName.c_str())==0) + found = true; + } + requester->channelFindResult(pvd::Status(), ret, found); return ret; } diff --git a/pdbApp/pdbgroup.cpp b/pdbApp/pdbgroup.cpp index b9715fd..4ef44d5 100644 --- a/pdbApp/pdbgroup.cpp +++ b/pdbApp/pdbgroup.cpp @@ -82,6 +82,9 @@ void PDBGroupGet::get() pvif[i]->put(*changed, DBE_VALUE|DBE_ALARM|DBE_PROPERTY, NULL); } } + //TODO: report unused fields as changed? + changed->clear(); + changed->set(0); requester->getDone(pvd::Status(), shared_from_this(), pvf, changed); } diff --git a/pdbApp/pdbsingle.cpp b/pdbApp/pdbsingle.cpp index d7342d9..b7c4c55 100644 --- a/pdbApp/pdbsingle.cpp +++ b/pdbApp/pdbsingle.cpp @@ -6,6 +6,14 @@ namespace pvd = epics::pvData; namespace pva = epics::pvAccess; +PDBSinglePV::PDBSinglePV(DBCH& chan, + const PDBProvider::shared_pointer& prov) + :provider(prov) +{ + this->chan.swap(chan); + fielddesc = PVIF::dtype(this->chan); +} + pva::Channel::shared_pointer PDBSinglePV::connect(const std::tr1::shared_ptr& prov, const pva::ChannelRequester::shared_pointer& req) @@ -53,5 +61,8 @@ void PDBSingleGet::get() DBScanLocker L(channel->pv->chan); pvif->put(*changed, DBE_VALUE|DBE_ALARM|DBE_PROPERTY, NULL); } + //TODO: report unused fields as changed? + changed->clear(); + changed->set(0); requester->getDone(pvd::Status(), shared_from_this(), pvf, changed); } diff --git a/pdbApp/pdbsingle.h b/pdbApp/pdbsingle.h index b8d4715..5830566 100644 --- a/pdbApp/pdbsingle.h +++ b/pdbApp/pdbsingle.h @@ -19,11 +19,7 @@ struct PDBSinglePV : public PDBPV, public std::tr1::enable_shared_from_thischan.swap(chan); - } + const PDBProvider::shared_pointer& prov); virtual ~PDBSinglePV() {} epics::pvAccess::Channel::shared_pointer diff --git a/pdbApp/pvif.cpp b/pdbApp/pvif.cpp index a000776..05162a5 100644 --- a/pdbApp/pvif.cpp +++ b/pdbApp/pvif.cpp @@ -9,6 +9,10 @@ namespace pvd = epics::pvData; DBCH::DBCH(dbChannel *ch) :chan(ch) { + if(dbChannelOpen(chan)) { + dbChannelDelete(chan); + throw std::invalid_argument("Failed to open channel"); + } if(!chan) throw std::invalid_argument("Invalid channel"); } diff --git a/pdbApp/pvif.h b/pdbApp/pvif.h index 8e22635..c053f1b 100644 --- a/pdbApp/pvif.h +++ b/pdbApp/pvif.h @@ -14,7 +14,7 @@ struct DBCH { dbChannel *chan; DBCH() :chan(0) {} - explicit DBCH(dbChannel *ch); + explicit DBCH(dbChannel *ch); // calls dbChannelOpen() explicit DBCH(const std::string& name); explicit DBCH(const char *name); ~DBCH(); diff --git a/testApp/Makefile b/testApp/Makefile index 29b6ecc..6dfb86a 100644 --- a/testApp/Makefile +++ b/testApp/Makefile @@ -36,8 +36,18 @@ TESTPROD_HOST += testpvif testpvif_SRCS += testpvif.cpp testpvif_SRCS += p2pTestIoc_registerRecordDeviceDriver.cpp testpvif_LIBS += pdbcore pvAccess pvData $(EPICS_BASE_IOC_LIBS) +TESTS += testpvif + +TESTPROD_HOST += testpdb +testpdb_SRCS += testpdb.cpp +testpdb_SRCS += p2pTestIoc_registerRecordDeviceDriver.cpp +testpdb_LIBS += testutils pdbcore pvAccess pvData $(EPICS_BASE_IOC_LIBS) +TESTS += testpdb + + TESTSCRIPTS_HOST += $(TESTS:%=%.t) + #=========================== include $(TOP)/configure/RULES diff --git a/testApp/testpdb.cpp b/testApp/testpdb.cpp new file mode 100644 index 0000000..b24f437 --- /dev/null +++ b/testApp/testpdb.cpp @@ -0,0 +1,109 @@ + +#include + +#include + +#include + +#include "utilities.h" +#include "pdb.h" + +namespace pvd = epics::pvData; +namespace pva = epics::pvAccess; + +namespace { + +template +void testFieldEqual(const pvd::PVStructurePtr& val, const char *name, typename PVD::value_type expect) +{ + typename PVD::shared_pointer fval(val->getSubField(name)); + if(!fval) { + testFail("field '%s' with type %s does not exist", name, typeid(PVD).name()); + } else { + typename PVD::value_type actual(fval->get()); + std::cout<<"# expect='"<createFieldBuilder() + ->addNestedStructure("record") + ->addNestedStructure("_options") + ->add("atomic", pvd::pvBoolean) + ->endNested() + ->endNested() + ->createStructure()); + pvd::PVStructurePtr pvr(pvd::getPVDataCreate()->createPVStructure(def)); + pvr->getSubFieldT("record._options.atomic")->put(atomic); + + + TestChannelRequester::shared_pointer req(new TestChannelRequester()); + pva::Channel::shared_pointer chan(prov->createChannel(name, req)); + + testOk1(!!chan); + testOk1(chan && chan->isConnected()); + testOk1(req->laststate == pva::Channel::CONNECTED); + testOk1(req->status.isOK()); + if(!chan || !chan->isConnected()) + testAbort("'%s' channel not connected", name); + + TestChannelGetRequester::shared_pointer greq(new TestChannelGetRequester()); + pva::ChannelGet::shared_pointer get(chan->createChannelGet(greq, pvr)); + + testOk1(!!get); + testOk1(greq->connected); + testOk1(!greq->done); + testOk1(greq->statusDone.isOK()); + if(!greq || !greq->connected) + testAbort("'%s channelGet not connected", name); + + get->get(); + + testOk1(greq->done); + testOk1(greq->statusDone.isOK()); + testOk1(!!greq->value); + if(!greq->value) + testAbort("'%s' get w/o data", name); + return greq->value; +} + +void testSingle(const PDBProvider::shared_pointer& prov) +{ + pvd::PVStructurePtr value; + + value = pvget(prov, "rec1", false); + testFieldEqual(value, "value", 1.0); + + value = pvget(prov, "rec1.RVAL", false); + testFieldEqual(value, "value", 10); +} + +} // namespace + +extern "C" +void p2pTestIoc_registerRecordDeviceDriver(struct dbBase *); + +MAIN(testpdb) +{ + testPlan(0); + try{ + TestIOC IOC; + + testdbReadDatabase("p2pTestIoc.dbd", NULL, NULL); + p2pTestIoc_registerRecordDeviceDriver(pdbbase); + testdbReadDatabase("testpdb.db", NULL, NULL); + + IOC.init(); + + PDBProvider::shared_pointer prov(new PDBProvider()); + testSingle(prov); + + }catch(std::exception& e){ + PRINT_EXCEPTION(e); + testAbort("Unexpected Exception: %s", e.what()); + } + return testDone(); +} diff --git a/testApp/testpdb.db b/testApp/testpdb.db new file mode 100644 index 0000000..38691e3 --- /dev/null +++ b/testApp/testpdb.db @@ -0,0 +1,18 @@ +record(ai, "rec1") { + field(VAL, "1.0") + field(RVAL, "10") +} +record(ai, "rec2") { + field(VAL, "2.0") + field(RVAL, "20") +} +record(ai, "rec3") { + field(VAL, "3.0") + field(RVAL, "30") + info(pdbGroup, "grp1|fld1=VAL|fld2=RVAL") +} +record(ai, "rec4") { + field(VAL, "4.0") + field(RVAL, "40") + info(pdbGroup, "grp1|fld3=VAL|fld4=RVAL") +} diff --git a/testApp/utilities.cpp b/testApp/utilities.cpp index 5a7b79d..2cd0372 100644 --- a/testApp/utilities.cpp +++ b/testApp/utilities.cpp @@ -64,6 +64,44 @@ bool TestChannelRequester::waitForConnect() } +static size_t countTestChannelGetRequester; + +TestChannelGetRequester::TestChannelGetRequester() + :connected(false) + ,done(false) +{ + epicsAtomicIncrSizeT(&countTestChannelGetRequester); +} + +TestChannelGetRequester::~TestChannelGetRequester() +{ + epicsAtomicDecrSizeT(&countTestChannelGetRequester); +} + +void TestChannelGetRequester::channelGetConnect(const epics::pvData::Status &status, + const epics::pvAccess::ChannelGet::shared_pointer &get, + const epics::pvData::Structure::const_shared_pointer &structure) +{ + if(connected) + testFail("channelGetConnect() called twice"); + statusConnect = status; + channelGet = get; + fielddesc = structure; + connected = true; +} + +void TestChannelGetRequester::getDone(const epics::pvData::Status &status, + const epics::pvAccess::ChannelGet::shared_pointer &get, + const epics::pvData::PVStructure::shared_pointer &pvStructure, + const epics::pvData::BitSet::shared_pointer &bitSet) +{ + statusDone = status; + channelGet = get; + value = pvStructure; + changed = bitSet; + done = true; +} + static size_t countTestChannelMonitorRequester; TestChannelMonitorRequester::TestChannelMonitorRequester() diff --git a/testApp/utilities.h b/testApp/utilities.h index ac9f7f6..4881d60 100644 --- a/testApp/utilities.h +++ b/testApp/utilities.h @@ -89,6 +89,33 @@ struct TestChannelRequester : public epics::pvAccess::ChannelRequester bool waitForConnect(); }; +struct TestChannelGetRequester : public epics::pvAccess::ChannelGetRequester +{ + POINTER_DEFINITIONS(TestChannelGetRequester); + DUMBREQUESTER(TestChannelGetRequester) + + bool connected, done; + epics::pvData::Status statusConnect, statusDone; + epics::pvAccess::ChannelGet::shared_pointer channelGet; + epics::pvData::Structure::const_shared_pointer fielddesc; + epics::pvData::PVStructure::shared_pointer value; + epics::pvData::BitSet::shared_pointer changed; + + TestChannelGetRequester(); + virtual ~TestChannelGetRequester(); + + virtual void channelGetConnect( + const epics::pvData::Status& status, + epics::pvAccess::ChannelGet::shared_pointer const & channelGet, + epics::pvData::Structure::const_shared_pointer const & structure); + + virtual void getDone( + const epics::pvData::Status& status, + epics::pvAccess::ChannelGet::shared_pointer const & channelGet, + epics::pvData::PVStructure::shared_pointer const & pvStructure, + epics::pvData::BitSet::shared_pointer const & bitSet); +}; + struct TestChannelMonitorRequester : public epics::pvData::MonitorRequester { POINTER_DEFINITIONS(TestChannelMonitorRequester); @@ -262,8 +289,7 @@ struct TestIOC { testdbPrepare(); } ~TestIOC() { - if(hasInit) - testIocShutdownOk(); + this->shutdown(); testdbCleanup(); } void init() { @@ -274,6 +300,12 @@ struct TestIOC { hasInit = true; } } + void shutdown() { + if(hasInit) { + testIocShutdownOk(); + hasInit = false; + } + } }; #endif // UTILITIES_H