Newer
Older
Martin Christoph Hierholzer
committed
/*
* testDeviceAccessors.cc
*
* Created on: Jun 22, 2016
* Author: Martin Hierholzer
*/
#include <future>
#define BOOST_TEST_MODULE testDeviceAccessors
#include <boost/test/included/unit_test.hpp>
#include <boost/test/test_case_template.hpp>
#include <boost/mpl/list.hpp>
#include <mtca4u/Device.h>
Martin Christoph Hierholzer
committed
#include <mtca4u/BackendFactory.h>
#include <mtca4u/NDRegisterAccessor.h>
Martin Christoph Hierholzer
committed
#include "Application.h"
Martin Christoph Hierholzer
committed
#include "ScalarAccessor.h"
#include "ApplicationModule.h"
#include "DeviceModule.h"
Martin Christoph Hierholzer
committed
using namespace boost::unit_test_framework;
namespace ctk = ChimeraTK;
// list of user types the accessors are tested with
typedef boost::mpl::list<int8_t,uint8_t,
int16_t,uint16_t,
int32_t,uint32_t,
float,double> test_types;
#define CHECK_TIMEOUT(condition, maxMilliseconds) \
{ \
std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); \
while(!(condition)) { \
bool timeout_reached = (std::chrono::steady_clock::now()-t0) > std::chrono::milliseconds(maxMilliseconds); \
BOOST_CHECK( !timeout_reached ); \
if(timeout_reached) break; \
usleep(1000); \
} \
}
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
/* the ApplicationModule for the test is a template of the user type */
template<typename T>
Martin Christoph Hierholzer
committed
struct TestModule : public ctk::ApplicationModule {
using ctk::ApplicationModule::ApplicationModule;
Martin Christoph Hierholzer
committed
ctk::ScalarPollInput<T> consumingPoll{this, "consumingPoll", "MV/m", "Description"};
Martin Christoph Hierholzer
committed
ctk::ScalarPushInput<T> consumingPush{this, "consumingPush", "MV/m", "Description"};
ctk::ScalarPushInput<T> consumingPush2{this, "consumingPush2", "MV/m", "Description"};
ctk::ScalarOutput<T> feedingToDevice{this, "feedingToDevice", "MV/m", "Description"};
Martin Christoph Hierholzer
committed
void mainLoop() {}
};
/*********************************************************************************************************************/
/* dummy application */
Martin Christoph Hierholzer
committed
template<typename T>
Martin Christoph Hierholzer
committed
struct TestApplication : public ctk::Application {
Martin Christoph Hierholzer
committed
TestApplication() : Application("testSuite") {}
Martin Christoph Hierholzer
committed
~TestApplication() { shutdown(); }
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
using Application::makeConnections; // we call makeConnections() manually in the tests to catch exceptions etc.
using Application::deviceMap; // expose the device map for the tests
using Application::networkList; // expose network list to check merging networks
Martin Christoph Hierholzer
committed
void defineConnections() {} // the setup is done in the tests
Martin Christoph Hierholzer
committed
TestModule<T> testModule{this,"testModule", "The test module"};
Martin Christoph Hierholzer
committed
ctk::DeviceModule devMymodule{"Dummy0","MyModule"};
ctk::DeviceModule dev{"Dummy0"};
Martin Christoph Hierholzer
committed
// note: direct device-to-controlsystem connections are tested in testControlSystemAccessors!
Martin Christoph Hierholzer
committed
};
/*********************************************************************************************************************/
/* test feeding a scalar to a device */
BOOST_AUTO_TEST_CASE_TEMPLATE( testFeedToDevice, T, test_types ) {
std::cout << "testFeedToDevice" << std::endl;
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
mtca4u::BackendFactory::getInstance().setDMapFilePath("test.dmap");
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
TestApplication<T> app;
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
app.testModule.feedingToDevice >> app.devMymodule("actuator");
Martin Christoph Hierholzer
committed
app.initialise();
Martin Christoph Hierholzer
committed
mtca4u::Device dev;
dev.open("Dummy0");
auto regacc = dev.getScalarRegisterAccessor<int>("/MyModule/actuator");
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
app.testModule.feedingToDevice = 42;
app.testModule.feedingToDevice.write();
regacc.read();
BOOST_CHECK(regacc == 42);
Martin Christoph Hierholzer
committed
app.testModule.feedingToDevice = 120;
regacc.read();
BOOST_CHECK(regacc == 42);
Martin Christoph Hierholzer
committed
app.testModule.feedingToDevice.write();
regacc.read();
BOOST_CHECK(regacc == 120);
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
/* test feeding a scalar to two different device registers */
BOOST_AUTO_TEST_CASE_TEMPLATE( testFeedToDeviceFanOut, T, test_types ) {
std::cout << "testFeedToDeviceFanOut" << std::endl;
mtca4u::BackendFactory::getInstance().setDMapFilePath("test.dmap");
TestApplication<T> app;
app.testModule.feedingToDevice >> app.devMymodule("actuator")
>> app.devMymodule("readBack");
app.initialise();
mtca4u::Device dev;
dev.open("Dummy0");
auto regac = dev.getScalarRegisterAccessor<int>("/MyModule/actuator");
auto regrb = dev.getScalarRegisterAccessor<int>("/MyModule/readBack");
regac = 0;
regrb = 0;
app.testModule.feedingToDevice = 42;
app.testModule.feedingToDevice.write();
regac.read();
BOOST_CHECK(regac == 42);
regrb.read();
BOOST_CHECK(regrb == 42);
app.testModule.feedingToDevice = 120;
regac.read();
BOOST_CHECK(regac == 42);
regrb.read();
BOOST_CHECK(regrb == 42);
app.testModule.feedingToDevice.write();
regac.read();
BOOST_CHECK(regac == 120);
regrb.read();
BOOST_CHECK(regrb == 120);
}
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
/* test consuming a scalar from a device */
BOOST_AUTO_TEST_CASE_TEMPLATE( testConsumeFromDevice, T, test_types ) {
std::cout << "testConsumeFromDevice" << std::endl;
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
mtca4u::BackendFactory::getInstance().setDMapFilePath("test.dmap");
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
TestApplication<T> app;
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
app.dev("/MyModule/actuator") >> app.testModule.consumingPoll;
Martin Christoph Hierholzer
committed
app.initialise();
Martin Christoph Hierholzer
committed
mtca4u::Device dev;
dev.open("Dummy0");
auto regacc = dev.getScalarRegisterAccessor<int>("/MyModule/actuator");
Martin Christoph Hierholzer
committed
// single theaded test only, since read() does not block in this case
Martin Christoph Hierholzer
committed
app.testModule.consumingPoll = 0;
regacc = 42;
regacc.write();
Martin Christoph Hierholzer
committed
BOOST_CHECK(app.testModule.consumingPoll == 0);
app.testModule.consumingPoll.read();
BOOST_CHECK(app.testModule.consumingPoll == 42);
app.testModule.consumingPoll.read();
BOOST_CHECK(app.testModule.consumingPoll == 42);
app.testModule.consumingPoll.read();
BOOST_CHECK(app.testModule.consumingPoll == 42);
regacc = 120;
regacc.write();
Martin Christoph Hierholzer
committed
BOOST_CHECK(app.testModule.consumingPoll == 42);
app.testModule.consumingPoll.read();
BOOST_CHECK( app.testModule.consumingPoll == 120 );
app.testModule.consumingPoll.read();
BOOST_CHECK( app.testModule.consumingPoll == 120 );
app.testModule.consumingPoll.read();
BOOST_CHECK( app.testModule.consumingPoll == 120 );
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
/* test consuming a scalar from a device with a ConsumingFanOut (i.e. one poll-type consumer and several push-type
* consumers). */
BOOST_AUTO_TEST_CASE_TEMPLATE( testConsumingFanOut, T, test_types ) {
std::cout << "testConsumingFanOut" << std::endl;
mtca4u::BackendFactory::getInstance().setDMapFilePath("test.dmap");
TestApplication<T> app;
app.dev("/MyModule/actuator") >> app.testModule.consumingPoll
>> app.testModule.consumingPush
>> app.testModule.consumingPush2;
app.initialise();
mtca4u::Device dev;
dev.open("Dummy0");
auto regacc = dev.getScalarRegisterAccessor<int>("/MyModule/actuator");
// single theaded test only, since read() does not block in this case
app.testModule.consumingPoll = 0;
regacc = 42;
regacc.write();
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
BOOST_CHECK(app.testModule.consumingPoll == 0);
BOOST_CHECK(app.testModule.consumingPush.readNonBlocking() == false);
BOOST_CHECK(app.testModule.consumingPush2.readNonBlocking() == false);
BOOST_CHECK(app.testModule.consumingPush == 0);
BOOST_CHECK(app.testModule.consumingPush2 == 0);
app.testModule.consumingPoll.read();
BOOST_CHECK(app.testModule.consumingPush.readNonBlocking() == true);
BOOST_CHECK(app.testModule.consumingPush2.readNonBlocking() == true);
BOOST_CHECK(app.testModule.consumingPoll == 42);
BOOST_CHECK(app.testModule.consumingPush == 42);
BOOST_CHECK(app.testModule.consumingPush2 == 42);
BOOST_CHECK(app.testModule.consumingPush.readNonBlocking() == false);
BOOST_CHECK(app.testModule.consumingPush2.readNonBlocking() == false);
app.testModule.consumingPoll.read();
BOOST_CHECK(app.testModule.consumingPush.readNonBlocking() == true);
BOOST_CHECK(app.testModule.consumingPush2.readNonBlocking() == true);
BOOST_CHECK(app.testModule.consumingPoll == 42);
BOOST_CHECK(app.testModule.consumingPush == 42);
BOOST_CHECK(app.testModule.consumingPush2 == 42);
BOOST_CHECK(app.testModule.consumingPush.readNonBlocking() == false);
BOOST_CHECK(app.testModule.consumingPush2.readNonBlocking() == false);
app.testModule.consumingPoll.read();
BOOST_CHECK(app.testModule.consumingPush.readNonBlocking() == true);
BOOST_CHECK(app.testModule.consumingPush2.readNonBlocking() == true);
BOOST_CHECK(app.testModule.consumingPoll == 42);
BOOST_CHECK(app.testModule.consumingPush == 42);
BOOST_CHECK(app.testModule.consumingPush2 == 42);
BOOST_CHECK(app.testModule.consumingPush.readNonBlocking() == false);
BOOST_CHECK(app.testModule.consumingPush2.readNonBlocking() == false);
regacc = 120;
regacc.write();
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
BOOST_CHECK(app.testModule.consumingPoll == 42);
BOOST_CHECK(app.testModule.consumingPush.readNonBlocking() == false);
BOOST_CHECK(app.testModule.consumingPush2.readNonBlocking() == false);
BOOST_CHECK(app.testModule.consumingPush == 42);
BOOST_CHECK(app.testModule.consumingPush2 == 42);
app.testModule.consumingPoll.read();
BOOST_CHECK(app.testModule.consumingPush.readNonBlocking() == true);
BOOST_CHECK(app.testModule.consumingPush2.readNonBlocking() == true);
BOOST_CHECK( app.testModule.consumingPoll == 120 );
BOOST_CHECK(app.testModule.consumingPush == 120);
BOOST_CHECK(app.testModule.consumingPush2 == 120);
BOOST_CHECK(app.testModule.consumingPush.readNonBlocking() == false);
BOOST_CHECK(app.testModule.consumingPush2.readNonBlocking() == false);
app.testModule.consumingPoll.read();
BOOST_CHECK(app.testModule.consumingPush.readNonBlocking() == true);
BOOST_CHECK(app.testModule.consumingPush2.readNonBlocking() == true);
BOOST_CHECK(app.testModule.consumingPoll == 120);
BOOST_CHECK(app.testModule.consumingPush == 120);
BOOST_CHECK(app.testModule.consumingPush2 == 120);
BOOST_CHECK(app.testModule.consumingPush.readNonBlocking() == false);
BOOST_CHECK(app.testModule.consumingPush2.readNonBlocking() == false);
app.testModule.consumingPoll.read();
BOOST_CHECK(app.testModule.consumingPush.readNonBlocking() == true);
BOOST_CHECK(app.testModule.consumingPush2.readNonBlocking() == true);
BOOST_CHECK(app.testModule.consumingPoll == 120);
BOOST_CHECK(app.testModule.consumingPush == 120);
BOOST_CHECK(app.testModule.consumingPush2 == 120);
BOOST_CHECK(app.testModule.consumingPush.readNonBlocking() == false);
BOOST_CHECK(app.testModule.consumingPush2.readNonBlocking() == false);
}
/*********************************************************************************************************************/
/* test merged networks (optimisation done in Application::optimiseConnections()) */
BOOST_AUTO_TEST_CASE_TEMPLATE( testMergedNetworks, T, test_types ) {
std::cout << "testMergedNetworks" << std::endl;
mtca4u::BackendFactory::getInstance().setDMapFilePath("test.dmap");
TestApplication<T> app;
// we abuse "feedingToDevice" as trigger here...
app.dev("/MyModule/actuator") [ app.testModule.feedingToDevice ] >> app.testModule.consumingPush;
app.dev("/MyModule/actuator") [ app.testModule.feedingToDevice ] >> app.testModule.consumingPush2;
// check that we have two separate networks for both connections
size_t nDeviceFeeders = 0;
for(auto &net : app.networkList) {
if( net.getFeedingNode().getType() == ctk::NodeType::Device ) nDeviceFeeders++;
}
BOOST_CHECK_EQUAL( nDeviceFeeders, 2 );
// the optimisation to test takes place here
app.initialise();
// check we are left with just one network fed by the device
nDeviceFeeders = 0;
for(auto &net : app.networkList) {
if( net.getFeedingNode().getType() == ctk::NodeType::Device ) nDeviceFeeders++;
}
BOOST_CHECK_EQUAL( nDeviceFeeders, 1 );
// run the application to see if everything still behaves as expected
app.run();
mtca4u::Device dev;
dev.open("Dummy0");
auto regacc = dev.getScalarRegisterAccessor<int>("/MyModule/actuator");
// single theaded test only, since read() does not block in this case
app.testModule.consumingPush = 0;
app.testModule.consumingPush2 = 0;
regacc = 42;
regacc.write();
BOOST_CHECK(app.testModule.consumingPush == 0);
BOOST_CHECK(app.testModule.consumingPush2 == 0);
app.testModule.feedingToDevice.write();
app.testModule.consumingPush.read();
app.testModule.consumingPush2.read();
BOOST_CHECK(app.testModule.consumingPush == 42);
BOOST_CHECK(app.testModule.consumingPush2 == 42);
regacc = 120;
regacc.write();
BOOST_CHECK(app.testModule.consumingPush == 42);
BOOST_CHECK(app.testModule.consumingPush2 == 42);
app.testModule.feedingToDevice.write();
app.testModule.consumingPush.read();
app.testModule.consumingPush2.read();
BOOST_CHECK( app.testModule.consumingPush == 120 );
BOOST_CHECK( app.testModule.consumingPush2 == 120 );
}
/*********************************************************************************************************************/
/* test feeding a constant to a device register. */
BOOST_AUTO_TEST_CASE_TEMPLATE( testConstantToDevice, T, test_types ) {
std::cout << "testConstantToDevice" << std::endl;
mtca4u::BackendFactory::getInstance().setDMapFilePath("test.dmap");
TestApplication<T> app;
ctk::VariableNetworkNode::makeConstant<T>(true, 18) >> app.dev("/MyModule/actuator");
app.initialise();
app.run();
mtca4u::Device dev;
dev.open("Dummy0");
CHECK_TIMEOUT( dev.read<T>("/MyModule/actuator") == 18, 3000 );
}
/*********************************************************************************************************************/
/* test feeding a constant to a device register with a fan out. */
BOOST_AUTO_TEST_CASE_TEMPLATE( testConstantToDeviceFanOut, T, test_types ) {
std::cout << "testConstantToDeviceFanOut" << std::endl;
mtca4u::BackendFactory::getInstance().setDMapFilePath("test.dmap");
TestApplication<T> app;
ctk::VariableNetworkNode::makeConstant<T>(true, 20) >> app.dev("/MyModule/actuator")
>> app.dev("/MyModule/readBack");
app.initialise();
app.run();
mtca4u::Device dev;
dev.open("Dummy0");
CHECK_TIMEOUT( dev.read<T>("/MyModule/actuator") == 20, 3000 );
CHECK_TIMEOUT( dev.read<T>("/MyModule/readBack") == 20, 3000 );
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
/* test subscript operator of DeviceModule */
BOOST_AUTO_TEST_CASE_TEMPLATE( testDeviceModuleSubscriptOp, T, test_types ) {
std::cout << "testDeviceModuleSubscriptOp" << std::endl;
mtca4u::BackendFactory::getInstance().setDMapFilePath("test.dmap");
TestApplication<T> app;
app.testModule.feedingToDevice >> app.dev["MyModule"]("actuator");
app.initialise();
mtca4u::Device dev;
dev.open("Dummy0");
auto regacc = dev.getScalarRegisterAccessor<int>("/MyModule/actuator");
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
app.testModule.feedingToDevice = 42;
app.testModule.feedingToDevice.write();
regacc.read();
BOOST_CHECK(regacc == 42);
Martin Christoph Hierholzer
committed
app.testModule.feedingToDevice = 120;
regacc.read();
BOOST_CHECK(regacc == 42);
Martin Christoph Hierholzer
committed
app.testModule.feedingToDevice.write();
regacc.read();
BOOST_CHECK(regacc == 120);
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
/* test DeviceModule::virtualise() (trivial implementation) */
BOOST_AUTO_TEST_CASE( testDeviceModuleVirtuallise ) {
std::cout << "testDeviceModuleVirtuallise" << std::endl;
mtca4u::BackendFactory::getInstance().setDMapFilePath("test.dmap");
TestApplication<int> app;
app.testModule.feedingToDevice >> app.dev.virtualise()["MyModule"]("actuator");
Martin Christoph Hierholzer
committed
app.initialise();
BOOST_CHECK( &(app.dev.virtualise()) == &(app.dev) );