monitoring test infrastructure
This commit is contained in:
@ -7,15 +7,18 @@ include $(TOP)/configure/CONFIG
|
||||
|
||||
PROD_HOST = p2p
|
||||
|
||||
|
||||
p2p_SRCS += main.cpp
|
||||
p2p_SRCS += server.cpp
|
||||
p2p_SRCS += client.cpp
|
||||
p2p_SRCS += chancache.cpp
|
||||
p2p_SRCS += moncache.cpp
|
||||
p2p_SRCS += channel.cpp
|
||||
p2p_LIBS += p2pcore pvAccess pvData Com
|
||||
|
||||
p2p_LIBS += pvAccess pvData Com
|
||||
LIBRARY_HOST += p2pcore
|
||||
|
||||
p2pcore_SRCS += server.cpp
|
||||
p2pcore_SRCS += client.cpp
|
||||
p2pcore_SRCS += chancache.cpp
|
||||
p2pcore_SRCS += moncache.cpp
|
||||
p2pcore_SRCS += channel.cpp
|
||||
|
||||
p2pcore_LIBS += pvAccess pvData Com
|
||||
|
||||
#===========================
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define WEAKMAP_H
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
@ -9,7 +10,7 @@
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsGuard.h>
|
||||
|
||||
/** @brief An associative map where the value is a weak_ptr to the value is stored.
|
||||
/** @brief An associative map where a weak_ptr to the value is stored.
|
||||
*
|
||||
* Acts like std::map<K, weak_ptr<V> > where entries are automatically
|
||||
* removed when no longer referenced.
|
||||
|
@ -7,11 +7,21 @@ include $(TOP)/configure/CONFIG
|
||||
|
||||
USR_CPPFLAGS = -I$(TOP)/p2pApp
|
||||
|
||||
LIBRARY += testutils
|
||||
|
||||
testutils_SRCS += utilities.cpp
|
||||
testutils_LIBS += pvAccess pvData Com
|
||||
|
||||
TESTPROD_HOST += testweak
|
||||
testweak_SRCS = testweak.cpp
|
||||
testweak_LIBS = Com
|
||||
TESTS += testweak
|
||||
|
||||
TESTPROD_HOST += testtest
|
||||
testtest_SRCS = testtest.cpp
|
||||
testtest_LIBS = testutils p2pcore pvAccess pvData Com
|
||||
TESTS += testtest
|
||||
|
||||
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
|
||||
|
||||
#===========================
|
||||
|
@ -1 +1,66 @@
|
||||
|
||||
#include <epicsGuard.h>
|
||||
#include <epicsUnitTest.h>
|
||||
#include <testMain.h>
|
||||
|
||||
#include <pv/monitor.h>
|
||||
#include <pv/thread.h>
|
||||
|
||||
#include "utilities.h"
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
|
||||
typedef epicsGuard<epicsMutex> Guard;
|
||||
|
||||
namespace {
|
||||
|
||||
struct DownStreamReq : public pvd::MonitorRequester, DumbRequester
|
||||
{
|
||||
pvd::Mutex lock;
|
||||
bool connected;
|
||||
bool unlistend;
|
||||
size_t eventCnt;
|
||||
pvd::Status connectStatus;
|
||||
pvd::MonitorPtr mon;
|
||||
pvd::StructureConstPtr structure;
|
||||
|
||||
DownStreamReq()
|
||||
:DumbRequester("DownStreamReq")
|
||||
,connected(false)
|
||||
,unlistend(false)
|
||||
,eventCnt(0)
|
||||
{}
|
||||
|
||||
virtual void monitorConnect(pvd::Status const & status,
|
||||
pvd::MonitorPtr const & monitor, pvd::StructureConstPtr const & structure)
|
||||
{
|
||||
Guard G(lock);
|
||||
assert(!connected);
|
||||
connectStatus = status;
|
||||
mon = monitor;
|
||||
this->structure = structure;
|
||||
connected = true;
|
||||
}
|
||||
|
||||
virtual void monitorEvent(MonitorPtr const & monitor)
|
||||
{
|
||||
Guard G(lock);
|
||||
mon = monitor;
|
||||
eventCnt++;
|
||||
}
|
||||
|
||||
virtual void unlisten(MonitorPtr const & monitor)
|
||||
{
|
||||
Guard G(lock);
|
||||
assert(!unlistend);
|
||||
unlistend = true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
MAIN(testmon)
|
||||
{
|
||||
testPlan(0);
|
||||
return testDone();
|
||||
}
|
||||
|
176
testApp/testtest.cpp
Normal file
176
testApp/testtest.cpp
Normal file
@ -0,0 +1,176 @@
|
||||
// Test the testing utilities
|
||||
|
||||
#include <epicsGuard.h>
|
||||
#include <epicsUnitTest.h>
|
||||
#include <testMain.h>
|
||||
|
||||
#include <pv/monitor.h>
|
||||
#include <pv/thread.h>
|
||||
|
||||
#include "utilities.h"
|
||||
|
||||
typedef epicsGuard<epicsMutex> Guard;
|
||||
typedef epicsGuardRelease<epicsMutex> UnGuard;
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
namespace pva = epics::pvAccess;
|
||||
|
||||
namespace {
|
||||
template<typename T>
|
||||
struct ScalarAccessor {
|
||||
pvd::PVScalar::shared_pointer field;
|
||||
typedef T value_type;
|
||||
ScalarAccessor(const pvd::PVStructurePtr& s, const char *name)
|
||||
:field(s->getSubFieldT<pvd::PVScalar>(name))
|
||||
{}
|
||||
operator value_type() {
|
||||
return field->getAs<T>();
|
||||
}
|
||||
ScalarAccessor& operator=(T v) {
|
||||
field->putFrom<T>(v);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
void testmonitor()
|
||||
{
|
||||
testDiag("Test Test* monitoring");
|
||||
|
||||
pvd::FieldCreatePtr fieldCreate(pvd::getFieldCreate());
|
||||
pvd::PVDataCreatePtr create(pvd::getPVDataCreate());
|
||||
|
||||
testDiag("Setup TestProvider with \"test\"");
|
||||
TestProvider::shared_pointer prov(new TestProvider);
|
||||
TestPV::shared_pointer pv(prov->addPV("test",
|
||||
fieldCreate->createFieldBuilder()
|
||||
->add("x", pvd::pvInt)
|
||||
->add("y", pvd::pvInt)
|
||||
->createStructure()));
|
||||
|
||||
ScalarAccessor<pvd::int32> x(pv->value, "x");
|
||||
|
||||
x = 42;
|
||||
|
||||
testDiag("Create channel");
|
||||
TestChannelRequester::shared_pointer creq(new TestChannelRequester);
|
||||
pva::Channel::shared_pointer chan(prov->createChannel("test", creq));
|
||||
|
||||
testOk1(!!chan.get());
|
||||
testOk1(creq->waitForConnect());
|
||||
testOk1(chan==creq->chan);
|
||||
if(!chan)
|
||||
testAbort("Create channel failed");
|
||||
|
||||
testDiag("Create monitor");
|
||||
TestChannelMonitorRequester::shared_pointer mreq(new TestChannelMonitorRequester);
|
||||
pvd::Monitor::shared_pointer mon;
|
||||
{
|
||||
pvd::PVStructurePtr pvreq(create->createPVStructure(fieldCreate->createFieldBuilder()
|
||||
->add("junk", pvd::pvInt)
|
||||
->createStructure()));
|
||||
mon = chan->createMonitor(mreq, pvreq);
|
||||
}
|
||||
|
||||
testOk1(!!mon.get());
|
||||
testOk1(mreq->connectStatus.isSuccess());
|
||||
testOk1(!!mreq->dtype.get());
|
||||
testOk1(mreq->eventCnt==0);
|
||||
|
||||
if(!mon)
|
||||
testAbort("Create monitor failed");
|
||||
|
||||
testDiag("ensure queue is initially empty");
|
||||
testOk1(!mon->poll());
|
||||
|
||||
testDiag("Start monitor and check initial update");
|
||||
testOk1(mon->start().isSuccess());
|
||||
|
||||
pvd::MonitorElementPtr elem(mon->poll());
|
||||
testOk1(!!elem.get());
|
||||
|
||||
testOk1(elem && elem->pvStructurePtr->getSubFieldT<pvd::PVInt>("x")->get()==42);
|
||||
testOk1(elem && elem->changedBitSet->nextSetBit(0)==0); // initial update shows all changed
|
||||
testOk1(elem && elem->changedBitSet->nextSetBit(1)==-1);
|
||||
testOk1(elem && elem->overrunBitSet->isEmpty());
|
||||
if(elem) mon->release(elem);
|
||||
|
||||
testDiag("ensure start() queues only one");
|
||||
testOk1(!mon->poll());
|
||||
|
||||
testDiag("Change a field");
|
||||
x = 43;
|
||||
pvd::BitSet changed;
|
||||
changed.set(1);
|
||||
pv->post(changed);
|
||||
|
||||
testOk1(mreq->eventCnt==1);
|
||||
|
||||
elem = mon->poll();
|
||||
testOk1(!!elem.get());
|
||||
|
||||
testOk1(elem && elem->pvStructurePtr->getSubFieldT<pvd::PVInt>("x")->get()==43);
|
||||
testOk1(elem && elem->changedBitSet->nextSetBit(0)==1);
|
||||
testOk1(elem && elem->changedBitSet->nextSetBit(2)==-1);
|
||||
testOk1(elem && elem->overrunBitSet->isEmpty());
|
||||
if(elem) mon->release(elem);
|
||||
|
||||
testDiag("ensure queues are empty");
|
||||
testOk1(!mon->poll());
|
||||
|
||||
testDiag("overflow queue");
|
||||
x = 44;
|
||||
pv->post(changed, false);
|
||||
x = 45;
|
||||
pv->post(changed, false);
|
||||
x = 46;
|
||||
pv->post(changed, false);
|
||||
|
||||
testOk1(mreq->eventCnt==1);
|
||||
|
||||
x = 47;
|
||||
pv->post(changed);
|
||||
|
||||
testOk1(mreq->eventCnt==2);
|
||||
|
||||
elem = mon->poll();
|
||||
testOk1(!!elem.get());
|
||||
|
||||
testOk1(elem && elem->pvStructurePtr->getSubFieldT<pvd::PVInt>("x")->get()==44);
|
||||
testOk1(elem && elem->changedBitSet->nextSetBit(0)==1);
|
||||
testOk1(elem && elem->overrunBitSet->isEmpty());
|
||||
if(elem) mon->release(elem); // overflow element is queued here
|
||||
|
||||
elem = mon->poll();
|
||||
testOk1(!!elem.get());
|
||||
|
||||
testOk1(elem && elem->pvStructurePtr->getSubFieldT<pvd::PVInt>("x")->get()==45);
|
||||
testOk1(elem && elem->changedBitSet->nextSetBit(0)==1);
|
||||
testOk1(elem && elem->overrunBitSet->isEmpty());
|
||||
if(elem) mon->release(elem);
|
||||
|
||||
elem = mon->poll();
|
||||
testOk1(!!elem.get());
|
||||
|
||||
testOk1(elem && elem->pvStructurePtr->getSubFieldT<pvd::PVInt>("x")->get()==47);
|
||||
testOk1(elem && elem->changedBitSet->nextSetBit(0)==1);
|
||||
testOk1(elem && elem->overrunBitSet->nextSetBit(0)==1);
|
||||
testOk1(elem && elem->overrunBitSet->nextSetBit(2)==-1);
|
||||
if(elem) mon->release(elem);
|
||||
|
||||
testDiag("ensure queues are empty");
|
||||
testOk1(!mon->poll());
|
||||
|
||||
testOk1(mreq->eventCnt==2);
|
||||
|
||||
mon->destroy();
|
||||
|
||||
chan->destroy();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
MAIN(testtest)
|
||||
{
|
||||
testPlan(0);
|
||||
testmonitor();
|
||||
return testDone();
|
||||
}
|
479
testApp/utilities.cpp
Normal file
479
testApp/utilities.cpp
Normal file
@ -0,0 +1,479 @@
|
||||
|
||||
#include <utilities.h>
|
||||
#include <helper.h>
|
||||
|
||||
typedef epicsGuard<epicsMutex> Guard;
|
||||
typedef epicsGuardRelease<epicsMutex> UnGuard;
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
namespace pva = epics::pvAccess;
|
||||
|
||||
TestChannelRequester::TestChannelRequester()
|
||||
:laststate(pva::Channel::NEVER_CONNECTED)
|
||||
{}
|
||||
|
||||
void TestChannelRequester::channelCreated(const pvd::Status& status, pva::Channel::shared_pointer const & channel)
|
||||
{
|
||||
Guard G(lock);
|
||||
laststate = pva::Channel::CONNECTED;
|
||||
this->status = status;
|
||||
chan = channel;
|
||||
wait.trigger();
|
||||
}
|
||||
|
||||
void TestChannelRequester::channelStateChange(pva::Channel::shared_pointer const & channel,
|
||||
pva::Channel::ConnectionState connectionState)
|
||||
{
|
||||
Guard G(lock);
|
||||
laststate = connectionState;
|
||||
wait.trigger();
|
||||
}
|
||||
|
||||
bool TestChannelRequester::waitForConnect()
|
||||
{
|
||||
Guard G(lock);
|
||||
assert(chan);
|
||||
while(true) {
|
||||
pva::Channel::ConnectionState cur = chan->getConnectionState();
|
||||
switch(cur) {
|
||||
case pva::Channel::NEVER_CONNECTED:
|
||||
break;
|
||||
case pva::Channel::CONNECTED:
|
||||
return true;
|
||||
case pva::Channel::DISCONNECTED:
|
||||
case pva::Channel::DESTROYED:
|
||||
return false;
|
||||
}
|
||||
UnGuard U(G);
|
||||
wait.wait();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TestChannelMonitorRequester::TestChannelMonitorRequester()
|
||||
:connected(false)
|
||||
,unlistend(false)
|
||||
,eventCnt(0)
|
||||
{}
|
||||
|
||||
void TestChannelMonitorRequester::monitorConnect(pvd::Status const & status,
|
||||
pvd::MonitorPtr const & monitor,
|
||||
pvd::StructureConstPtr const & structure)
|
||||
{
|
||||
Guard G(lock);
|
||||
connectStatus = status;
|
||||
dtype = structure;
|
||||
connected = true;
|
||||
wait.trigger();
|
||||
}
|
||||
|
||||
void TestChannelMonitorRequester::monitorEvent(pvd::MonitorPtr const & monitor)
|
||||
{
|
||||
mon = monitor;
|
||||
eventCnt++;
|
||||
wait.trigger();
|
||||
}
|
||||
|
||||
void TestChannelMonitorRequester::unlisten(pvd::MonitorPtr const & monitor)
|
||||
{
|
||||
Guard G(lock);
|
||||
unlistend = true;
|
||||
wait.trigger();
|
||||
}
|
||||
|
||||
bool TestChannelMonitorRequester::waitForEvent()
|
||||
{
|
||||
Guard G(lock);
|
||||
size_t icnt = eventCnt;
|
||||
while(!unlistend && eventCnt==icnt) {
|
||||
UnGuard U(G);
|
||||
wait.wait();
|
||||
}
|
||||
return !unlistend;
|
||||
}
|
||||
|
||||
TestPVChannel::TestPVChannel(const std::tr1::shared_ptr<TestPV> &pv,
|
||||
const std::tr1::shared_ptr<pva::ChannelRequester> &req)
|
||||
:pv(pv)
|
||||
,requester(req)
|
||||
,state(CONNECTED)
|
||||
{}
|
||||
|
||||
TestPVChannel::~TestPVChannel()
|
||||
{
|
||||
Guard G(pv->provider->lock);
|
||||
if(requester)
|
||||
testDiag("Warning: TestPVChannel dropped w/o destroy()");
|
||||
}
|
||||
|
||||
void TestPVChannel::destroy()
|
||||
{
|
||||
std::tr1::shared_ptr<TestProvider> P(pv->provider);
|
||||
Guard G(P->lock);
|
||||
requester.reset();
|
||||
state = DESTROYED;
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<pva::ChannelProvider>
|
||||
TestPVChannel::getProvider()
|
||||
{ return pv->provider; }
|
||||
|
||||
TestPVChannel::ConnectionState TestPVChannel::getConnectionState()
|
||||
{
|
||||
Guard G(pv->provider->lock);
|
||||
return state;
|
||||
}
|
||||
|
||||
std::string TestPVChannel::getChannelName()
|
||||
{ return pv->name; }
|
||||
|
||||
bool TestPVChannel::isConnected()
|
||||
{
|
||||
return getConnectionState()==CONNECTED;
|
||||
}
|
||||
|
||||
void TestPVChannel::getField(pva::GetFieldRequester::shared_pointer const & requester,std::string const & subField)
|
||||
{
|
||||
Guard G(pv->provider->lock);
|
||||
|
||||
//TODO subField?
|
||||
requester->getDone(pvd::Status(), pv->dtype);
|
||||
}
|
||||
|
||||
pva::ChannelProcess::shared_pointer
|
||||
TestPVChannel::createChannelProcess(
|
||||
pva::ChannelProcessRequester::shared_pointer const & requester,
|
||||
pvd::PVStructure::shared_pointer const & pvRequest)
|
||||
{
|
||||
pva::ChannelProcess::shared_pointer ret;
|
||||
requester->channelProcessConnect(pvd::Status(pvd::Status::STATUSTYPE_FATAL, "Not implemented"), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pva::ChannelGet::shared_pointer
|
||||
TestPVChannel::createChannelGet(
|
||||
pva::ChannelGetRequester::shared_pointer const & requester,
|
||||
pvd::PVStructure::shared_pointer const & pvRequest)
|
||||
{
|
||||
pva::ChannelGet::shared_pointer ret;
|
||||
requester->channelGetConnect(pvd::Status(pvd::Status::STATUSTYPE_FATAL, "Not implemented"),
|
||||
ret,
|
||||
pvd::StructureConstPtr());
|
||||
return ret;
|
||||
}
|
||||
|
||||
pva::ChannelPut::shared_pointer
|
||||
TestPVChannel::createChannelPut(
|
||||
pva::ChannelPutRequester::shared_pointer const & requester,
|
||||
pvd::PVStructure::shared_pointer const & pvRequest)
|
||||
{
|
||||
pva::ChannelPut::shared_pointer ret;
|
||||
requester->channelPutConnect(pvd::Status(pvd::Status::STATUSTYPE_FATAL, "Not implemented"),
|
||||
ret,
|
||||
pvd::StructureConstPtr());
|
||||
return ret;
|
||||
}
|
||||
|
||||
pva::ChannelPutGet::shared_pointer
|
||||
TestPVChannel::createChannelPutGet(
|
||||
pva::ChannelPutGetRequester::shared_pointer const & requester,
|
||||
pvd::PVStructure::shared_pointer const & pvRequest)
|
||||
{
|
||||
pva::ChannelPutGet::shared_pointer ret;
|
||||
requester->channelPutGetConnect(pvd::Status(pvd::Status::STATUSTYPE_FATAL, "Not implemented"),
|
||||
ret,
|
||||
pvd::StructureConstPtr(),
|
||||
pvd::StructureConstPtr());
|
||||
return ret;
|
||||
}
|
||||
|
||||
pva::ChannelRPC::shared_pointer
|
||||
TestPVChannel::createChannelRPC(
|
||||
pva::ChannelRPCRequester::shared_pointer const & requester,
|
||||
pvd::PVStructure::shared_pointer const & pvRequest)
|
||||
{
|
||||
pva::ChannelRPC::shared_pointer ret;
|
||||
requester->channelRPCConnect(pvd::Status(pvd::Status::STATUSTYPE_FATAL, "Not implemented"),
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pvd::Monitor::shared_pointer
|
||||
TestPVChannel::createMonitor(
|
||||
pvd::MonitorRequester::shared_pointer const & requester,
|
||||
pvd::PVStructure::shared_pointer const & pvRequest)
|
||||
{
|
||||
shared_pointer self(weakself);
|
||||
TestPVMonitor::shared_pointer ret(new TestPVMonitor(self, requester, 2));
|
||||
{
|
||||
Guard G(pv->provider->lock);
|
||||
monitors.insert(ret);
|
||||
static_cast<TestPVMonitor*>(ret.get())->weakself = ret; // save wrapped weak ref
|
||||
}
|
||||
requester->monitorConnect(pvd::Status(), ret, pv->dtype);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pva::ChannelArray::shared_pointer
|
||||
TestPVChannel::createChannelArray(
|
||||
pva::ChannelArrayRequester::shared_pointer const & requester,
|
||||
pvd::PVStructure::shared_pointer const & pvRequest)
|
||||
{
|
||||
pva::ChannelArray::shared_pointer ret;
|
||||
requester->channelArrayConnect(pvd::Status(pvd::Status::STATUSTYPE_FATAL, "Not implemented"),
|
||||
ret,
|
||||
pvd::Array::const_shared_pointer());
|
||||
return ret;
|
||||
}
|
||||
|
||||
TestPVMonitor::TestPVMonitor(const TestPVChannel::shared_pointer& ch,
|
||||
const pvd::MonitorRequester::shared_pointer& req,
|
||||
size_t bsize)
|
||||
:channel(ch)
|
||||
,requester(req)
|
||||
,running(false)
|
||||
,finalize(false)
|
||||
,inoverflow(false)
|
||||
,needWakeup(false)
|
||||
{
|
||||
pvd::PVDataCreatePtr fact(pvd::PVDataCreate::getPVDataCreate());
|
||||
for(size_t i=0; i<bsize; i++) {
|
||||
pvd::MonitorElementPtr elem(new pvd::MonitorElement(fact->createPVStructure(channel->pv->dtype)));
|
||||
free.push_back(elem);
|
||||
}
|
||||
}
|
||||
|
||||
TestPVMonitor::~TestPVMonitor()
|
||||
{
|
||||
Guard G(channel->pv->provider->lock);
|
||||
if(requester)
|
||||
testDiag("Warning: TestPVMonitor dropped w/o destroy()");
|
||||
}
|
||||
|
||||
void TestPVMonitor::destroy()
|
||||
{
|
||||
Guard G(channel->pv->provider->lock);
|
||||
requester.reset();
|
||||
|
||||
shared_pointer self(weakself);
|
||||
channel->monitors.erase(self); // ensure we don't get more notifications
|
||||
}
|
||||
|
||||
pvd::Status TestPVMonitor::start()
|
||||
{
|
||||
bool wake;
|
||||
{
|
||||
Guard G(channel->pv->provider->lock);
|
||||
if(finalize && buffer.empty())
|
||||
return pvd::Status();
|
||||
|
||||
if(!running) {
|
||||
wake = running = needWakeup = true;
|
||||
}
|
||||
|
||||
if(!this->free.empty()) {
|
||||
pvd::MonitorElementPtr monitorElement(this->free.front());
|
||||
|
||||
if(changedMask.isEmpty()) {
|
||||
changedMask.set(0); // initial update has all changed
|
||||
overflowMask.clear();
|
||||
}
|
||||
|
||||
monitorElement->pvStructurePtr->copyUnchecked(*channel->pv->value);
|
||||
*monitorElement->changedBitSet = changedMask;
|
||||
*monitorElement->overrunBitSet = overflowMask;
|
||||
changedMask.clear();
|
||||
overflowMask.clear();
|
||||
|
||||
buffer.push_back(monitorElement);
|
||||
this->free.pop_front();
|
||||
}
|
||||
}
|
||||
(void)wake; // todo notify?
|
||||
return pvd::Status();
|
||||
}
|
||||
|
||||
pvd::Status TestPVMonitor::stop()
|
||||
{
|
||||
Guard G(channel->pv->provider->lock);
|
||||
running = false;
|
||||
return pvd::Status();
|
||||
}
|
||||
|
||||
pvd::MonitorElementPtr TestPVMonitor::poll()
|
||||
{
|
||||
pvd::MonitorElementPtr ret;
|
||||
Guard G(channel->pv->provider->lock);
|
||||
if(!buffer.empty()) {
|
||||
ret = buffer.front();
|
||||
buffer.pop_front();
|
||||
}
|
||||
testDiag("pop %p", ret.get());
|
||||
return ret;
|
||||
}
|
||||
|
||||
void TestPVMonitor::release(pvd::MonitorElementPtr const & monitorElement)
|
||||
{
|
||||
Guard G(channel->pv->provider->lock);
|
||||
testDiag("release %p", monitorElement.get());
|
||||
|
||||
if(inoverflow) {
|
||||
assert(!buffer.empty());
|
||||
assert(this->free.empty());
|
||||
|
||||
monitorElement->pvStructurePtr->copyUnchecked(*channel->pv->value);
|
||||
*monitorElement->changedBitSet = changedMask;
|
||||
*monitorElement->overrunBitSet = overflowMask;
|
||||
|
||||
changedMask.clear();
|
||||
overflowMask.clear();
|
||||
|
||||
buffer.push_back(monitorElement);
|
||||
testDiag("overflow resume %p", monitorElement.get());
|
||||
inoverflow = false;
|
||||
} else {
|
||||
this->free.push_back(monitorElement);
|
||||
}
|
||||
}
|
||||
|
||||
TestPV::TestPV(const std::string& name,
|
||||
const std::tr1::shared_ptr<TestProvider>& provider,
|
||||
const pvd::StructureConstPtr& dtype)
|
||||
:name(name)
|
||||
,provider(provider)
|
||||
,factory(pvd::PVDataCreate::getPVDataCreate())
|
||||
,dtype(dtype)
|
||||
,value(factory->createPVStructure(dtype))
|
||||
{}
|
||||
|
||||
void TestPV::post(const pvd::BitSet& changed, bool notify)
|
||||
{
|
||||
Guard G(provider->lock);
|
||||
channels_t::vector_type toupdate(channels.lock_vector());
|
||||
|
||||
FOREACH(it, end, toupdate) // channel
|
||||
{
|
||||
TestPVChannel *chan = it->get();
|
||||
|
||||
TestPVChannel::monitors_t::vector_type tomon(chan->monitors.lock_vector());
|
||||
FOREACH(it2, end2, tomon) // monitor/subscription
|
||||
{
|
||||
TestPVMonitor *mon = it2->get();
|
||||
|
||||
if(mon->inoverflow || mon->free.empty()) {
|
||||
mon->inoverflow = true;
|
||||
mon->overflowMask.or_and(mon->changedMask, changed); // oflow = prev_changed & new_changed
|
||||
mon->changedMask |= changed;
|
||||
testDiag("overflow");
|
||||
|
||||
} else {
|
||||
|
||||
if(!mon->needWakeup)
|
||||
mon->needWakeup = mon->buffer.empty();
|
||||
|
||||
AUTO_REF(elem, mon->free.front());
|
||||
elem->pvStructurePtr->copyUnchecked(*value);
|
||||
*elem->changedBitSet = changed;
|
||||
elem->overrunBitSet->clear(); // redundant/paranoia
|
||||
|
||||
mon->buffer.push_back(elem);
|
||||
mon->free.pop_front();
|
||||
testDiag("push %p", elem.get());
|
||||
}
|
||||
|
||||
if(mon->needWakeup && notify) {
|
||||
UnGuard U(G);
|
||||
mon->requester->monitorEvent(*it2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TestPV::disconnect()
|
||||
{
|
||||
Guard G(provider->lock);
|
||||
channels_t::vector_type toupdate(channels.lock_vector());
|
||||
|
||||
FOREACH(it, end, toupdate) // channel
|
||||
{
|
||||
TestPVChannel *chan = it->get();
|
||||
|
||||
chan->state = TestPVChannel::DISCONNECTED;
|
||||
{
|
||||
UnGuard U(G);
|
||||
chan->requester->channelStateChange(*it, TestPVChannel::DISCONNECTED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TestProvider::TestProvider() {}
|
||||
|
||||
void TestProvider::destroy()
|
||||
{
|
||||
// TODO: disconnect all?
|
||||
}
|
||||
|
||||
pva::ChannelFind::shared_pointer
|
||||
TestProvider::channelList(pva::ChannelListRequester::shared_pointer const & requester)
|
||||
{
|
||||
pva::ChannelFind::shared_pointer ret;
|
||||
pvd::PVStringArray::const_svector names;
|
||||
requester->channelListResult(pvd::Status(pvd::Status::STATUSTYPE_FATAL, "Not implemented"),
|
||||
ret,
|
||||
names,
|
||||
true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pva::ChannelFind::shared_pointer
|
||||
TestProvider::channelFind(std::string const & channelName,
|
||||
pva::ChannelFindRequester::shared_pointer const & requester)
|
||||
{
|
||||
pva::ChannelFind::shared_pointer ret;
|
||||
requester->channelFindResult(pvd::Status(pvd::Status::STATUSTYPE_FATAL, "Not implemented"),
|
||||
ret, false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pva::Channel::shared_pointer
|
||||
TestProvider::createChannel(std::string const & channelName,pva::ChannelRequester::shared_pointer const & requester,
|
||||
short priority)
|
||||
{
|
||||
return createChannel(channelName, requester, priority, "<unused>");
|
||||
}
|
||||
|
||||
pva::Channel::shared_pointer
|
||||
TestProvider::createChannel(std::string const & channelName,
|
||||
pva::ChannelRequester::shared_pointer const & requester,
|
||||
short priority, std::string const & address)
|
||||
{
|
||||
pva::Channel::shared_pointer ret;
|
||||
|
||||
{
|
||||
Guard G(lock);
|
||||
|
||||
TestPV::shared_pointer pv(pvs.find(channelName));
|
||||
if(pv) {
|
||||
TestPVChannel::shared_pointer chan(new TestPVChannel(pv, requester));
|
||||
pv->channels.insert(chan);
|
||||
chan->weakself = chan;
|
||||
ret = chan;
|
||||
}
|
||||
}
|
||||
|
||||
if(ret) {
|
||||
requester->channelCreated(pvd::Status(), ret);
|
||||
} else {
|
||||
requester->channelCreated(pvd::Status(pvd::Status::STATUSTYPE_ERROR, "PV not found"), ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
TestPV::shared_pointer
|
||||
TestProvider::addPV(const std::string& name, const pvd::StructureConstPtr& tdef)
|
||||
{
|
||||
Guard G(lock);
|
||||
TestPV::shared_pointer ret(new TestPV(name, shared_from_this(), tdef));
|
||||
pvs.insert(name, ret);
|
||||
return ret;
|
||||
}
|
205
testApp/utilities.h
Normal file
205
testApp/utilities.h
Normal file
@ -0,0 +1,205 @@
|
||||
#ifndef UTILITIES_H
|
||||
#define UTILITIES_H
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include <epicsEvent.h>
|
||||
#include <epicsUnitTest.h>
|
||||
|
||||
#include <pv/pvAccess.h>
|
||||
|
||||
#include "weakmap.h"
|
||||
#include "weakset.h"
|
||||
|
||||
struct TestPV;
|
||||
struct TestPVChannel;
|
||||
struct TestPVMonitor;
|
||||
struct TestProvider;
|
||||
|
||||
// minimally useful boilerplate which must appear *everywhere*
|
||||
#define DUMBREQUESTER(NAME) \
|
||||
virtual std::string getRequesterName() { return #NAME; } \
|
||||
virtual void message(std::string const & message,epics::pvData::MessageType messageType) { \
|
||||
testDiag("%s : " #NAME "(%p) : %s", epics::pvData::getMessageTypeName(messageType).c_str(), this, message.c_str()); \
|
||||
}
|
||||
|
||||
struct TestChannelRequester : public epics::pvAccess::ChannelRequester
|
||||
{
|
||||
POINTER_DEFINITIONS(TestChannelRequester);
|
||||
DUMBREQUESTER(TestChannelRequester)
|
||||
|
||||
epicsMutex lock;
|
||||
epicsEvent wait;
|
||||
epics::pvAccess::Channel::shared_pointer chan;
|
||||
epics::pvData::Status status;
|
||||
epics::pvAccess::Channel::ConnectionState laststate;
|
||||
TestChannelRequester();
|
||||
virtual ~TestChannelRequester() {}
|
||||
virtual void channelCreated(const epics::pvData::Status& status, epics::pvAccess::Channel::shared_pointer const & channel);
|
||||
virtual void channelStateChange(epics::pvAccess::Channel::shared_pointer const & channel, epics::pvAccess::Channel::ConnectionState connectionState);
|
||||
|
||||
bool waitForConnect();
|
||||
};
|
||||
|
||||
struct TestChannelMonitorRequester : public epics::pvData::MonitorRequester
|
||||
{
|
||||
POINTER_DEFINITIONS(TestChannelMonitorRequester);
|
||||
DUMBREQUESTER(TestChannelMonitorRequester)
|
||||
|
||||
epicsMutex lock;
|
||||
epicsEvent wait;
|
||||
bool connected;
|
||||
bool unlistend;
|
||||
size_t eventCnt;
|
||||
epics::pvData::Status connectStatus;
|
||||
epics::pvData::MonitorPtr mon;
|
||||
epics::pvData::StructureConstPtr dtype;
|
||||
|
||||
TestChannelMonitorRequester();
|
||||
virtual ~TestChannelMonitorRequester() {}
|
||||
|
||||
virtual void monitorConnect(epics::pvData::Status const & status,
|
||||
epics::pvData::MonitorPtr const & monitor,
|
||||
epics::pvData::StructureConstPtr const & structure);
|
||||
virtual void monitorEvent(epics::pvData::MonitorPtr const & monitor);
|
||||
virtual void unlisten(epics::pvData::MonitorPtr const & monitor);
|
||||
|
||||
bool waitForEvent();
|
||||
};
|
||||
|
||||
struct TestPVChannel : public epics::pvAccess::Channel
|
||||
{
|
||||
POINTER_DEFINITIONS(TestPVChannel);
|
||||
DUMBREQUESTER(TestPVChannel)
|
||||
std::tr1::weak_ptr<TestPVChannel> weakself;
|
||||
|
||||
const std::tr1::shared_ptr<TestPV> pv;
|
||||
std::tr1::shared_ptr<epics::pvAccess::ChannelRequester> requester;
|
||||
ConnectionState state;
|
||||
|
||||
typedef weak_set<TestPVMonitor> monitors_t;
|
||||
monitors_t monitors;
|
||||
|
||||
TestPVChannel(const std::tr1::shared_ptr<TestPV>& pv,
|
||||
const std::tr1::shared_ptr<epics::pvAccess::ChannelRequester>& req);
|
||||
virtual ~TestPVChannel();
|
||||
|
||||
virtual void destroy();
|
||||
|
||||
virtual std::tr1::shared_ptr<epics::pvAccess::ChannelProvider> getProvider();
|
||||
virtual std::string getRemoteAddress() { return "localhost:1234"; }
|
||||
virtual ConnectionState getConnectionState();
|
||||
virtual std::string getChannelName();
|
||||
virtual std::tr1::shared_ptr<epics::pvAccess::ChannelRequester> getChannelRequester()
|
||||
{ return std::tr1::shared_ptr<epics::pvAccess::ChannelRequester>(requester); }
|
||||
virtual bool isConnected();
|
||||
virtual void getField(epics::pvAccess::GetFieldRequester::shared_pointer const & requester,std::string const & subField);
|
||||
virtual epics::pvAccess::AccessRights getAccessRights(epics::pvData::PVField::shared_pointer const & pvField)
|
||||
{ return epics::pvAccess::readWrite; }
|
||||
|
||||
virtual epics::pvAccess::ChannelProcess::shared_pointer createChannelProcess(
|
||||
epics::pvAccess::ChannelProcessRequester::shared_pointer const & channelProcessRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
virtual epics::pvAccess::ChannelGet::shared_pointer createChannelGet(
|
||||
epics::pvAccess::ChannelGetRequester::shared_pointer const & channelGetRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
virtual epics::pvAccess::ChannelPut::shared_pointer createChannelPut(
|
||||
epics::pvAccess::ChannelPutRequester::shared_pointer const & channelPutRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
virtual epics::pvAccess::ChannelPutGet::shared_pointer createChannelPutGet(
|
||||
epics::pvAccess::ChannelPutGetRequester::shared_pointer const & channelPutGetRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
virtual epics::pvAccess::ChannelRPC::shared_pointer createChannelRPC(
|
||||
epics::pvAccess::ChannelRPCRequester::shared_pointer const & channelRPCRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
virtual epics::pvData::Monitor::shared_pointer createMonitor(
|
||||
epics::pvData::MonitorRequester::shared_pointer const & monitorRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
virtual epics::pvAccess::ChannelArray::shared_pointer createChannelArray(
|
||||
epics::pvAccess::ChannelArrayRequester::shared_pointer const & channelArrayRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
|
||||
virtual void printInfo() {}
|
||||
virtual void printInfo(std::ostream& out) {}
|
||||
};
|
||||
|
||||
struct TestPVMonitor : public epics::pvData::Monitor
|
||||
{
|
||||
POINTER_DEFINITIONS(TestPVMonitor);
|
||||
std::tr1::weak_ptr<TestPVMonitor> weakself;
|
||||
|
||||
TestPVChannel::shared_pointer channel;
|
||||
epics::pvData::MonitorRequester::shared_pointer requester;
|
||||
|
||||
bool running;
|
||||
bool finalize;
|
||||
bool inoverflow;
|
||||
bool needWakeup;
|
||||
|
||||
TestPVMonitor(const TestPVChannel::shared_pointer& ch,
|
||||
const epics::pvData::MonitorRequester::shared_pointer& req,
|
||||
size_t bsize);
|
||||
virtual ~TestPVMonitor();
|
||||
|
||||
virtual void destroy();
|
||||
|
||||
virtual epics::pvData::Status start();
|
||||
virtual epics::pvData::Status stop();
|
||||
virtual epics::pvData::MonitorElementPtr poll();
|
||||
virtual void release(epics::pvData::MonitorElementPtr const & monitorElement);
|
||||
|
||||
std::deque<epics::pvData::MonitorElementPtr> buffer, free;
|
||||
epics::pvData::BitSet changedMask, overflowMask;
|
||||
};
|
||||
|
||||
struct TestPV
|
||||
{
|
||||
POINTER_DEFINITIONS(TestPV);
|
||||
std::tr1::weak_ptr<TestPV> weakself;
|
||||
|
||||
const std::string name;
|
||||
std::tr1::shared_ptr<TestProvider> const provider;
|
||||
epics::pvData::PVDataCreatePtr factory;
|
||||
const epics::pvData::StructureConstPtr dtype;
|
||||
epics::pvData::PVStructurePtr value;
|
||||
|
||||
TestPV(const std::string& name,
|
||||
const std::tr1::shared_ptr<TestProvider>& provider,
|
||||
const epics::pvData::StructureConstPtr& dtype);
|
||||
|
||||
void post(const epics::pvData::BitSet& changed, bool notify = true);
|
||||
|
||||
void disconnect();
|
||||
|
||||
typedef weak_set<TestPVChannel> channels_t;
|
||||
channels_t channels;
|
||||
friend struct TestProvider;
|
||||
};
|
||||
|
||||
struct TestProvider : public epics::pvAccess::ChannelProvider, std::tr1::enable_shared_from_this<TestProvider>
|
||||
{
|
||||
POINTER_DEFINITIONS(TestProvider);
|
||||
|
||||
virtual std::string getProviderName() { return "TestProvider"; }
|
||||
|
||||
virtual void destroy();
|
||||
|
||||
virtual epics::pvAccess::ChannelFind::shared_pointer channelFind(std::string const & channelName,
|
||||
epics::pvAccess::ChannelFindRequester::shared_pointer const & channelFindRequester);
|
||||
virtual epics::pvAccess::ChannelFind::shared_pointer channelList(epics::pvAccess::ChannelListRequester::shared_pointer const & channelListRequester);
|
||||
virtual epics::pvAccess::Channel::shared_pointer createChannel(std::string const & channelName,epics::pvAccess::ChannelRequester::shared_pointer const & channelRequester,
|
||||
short priority = PRIORITY_DEFAULT);
|
||||
virtual epics::pvAccess::Channel::shared_pointer createChannel(std::string const & channelName, epics::pvAccess::ChannelRequester::shared_pointer const & channelRequester,
|
||||
short priority, std::string const & address);
|
||||
|
||||
TestProvider();
|
||||
virtual ~TestProvider() {}
|
||||
|
||||
TestPV::shared_pointer addPV(const std::string& name, const epics::pvData::StructureConstPtr& tdef);
|
||||
|
||||
epicsMutex lock;
|
||||
typedef weak_value_map<std::string, TestPV> pvs_t;
|
||||
pvs_t pvs;
|
||||
};
|
||||
|
||||
#endif // UTILITIES_H
|
Reference in New Issue
Block a user