-
Martin Christoph Hierholzer authoredMartin Christoph Hierholzer authored
testDirectDeviceToCS.cc 9.22 KiB
/*
* testDirectDeviceToCS.cc
*
* Created on: Jun 22, 2016
* Author: Martin Hierholzer
*/
#define BOOST_TEST_MODULE testDirectDeviceToCS
#include <boost/test/included/unit_test.hpp>
#include <ChimeraTK/Device.h>
#include "Application.h"
#include "PeriodicTrigger.h"
#include "DeviceModule.h"
#include "ControlSystemModule.h"
#include "TestFacility.h"
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); \
} \
}
/*********************************************************************************************************************/
/* the ApplicationModule for the test is a template of the user type */
template<typename T>
struct TestModule : public ctk::ApplicationModule {
using ctk::ApplicationModule::ApplicationModule;
ctk::ScalarPushInput<T> consumer{this, "consumer", "", "No comment."};
ctk::ScalarOutput<T> feeder{this, "feeder", "MV/m", "Some fancy explanation about this variable"};
void mainLoop() {}
};
/*********************************************************************************************************************/
/* dummy application */
template<typename T>
struct TestApplication : public ctk::Application {
TestApplication() : Application("testSuite") {
ChimeraTK::BackendFactory::getInstance().setDMapFilePath("test.dmap");
}
~TestApplication() { shutdown(); }
using Application::makeConnections; // we call makeConnections() manually in the tests to catch exceptions etc.
void defineConnections() {} // the setup is done in the tests
TestModule<T> testModule{this, "TestModule", "The test module"};
ctk::ControlSystemModule cs;
ctk::DeviceModule dev{"Dummy0"};
};
/*********************************************************************************************************************/
/* dummy application for connectTo() test */
struct TestApplicationConnectTo : ctk::Application {
TestApplicationConnectTo() : Application("testSuite") {}
~TestApplicationConnectTo() { shutdown(); }
using Application::makeConnections; // we call makeConnections() manually in the tests to catch exceptions etc.
void defineConnections() {
dev.connectTo(cs, trigger.tick);
}
ctk::PeriodicTrigger trigger{this, "trigger", ""};
ctk::DeviceModule dev{"(dummy?map=test3.map)"};
ctk::ControlSystemModule cs;
};
/*********************************************************************************************************************/
template<typename T, typename LAMBDA>
void testDirectRegister(ctk::TestFacility &test, ChimeraTK::ScalarRegisterAccessor<T> sender,
ChimeraTK::ScalarRegisterAccessor<T> receiver,
LAMBDA trigger, bool testMinMax=true) {
sender = 42;
sender.write();
trigger();
test.stepApplication();
receiver.read();
BOOST_CHECK_EQUAL( receiver, 42 );
if(std::numeric_limits<T>::is_signed) {
sender = -120;
sender.write();
trigger();
test.stepApplication();
receiver.read();
BOOST_CHECK_EQUAL( receiver, -120 );
}
if(testMinMax) {
sender = std::numeric_limits<T>::max();
sender.write();
trigger();
test.stepApplication();
receiver.read();
BOOST_CHECK_EQUAL( receiver, std::numeric_limits<T>::max() );
sender = std::numeric_limits<T>::min();
sender.write();
trigger();
test.stepApplication();
receiver.read();
BOOST_CHECK_EQUAL( receiver, std::numeric_limits<T>::min() );
sender = std::numeric_limits<T>::epsilon();
sender.write();
trigger();
test.stepApplication();
receiver.read();
BOOST_CHECK_EQUAL( receiver, std::numeric_limits<T>::epsilon() );
}
}
/*********************************************************************************************************************/
/* test direct control system to device connections */
BOOST_AUTO_TEST_CASE_TEMPLATE( testDirectCStoDev, T, test_types ) {
TestApplication<T> app;
auto pvManagers = ctk::createPVManager();
app.setPVManager(pvManagers.second);
app.cs("myFeeder", typeid(T), 1) >> app.dev("/MyModule/actuator");
app.initialise();
app.run();
ChimeraTK::Device dev;
dev.open("Dummy0");
BOOST_CHECK_EQUAL(pvManagers.first->getAllProcessVariables().size(), 1);
auto myFeeder = pvManagers.first->getProcessArray<T>("/myFeeder");
BOOST_CHECK( myFeeder->getName() == "/myFeeder" );
myFeeder->accessData(0) = 18;
myFeeder->write();
CHECK_TIMEOUT( dev.read<T>("/MyModule/actuator") == 18, 3000);
myFeeder->accessData(0) = 20;
myFeeder->write();
CHECK_TIMEOUT( dev.read<T>("/MyModule/actuator") == 20, 3000);
}
/*********************************************************************************************************************/
/* test direct control system to device connections with fan out */
BOOST_AUTO_TEST_CASE_TEMPLATE( testDirectCStoDevFanOut, T, test_types ) {
TestApplication<T> app;
auto pvManagers = ctk::createPVManager();
app.setPVManager(pvManagers.second);
app.cs("myFeeder", typeid(T), 1) >> app.dev("/MyModule/actuator")
>> app.dev("/MyModule/readBack");
app.initialise();
app.run();
ChimeraTK::Device dev;
dev.open("Dummy0");
BOOST_CHECK_EQUAL(pvManagers.first->getAllProcessVariables().size(), 1);
auto myFeeder = pvManagers.first->getProcessArray<T>("/myFeeder");
BOOST_CHECK( myFeeder->getName() == "/myFeeder" );
myFeeder->accessData(0) = 18;
myFeeder->write();
CHECK_TIMEOUT( dev.read<T>("/MyModule/actuator") == 18, 3000);
CHECK_TIMEOUT( dev.read<T>("/MyModule/readBack") == 18, 3000);
myFeeder->accessData(0) = 20;
myFeeder->write();
CHECK_TIMEOUT( dev.read<T>("/MyModule/actuator") == 20, 3000);
CHECK_TIMEOUT( dev.read<T>("/MyModule/readBack") == 20, 3000);
}
/*********************************************************************************************************************/
/* test connectTo */
BOOST_AUTO_TEST_CASE( testConnectTo ) {
std::cout << "testConnectTo" << std::endl;
ctk::Device dev;
dev.open("(dummy?map=test3.map)");
TestApplicationConnectTo app;
ctk::TestFacility test;
auto devActuator = dev.getScalarRegisterAccessor<int32_t>("/MyModule/actuator");
auto devReadback = dev.getScalarRegisterAccessor<int32_t>("/MyModule/readBack");
auto devint32 = dev.getScalarRegisterAccessor<int32_t>("/Integers/signed32");
auto devuint32 = dev.getScalarRegisterAccessor<uint32_t>("/Integers/unsigned32");
auto devint16 = dev.getScalarRegisterAccessor<int16_t>("/Integers/signed16");
auto devuint16 = dev.getScalarRegisterAccessor<uint16_t>("/Integers/unsigned16");
auto devint8 = dev.getScalarRegisterAccessor<int8_t>("/Integers/signed8");
auto devuint8 = dev.getScalarRegisterAccessor<uint8_t>("/Integers/unsigned8");
auto devfloat = dev.getScalarRegisterAccessor<double>("/FixedPoint/value");
auto csActuator = test.getScalar<int32_t>("/MyModule/actuator");
auto csReadback = test.getScalar<int32_t>("/MyModule/readBack");
auto csint32 = test.getScalar<int32_t>("/Integers/signed32");
auto csuint32 = test.getScalar<uint32_t>("/Integers/unsigned32");
auto csint16 = test.getScalar<int16_t>("/Integers/signed16");
auto csuint16 = test.getScalar<uint16_t>("/Integers/unsigned16");
auto csint8 = test.getScalar<int8_t>("/Integers/signed8");
auto csuint8 = test.getScalar<uint8_t>("/Integers/unsigned8");
auto csfloat = test.getScalar<double>("/FixedPoint/value");
test.runApplication();
testDirectRegister(test, csActuator, devActuator, []{});
testDirectRegister(test, devReadback, csReadback, [&]{app.trigger.sendTrigger();});
testDirectRegister(test, csint32, devint32, []{});
testDirectRegister(test, csuint32, devuint32, []{});
testDirectRegister(test, csint16, devint16, []{});
testDirectRegister(test, csuint16, devuint16, []{});
testDirectRegister(test, csint8, devint8, []{});
testDirectRegister(test, csuint8, devuint8, []{});
testDirectRegister(test, csfloat, devfloat, []{}, false);
}