diff --git a/testApp/Makefile b/testApp/Makefile index 4d59b61..d23979e 100644 --- a/testApp/Makefile +++ b/testApp/Makefile @@ -10,10 +10,11 @@ USR_CPPFLAGS += -I$(TOP)/src/remoteClient PVACCESS_TEST = $(TOP)/testApp -PROD_LIBS += pvAccess pvData Com +PROD_LIBS += pvAccess pvAccessCA pvData Com include $(PVACCESS_TEST)/utils/Makefile include $(PVACCESS_TEST)/remote/Makefile +include $(PVACCESS_TEST)/ca/Makefile # pvAccessAllTests runs all the test programs in a known working order. testHarness_SRCS += pvAccessAllTests.c diff --git a/testApp/ca/Makefile b/testApp/ca/Makefile new file mode 100644 index 0000000..5c38178 --- /dev/null +++ b/testApp/ca/Makefile @@ -0,0 +1,8 @@ +# This is a Makefile fragment, see ../Makefile + +SRC_DIRS += $(PVACCESS_TEST)/ca + +TESTPROD_HOST += testCaProvider +testCaProvider += testCaProvider.cpp +testHarness_SRCS += testCaProvider.cpp +TESTS += testCaProvider diff --git a/testApp/ca/testCaProvider.cpp b/testApp/ca/testCaProvider.cpp new file mode 100644 index 0000000..38a879c --- /dev/null +++ b/testApp/ca/testCaProvider.cpp @@ -0,0 +1,681 @@ +/* testCaProvider.cpp */ +/* + * Copyright information and license terms for this software can be + * found in the file LICENSE that is included with the distribution + */ +/* Author: Marty Kraimer Date: 2018.05 */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace epics::pvData; +using namespace epics::pvAccess; +using namespace epics::pvAccess::ca; +using std::string; + +class TestChannel; +typedef std::tr1::shared_ptr TestChannelPtr; + +class TestChannel: + public ChannelRequester, + public std::tr1::enable_shared_from_this +{ +public: + POINTER_DEFINITIONS(TestChannel); + string getRequesterName(); + void message( + string const & message, + MessageType messageType); + virtual void channelCreated(const Status& status, Channel::shared_pointer const & channel); + virtual void channelStateChange(Channel::shared_pointer const & channel, Channel::ConnectionState connectionState); + string getChannelName(); + Channel::shared_pointer getChannel(); + static TestChannelPtr create(string const & channelName); + void connect(); + void waitConnect(double timeout); +private: + TestChannel(string const & channelName); + string channelName; + Event waitForConnect; + Channel::shared_pointer channel; +}; + +string TestChannel::getChannelName() { return channelName;} + +Channel::shared_pointer TestChannel::getChannel() { return channel;} + +TestChannelPtr TestChannel::create(string const & channelName) +{ + TestChannelPtr testChannel(new TestChannel(channelName)); + testChannel->connect(); + return testChannel; +} + +TestChannel::TestChannel(string const & channelName) +: channelName(channelName) +{ +} + +string TestChannel::getRequesterName() { return "testChannel";} +void TestChannel::message(string const & message,MessageType messageType) {}; + +void TestChannel::channelCreated(const Status& status, Channel::shared_pointer const & channel) +{ + if(channel->isConnected()) waitForConnect.signal(); +} + +void TestChannel::channelStateChange(Channel::shared_pointer const & channel, Channel::ConnectionState connectionState) +{ + if(connectionState==Channel::CONNECTED) waitForConnect.signal(); +} + +void TestChannel::connect() +{ + ChannelProviderRegistry::shared_pointer reg(ChannelProviderRegistry::clients()); + ChannelProvider::shared_pointer channelProvider(reg->getProvider("ca")); + if(!channelProvider) throw std::runtime_error(channelName + " provider ca not registered"); + channel = channelProvider->createChannel(channelName,shared_from_this(),ChannelProvider::PRIORITY_DEFAULT); + if(!channel) throw std::runtime_error(channelName + " channelCreate failed "); + waitConnect(5.0); +} + +void TestChannel::waitConnect(double timeout) +{ + if(waitForConnect.wait(timeout)) return; + throw std::runtime_error(channelName + " TestChannel::waitConnect failed "); +} + +class TestChannelGet; +typedef std::tr1::shared_ptr TestChannelGetPtr; + +class TestChannelGetRequester; +typedef std::tr1::shared_ptr TestChannelGetRequesterPtr; +typedef std::tr1::weak_ptr TestChannelGetRequesterWPtr; + +class TestChannelGetRequester +{ +public: + virtual void getDone( + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet) = 0; +}; + +class TestChannelGet: + public ChannelGetRequester, + public std::tr1::enable_shared_from_this +{ +public: + POINTER_DEFINITIONS(TestChannelGet); + virtual string getRequesterName(); + virtual void message(string const & message, epics::pvData::MessageType messageType) {} + virtual void channelGetConnect( + const Status& status, + ChannelGet::shared_pointer const & channelGet, + Structure::const_shared_pointer const & structure); + virtual void getDone( + const Status& status, + ChannelGet::shared_pointer const & channelGet, + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet); + static TestChannelGetPtr create( + TestChannelGetRequesterPtr const &getRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest); + void connect(); + void waitConnect(double timeout); + void get(); +private: + TestChannelGet( + TestChannelGetRequesterPtr const &getRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest); + + TestChannelGetRequesterWPtr getRequester; + TestChannelPtr testChannel; + PVStructurePtr pvRequest; + PVStructurePtr pvStructure; + Event waitForConnect; + ChannelGet::shared_pointer channelGet; +}; + +TestChannelGetPtr TestChannelGet::create( + TestChannelGetRequesterPtr const &getRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest) +{ + TestChannelGetPtr testChannelGet(new TestChannelGet(getRequester,testChannel,pvRequest)); + testChannelGet->connect(); + testChannelGet->waitConnect(5.0); + return testChannelGet; +} + +TestChannelGet::TestChannelGet( + TestChannelGetRequesterPtr const &getRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest) + : getRequester(getRequester), + testChannel(testChannel), + pvRequest(pvRequest) +{ +} + +string TestChannelGet::getRequesterName() {return "TestChannelGet";} + +void TestChannelGet::channelGetConnect( + const Status& status, + ChannelGet::shared_pointer const & channelGet, + Structure::const_shared_pointer const & structure) +{ + waitForConnect.signal(); +} + +void TestChannelGet::getDone( + const Status& status, + ChannelGet::shared_pointer const & channelGet, + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet) +{ + TestChannelGetRequesterPtr req(getRequester.lock()); + if(!req) return; + if(status.isOK()) { + req->getDone(pvStructure,bitSet); + return; + } + string message = string("channel ") + + testChannel->getChannelName() + + " TestChannelGet::getDone " + + status.getMessage(); + throw std::runtime_error(message); +} + +void TestChannelGet::connect() +{ + channelGet = testChannel->getChannel()->createChannelGet(shared_from_this(),pvRequest); + if(!channelGet) throw std::runtime_error(testChannel->getChannelName() + " channelCreate failed "); +} + +void TestChannelGet::waitConnect(double timeout) +{ + if(waitForConnect.wait(timeout)) return; + throw std::runtime_error(testChannel->getChannelName() + " TestChannelGet::waitConnect failed "); +} + + +void TestChannelGet::get() +{ + channelGet->get(); +} + +class TestChannelPut; +typedef std::tr1::shared_ptr TestChannelPutPtr; + +class TestChannelPutRequester; +typedef std::tr1::shared_ptr TestChannelPutRequesterPtr; +typedef std::tr1::weak_ptr TestChannelPutRequesterWPtr; + +class TestChannelPutRequester +{ +public: + virtual void putDone() = 0; +}; + +class TestChannelPut: + public ChannelPutRequester, + public std::tr1::enable_shared_from_this +{ +public: + POINTER_DEFINITIONS(TestChannelPut); + virtual string getRequesterName(); + virtual void message(string const & message, MessageType messageType) {} + virtual void channelPutConnect( + const Status& status, + ChannelPut::shared_pointer const & channelPut, + Structure::const_shared_pointer const & structure); + virtual void putDone( + const Status& status, + ChannelPut::shared_pointer const & channelPut); + virtual void getDone( + const Status& status, + ChannelPut::shared_pointer const & channelPut, + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet); + static TestChannelPutPtr create( + TestChannelPutRequesterPtr const &putRequester, + TestChannelPtr const &testChannel); + void connect(); + void waitConnect(double timeout); + void put(string const & value); +private: + TestChannelPut( + TestChannelPutRequesterPtr const &putRequester, + TestChannelPtr const &testChannel); + + TestChannelPutRequesterWPtr putRequester; + TestChannelPtr testChannel; + PVStructurePtr pvStructure; + BitSetPtr bitSet; + Event waitForConnect; + ChannelPut::shared_pointer channelPut; +}; + +TestChannelPutPtr TestChannelPut::create( + TestChannelPutRequesterPtr const &putRequester, + TestChannelPtr const &testChannel) +{ + TestChannelPutPtr testChannelPut(new TestChannelPut(putRequester,testChannel)); + testChannelPut->connect(); + testChannelPut->waitConnect(5.0); + return testChannelPut; +} + +TestChannelPut::TestChannelPut( + TestChannelPutRequesterPtr const &putRequester, + TestChannelPtr const &testChannel) + : putRequester(putRequester), + testChannel(testChannel) +{ +} + +string TestChannelPut::getRequesterName() {return "TestChannelPut";} + +void TestChannelPut::channelPutConnect( + const Status& status, + ChannelPut::shared_pointer const & channelPut, + Structure::const_shared_pointer const & structure) +{ + pvStructure = PVDataCreate::getPVDataCreate()->createPVStructure(structure); + bitSet = BitSetPtr(new BitSet(pvStructure->getNumberFields())); + waitForConnect.signal(); +} + +void TestChannelPut::getDone( + const Status& status, + ChannelPut::shared_pointer const & channelPut, + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet) +{ + throw std::runtime_error("TestChannelPut::getDone should not be called"); +} + +void TestChannelPut::putDone( + const Status& status, + ChannelPut::shared_pointer const & channelPut) +{ + TestChannelPutRequesterPtr req(putRequester.lock()); + if(!req) return; + if(status.isOK()) { + req->putDone(); + return; + } + string message = string("channel ") + + testChannel->getChannelName() + + " TestChannelPut::putDone " + + status.getMessage(); + throw std::runtime_error(message); +} + +void TestChannelPut::connect() +{ + string request("value"); + PVStructurePtr pvRequest(createRequest(request)); + + channelPut = testChannel->getChannel()->createChannelPut(shared_from_this(),pvRequest); + if(!channelPut) throw std::runtime_error(testChannel->getChannelName() + " channelCreate failed "); +} + +void TestChannelPut::waitConnect(double timeout) +{ + if(waitForConnect.wait(timeout)) return; + throw std::runtime_error(testChannel->getChannelName() + " TestChannelPut::waitConnect failed "); +} + + +void TestChannelPut::put(string const & value) +{ + PVFieldPtr pvField(pvStructure->getSubField("value")); + if(!pvField) throw std::runtime_error(testChannel->getChannelName() + " TestChannelPut::put no value "); + FieldConstPtr field(pvField->getField()); + Type type(field->getType()); + if(type==scalar) { + PVScalarPtr pvScalar(std::tr1::static_pointer_cast(pvField)); + getConvert()->fromString(pvScalar,value); + bitSet->set(pvField->getFieldOffset()); + channelPut->put(pvStructure,bitSet); + return; + } + throw std::runtime_error(testChannel->getChannelName() + " TestChannelPut::put not supported type"); +} + +class TestChannelMonitor; +typedef std::tr1::shared_ptr TestChannelMonitorPtr; + +class TestChannelMonitorRequester; +typedef std::tr1::shared_ptr TestChannelMonitorRequesterPtr; +typedef std::tr1::weak_ptr TestChannelMonitorRequesterWPtr; + +class TestChannelMonitorRequester +{ +public: + virtual void monitorEvent( + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet) = 0; +}; + +class TestChannelMonitor: + public MonitorRequester, + public std::tr1::enable_shared_from_this +{ +public: + POINTER_DEFINITIONS(TestChannelMonitor); + virtual string getRequesterName(); + virtual void message(string const & message, MessageType messageType) {} + virtual void monitorConnect( + Status const & status, + MonitorPtr const & monitor, + StructureConstPtr const & structure); + virtual void monitorEvent(MonitorPtr const & monitor); + virtual void unlisten(MonitorPtr const & monitor); + static TestChannelMonitorPtr create( + TestChannelMonitorRequesterPtr const &putRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest); + void connect(); + void waitConnect(double timeout); + void stopEvents(); +private: + TestChannelMonitor( + TestChannelMonitorRequesterPtr const &putRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest); + + TestChannelMonitorRequesterWPtr monitorRequester; + TestChannelPtr testChannel; + PVStructurePtr pvRequest; + Event waitForConnect; + Monitor::shared_pointer channelMonitor; +}; + +TestChannelMonitorPtr TestChannelMonitor::create( + TestChannelMonitorRequesterPtr const &monitorRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest) +{ + TestChannelMonitorPtr testChannelMonitor(new TestChannelMonitor(monitorRequester,testChannel,pvRequest)); + testChannelMonitor->connect(); + testChannelMonitor->waitConnect(5.0); + return testChannelMonitor; +} + +TestChannelMonitor::TestChannelMonitor( + TestChannelMonitorRequesterPtr const &monitorRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest) + : monitorRequester(monitorRequester), + testChannel(testChannel), + pvRequest(pvRequest) +{ +} + +string TestChannelMonitor::getRequesterName() {return "TestChannelMonitor";} + +void TestChannelMonitor::monitorConnect( + Status const & status, + MonitorPtr const & monitor, + StructureConstPtr const & structure) +{ + waitForConnect.signal(); +} + + +void TestChannelMonitor::monitorEvent(MonitorPtr const & monitor) +{ + TestChannelMonitorRequesterPtr req(monitorRequester.lock()); + if(!req) return; + while(true) { + MonitorElementPtr monitorElement = monitor->poll(); + if(!monitorElement) return; + req->monitorEvent(monitorElement->pvStructurePtr,monitorElement->changedBitSet); + monitor->release(monitorElement); + } +} + + +void TestChannelMonitor::unlisten(MonitorPtr const & monitor) +{ +} + +void TestChannelMonitor::connect() +{ + channelMonitor = testChannel->getChannel()->createMonitor(shared_from_this(),pvRequest); + if(!channelMonitor) throw std::runtime_error(testChannel->getChannelName() + " TestChannelMonitor::connect failed "); +} + +void TestChannelMonitor::waitConnect(double timeout) +{ + if(waitForConnect.wait(timeout)) { + channelMonitor->start(); + return; + } + throw std::runtime_error(testChannel->getChannelName() + " TestChannelMonitor::waitConnect failed "); +} + +void TestChannelMonitor::stopEvents() +{ + channelMonitor->stop(); +} + +class TestClient; +typedef std::tr1::shared_ptr TestClientPtr; + +class TestClient: + public TestChannelGetRequester, + public TestChannelPutRequester, + public TestChannelMonitorRequester, + public std::tr1::enable_shared_from_this +{ +public: + POINTER_DEFINITIONS(TestClient); + virtual void getDone( + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet); + virtual void putDone(); + virtual void monitorEvent( + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet); + static TestClientPtr create(string const &channelName,PVStructurePtr const & pvRequest); + void connect(); + void get(); + void put(string const & value); + void waitGet(double timeout); + void waitPut(double timeout); + void stopEvents(); +private: + TestClient(string const &channelName,PVStructurePtr const & pvRequest); + string channelName; + PVStructurePtr pvRequest; + TestChannelPtr testChannel; + TestChannelGetPtr testChannelGet; + TestChannelPutPtr testChannelPut; + TestChannelMonitorPtr testChannelMonitor; + Event waitForGet; + Event waitForPut; +}; + +TestClientPtr TestClient::create(string const &channelName,PVStructurePtr const & pvRequest) +{ + TestClientPtr testClient(new TestClient(channelName,pvRequest)); + testClient->connect(); + return testClient; +} + +TestClient::TestClient(string const &channelName,PVStructurePtr const & pvRequest) + : channelName(channelName), + pvRequest(pvRequest) +{ +} + +void TestClient::connect() +{ + testChannel = TestChannel::create(channelName); + testChannelGet = TestChannelGet::create(shared_from_this(),testChannel,pvRequest); + testChannelPut = TestChannelPut::create(shared_from_this(),testChannel); + testChannelMonitor = TestChannelMonitor::create(shared_from_this(),testChannel,pvRequest); +} + +void TestClient::getDone( + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet) +{ + testOk(pvStructure!=NULL,"pvStructure not null"); + testOk(pvStructure->getSubField("value")!=NULL,"value not null"); + testOk(pvStructure->getSubField("timeStamp")!=NULL,"timeStamp not null"); + testOk(pvStructure->getSubField("alarm")!=NULL,"alarm not null"); + waitForGet.signal(); +} + +void TestClient::putDone() +{ + waitForPut.signal(); +} + +void TestClient::monitorEvent( + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet) +{ + std::cout << testChannel->getChannelName() + " TestClient::monitorEvent" + << " bitSet " << *bitSet + << " pvStructure\n" << pvStructure << "\n"; +} + +void TestClient::get() +{ + testChannelGet->get(); + waitGet(5.0); +} + +void TestClient::waitGet(double timeout) +{ + if(waitForGet.wait(timeout)) return; + throw std::runtime_error(testChannel->getChannelName() + " TestClient::waitGet failed "); +} + +void TestClient::put(string const & value) +{ + testChannelPut->put(value); + waitPut(5.0); +} + +void TestClient::waitPut(double timeout) +{ + if(waitForPut.wait(timeout)) return; + throw std::runtime_error(testChannel->getChannelName() + " TestClient::waitPut failed "); +} + +void TestClient::stopEvents() +{ + testChannelMonitor->stopEvents(); +} + +class TestIoc; +typedef std::tr1::shared_ptr TestIocPtr; + +class TestIoc : + public epicsThreadRunable +{ +public: + virtual void run(); + static TestIocPtr create(); + void start(); +private: + std::auto_ptr thread; +}; + +TestIocPtr TestIoc::create() +{ + return TestIocPtr(new TestIoc()); +} + +void TestIoc::start() +{ + thread = std::auto_ptr(new epicsThread( + *this, + "testIoc", + epicsThreadGetStackSize(epicsThreadStackSmall), + epicsThreadPriorityLow)); + thread->start(); +} + +void TestIoc::run() +{ + system("softIoc -d ../ca/testCaProvider.db"); +} + +MAIN(testCaProvider) +{ + testPlan(11); + testDiag("===Test caProvider==="); + CAClientFactory::start(); + ChannelProviderRegistry::shared_pointer reg(ChannelProviderRegistry::clients()); + ChannelProvider::shared_pointer channelProvider(reg->getProvider("ca")); + try{ + TestIocPtr testIoc(new TestIoc()); + testIoc->start(); + if(!channelProvider) { + throw std::runtime_error(" provider ca not registered"); + } + string channelName; + channelName = "DBRdoubleout"; + string request("value,alarm,timeStamp"); + PVStructurePtr pvRequest(createRequest(request)); + TestClientPtr scalarout(TestClient::create(channelName,pvRequest)); + testOk(scalarout!=NULL,"DBRdoubleout not null"); + scalarout->put("1.5"); + scalarout->get(); + scalarout->stopEvents(); + channelName = "DBRstringout"; + scalarout = TestClient::create(channelName,pvRequest); + testOk(scalarout!=NULL,"DBRstringout not null"); + scalarout->put("test"); + scalarout->get(); + scalarout->stopEvents(); + // put to record that makes IOC exit + channelName = "DBRexit"; + scalarout = TestClient::create(channelName,pvRequest); + testOk(scalarout!=NULL,"DBRexit not null"); + scalarout->put("1"); + }catch(std::exception& e){ + PRINT_EXCEPTION(e); + testAbort("Unexpected Exception: %s", e.what()); + } + return testDone();; +} + diff --git a/testApp/ca/testCaProvider.db b/testApp/ca/testCaProvider.db new file mode 100644 index 0000000..d66e322 --- /dev/null +++ b/testApp/ca/testCaProvider.db @@ -0,0 +1,290 @@ + +record(calcout, "DBRcalcout") +{ + field(DESC, "calcout") + field(CALC, "(A