4#define BOOST_TEST_DYN_LINK
5#define BOOST_TEST_MODULE SubdeviceBackendUnifiedTest
6#include <boost/test/unit_test.hpp>
7using namespace boost::unit_test_framework;
15#include <boost/thread/barrier.hpp>
19BOOST_AUTO_TEST_SUITE(DoubleBufferingBackendUnifiedTestSuite)
31 static boost::shared_ptr<DeviceBackend>
createInstance(std::string, std::map<std::string, std::string> parameters) {
32 return returnInstance<DummyForDoubleBuffering>(
33 parameters.at(
"map"), convertPathRelativeToDmapToAbs(parameters.at(
"map")));
43 void read(uint64_t bar, uint64_t address, int32_t* data,
size_t sizeInBytes)
override {
48 for(
unsigned i = 0; i < 2; i++) {
49 if(blockNextRead[i]) {
50 blockedInRead[i].wait();
51 unblockRead[i].wait();
52 blockNextRead[i] =
false;
62 static thread_local bool blockNextRead[2];
65 std::array<boost::barrier, 2> blockedInRead{boost::barrier{2}, boost::barrier{2}};
67 std::array<boost::barrier, 2> unblockRead{boost::barrier{2}, boost::barrier{2}};
73static std::string rawDeviceCdd(
"(DummyForDoubleBuffering?map=doubleBuffer.map)");
74std::string
lmap =
"(logicalNameMap?map=doubleBuffer.xlmap&target=" + rawDeviceCdd +
")";
80template<
typename Register>
93 .disableAsyncReadInconsistency()
94 .disableTestWriteNeverLosesData()
95 .disableSwitchReadOnly()
96 .disableSwitchWriteOnly()
97 .disableTestRawTransfer();
99 template<
typename UserType>
101 auto values = this->getRemoteValue<typename Register::minimumUserType>();
102 for(
size_t i = 0; i < this->
nChannels(); ++i)
103 for(
size_t j = 0; j < this->nElementsPerChannel(); ++j) {
104 values[i][j] += this->increment * (i + j + 1);
109 template<
typename UserType>
119 auto currentBufferNumber = backdoor->getRegisterAccessor<uint32_t>(
"APP.0.WORD_DUB_BUF_CURR", 0, 0, {});
120 auto buffer0 = backdoor->getRegisterAccessor<
typename Register::minimumUserType>(
121 "APP/0/DAQ0_BUF0", this->nElementsPerChannel(), 0, {});
122 auto buffer1 = backdoor->getRegisterAccessor<
typename Register::minimumUserType>(
123 "APP/0/DAQ0_BUF1", this->nElementsPerChannel(), 0, {});
125 bool deviceWasOpened =
false;
126 if(!backdoor->isOpen()) {
128 deviceWasOpened =
true;
131 boost::shared_ptr<NDRegisterAccessor<typename Register::minimumUserType>> currentBuffer;
133 currentBufferNumber->read();
135 if(currentBufferNumber->accessData(0) == 1) {
136 currentBuffer = buffer0;
139 currentBuffer = buffer1;
141 currentBuffer->read();
142 std::vector<std::vector<UserType>> v;
143 for(
size_t i = 0; i < this->
nChannels(); ++i) {
144 v.push_back(std::vector<UserType>());
145 for(
size_t j = 0; j < this->nElementsPerChannel(); ++j) {
146 v[i].push_back(currentBuffer->accessData(j));
150 if(deviceWasOpened) {
158 auto currentBufferNumber = backdoor->getRegisterAccessor<uint32_t>(
"APP.0.WORD_DUB_BUF_CURR", 0, 0, {});
159 auto buffer0 = backdoor->getRegisterAccessor<
typename Register::minimumUserType>(
160 "APP/0/DAQ0_BUF0", this->nElementsPerChannel(), 0, {});
161 auto buffer1 = backdoor->getRegisterAccessor<
typename Register::minimumUserType>(
162 "APP/0/DAQ0_BUF1", this->nElementsPerChannel(), 0, {});
163 boost::shared_ptr<NDRegisterAccessor<typename Register::minimumUserType>> currentBuffer;
165 bool deviceWasOpened =
false;
166 if(!backdoor->isOpen()) {
168 deviceWasOpened =
true;
172 currentBufferNumber->write();
175 auto values = this->generateValue<typename Register::minimumUserType>();
177 if(currentBufferNumber->accessData(0) == 1) {
178 currentBuffer = buffer0;
181 currentBuffer = buffer1;
183 for(
size_t i = 0; i < this->
nChannels(); ++i) {
184 for(
size_t j = 0; j < this->nElementsPerChannel(); ++j) {
185 currentBuffer->accessData(i, j) = values[i][j];
188 currentBuffer->write();
190 if(deviceWasOpened) {
196 if(caseNumber == 0) {
197 backdoor->throwExceptionRead = enable;
198 backdoor->throwExceptionOpen = enable;
206 std::string
path() {
return "/doubleBuffer"; }
217template<
typename Register>
232 doubleBufDummy = boost::dynamic_pointer_cast<DummyForDoubleBuffering>(backdoor);
244 auto accessor = d.getOneDRegisterAccessor<uint32_t>(
"/doubleBuffer");
249 doubleBufDummy->blockNextRead[0] =
true;
254 doubleBufDummy->blockedInRead[0].wait();
258 doubleBufferingEnabled->readLatest();
259 BOOST_CHECK(!doubleBufferingEnabled->accessData(0));
261 doubleBufDummy->unblockRead[0].wait();
265 doubleBufferingEnabled->readLatest();
266 BOOST_CHECK(doubleBufferingEnabled->accessData(0));
285 std::thread readerA([&] {
286 auto accessor = d.getOneDRegisterAccessor<uint32_t>(
"/doubleBuffer");
288 doubleBufDummy->blockNextRead[0] =
true;
291 std::thread readerB([&] {
292 auto accessor = d.getOneDRegisterAccessor<uint32_t>(
"/doubleBuffer");
294 doubleBufDummy->blockedInRead[0].wait();
296 doubleBufDummy->blockNextRead[1] =
true;
299 doubleBufDummy->blockedInRead[1].wait();
300 doubleBufDummy->unblockRead[0].wait();
304 doubleBufferingEnabled->readLatest();
305 BOOST_CHECK(!doubleBufferingEnabled->accessData(0));
307 doubleBufDummy->unblockRead[1].wait();
310 doubleBufferingEnabled->readLatest();
311 BOOST_CHECK(doubleBufferingEnabled->accessData(0));
318template<
class Derived>
321 const std::string
lmap =
"(logicalNameMap?map=doubleBuffer.xlmap&target=" + this->rawDeviceCdd +
")";
325 boost::shared_ptr<NDRegisterAccessor<float>>
buf0,
buf1;
352 float modulation = 4.2;
353 unsigned channel = 3;
354 buf0->accessData(channel, 0) = modulation;
355 buf1->accessData(channel, 0) = 2 * modulation;
359 boost::barrier waitForBufferSwapStart{2}, waitForBufferSwapDone{2};
360 std::thread readerA([&] {
362 accessorA.readLatest();
364 BOOST_CHECK_CLOSE(accessorA[0], modulation, 1e-4);
365 waitForBufferSwapStart.wait();
366 waitForBufferSwapDone.wait();
367 accessorA.readLatest();
368 BOOST_CHECK_CLOSE(accessorA[0], 2 * modulation, 1e-4);
371 waitForBufferSwapStart.wait();
374 waitForBufferSwapDone.wait();
388 float modulation = 4.2;
389 float correction = 10.1;
390 buf0->accessData(3, 0) = modulation;
391 buf1->accessData(3, 0) = 2 * modulation;
392 buf0->accessData(1, 0) = correction;
393 buf1->accessData(1, 0) = 2 * correction;
397 boost::barrier waitForBufferSwap{3};
400 bool readerA_ok1, readerA_ok2, readerB_ok1, readerB_ok2;
402 std::thread readerA([&] {
404 accessorA.readLatest();
405 readerA_ok1 = abs(accessorA[0] - modulation) < 1e-4;
406 waitForBufferSwap.wait();
407 waitForBufferSwap.wait();
408 accessorA.readLatest();
409 readerA_ok2 = abs(accessorA[0] - 2 * modulation) < 1e-4;
411 std::thread readerB([&] {
414 readerB_ok1 = abs(accessorB[0] - correction) < 1e-4;
415 waitForBufferSwap.wait();
416 waitForBufferSwap.wait();
418 readerB_ok2 = abs(accessorB[0] - 2 * correction) < 1e-4;
421 waitForBufferSwap.wait();
424 waitForBufferSwap.wait();
428 BOOST_CHECK(readerA_ok1);
429 BOOST_CHECK(readerA_ok2);
430 BOOST_CHECK(readerB_ok1);
431 BOOST_CHECK(readerB_ok2);
440 BOOST_CHECK_CLOSE(accessorA[0], 2 * modulation, 1e-4);
441 BOOST_CHECK_CLOSE(accessorB[0], 2 * correction, 1e-4);
446 BOOST_CHECK_CLOSE(accessorA[0], modulation, 1e-4);
447 BOOST_CHECK_CLOSE(accessorB[0], correction, 1e-4);
454 c.enableDoubleBufferingReg =
"DAQ0/WORD_DUB_BUF_ENA";
455 c.currentBufferNumberReg =
"DAQ0/WORD_DUB_BUF_CURR/DUMMY_WRITEABLE";
456 c.firstBufferReg =
"APP0/DAQ0_BUF0";
457 c.secondBufferReg =
"APP0/DAQ0_BUF1";
465 c.enableDoubleBufferingReg =
"DAQ2/WORD_DUB_BUF_ENA";
466 c.currentBufferNumberReg =
"DAQ2/WORD_DUB_BUF_CURR/DUMMY_WRITEABLE";
467 c.firstBufferReg =
"APP2/DAQ2_BUF0";
468 c.secondBufferReg =
"APP2/DAQ2_BUF1";
476 simpleCheckExtractedChannels(
"modulationA");
477 checkExtractedChannels(
"modulationA",
"correctionA");
482 simpleCheckExtractedChannels(
"modulationC");
483 checkExtractedChannels(
"modulationC",
"correctionC");
488BOOST_AUTO_TEST_SUITE_END()
Set of AccessMode flags with additional functionality for an easier handling.
static BackendFactory & getInstance()
Static function to get an instance of factory.
void registerBackendType(const std::string &backendType, boost::shared_ptr< DeviceBackend >(*creatorFunction)(std::string address, std::map< std::string, std::string > parameters), const std::vector< std::string > &sdmParameterNames={}, const std::string &deviceAccessVersion=CHIMERATK_DEVICEACCESS_VERSION)
Register a backend by the name backendType with the given creatorFunction.
boost::shared_ptr< DeviceBackend > createBackend(const std::string &aliasOrUri)
Create a new backend and return the instance as a shared pointer.
Class allows to read/write registers from device.
OneDRegisterAccessor< UserType > getOneDRegisterAccessor(const RegisterPath ®isterPathName, size_t numberOfWords=0, size_t wordOffsetInRegister=0, const AccessModeFlags &flags=AccessModeFlags({})) const
Get a OneDRegisterAccessor object for the given register.
void open(std::string const &aliasName)
Open a device by the given alias name from the DMAP file.
void read(uint64_t bar, uint64_t address, int32_t *data, size_t sizeInBytes) override
Read function to be implemented by backends.
ExceptionDummy(std::string const &mapFileName, const std::string &dataConsistencyKeyDescriptor="")
Group multiple data accessors to efficiently trigger data transfers on the whole group.
void addAccessor(TransferElementAbstractor &accessor)
Add a register accessor to the group.
void read()
Trigger read transfer for all accessors in the group.
Class to test any backend for correct behaviour.
UnifiedBackendTest< typename boost::mpl::push_back< VECTOR_OF_REGISTERS_T, REG_T >::type > addRegister()
Add a register to be used by the test.
size_t writeQueueLength()
size_t nRuntimeErrorCases()
static constexpr auto capabilities
std::vector< std::vector< UserType > > generateValue()
void setForceRuntimeError(bool enable, size_t caseNumber)
ChimeraTK::AccessModeFlags supportedFlags()
std::vector< std::vector< UserType > > getRemoteValue(bool=false)
static uint32_t _currentBufferNumber
Descriptor for the test capabilities for each register.
constexpr TestCapabilities< _syncRead, TestCapability::disabled, _asyncReadInconsistency, _switchReadOnly, _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue, _setRemoteValueIncrementsVersion, _testPartialAccessor > disableForceDataLossWrite() const
std::string secondBufferReg
std::string currentBufferNumberReg
std::string firstBufferReg
std::string enableDoubleBufferingReg
DeviceFixture used for the 2D access tests here no overwriting of ExceptionBackend.
boost::shared_ptr< NDRegisterAccessor< uint32_t > > writingBufferNum
boost::shared_ptr< NDRegisterAccessor< float > > buf1
void simpleCheckExtractedChannels(std::string readerAReg)
boost::shared_ptr< DeviceBackend > backdoor
void checkExtractedChannels(std::string readerAReg, std::string readerBReg)
const std::string rawDeviceCdd
boost::shared_ptr< NDRegisterAccessor< float > > buf0
boost::shared_ptr< NDRegisterAccessor< uint32_t > > doubleBufferingEnabled
boost::shared_ptr< NDRegisterAccessor< uint32_t > > doubleBufferingEnabled
boost::shared_ptr< DummyForDoubleBuffering > doubleBufDummy
dummy backend used for testing the double buffering handshake.
void read(uint64_t bar, uint64_t address, int32_t *data, size_t sizeInBytes) override
Read function to be implemented by backends.
static thread_local bool blockNextRead[2]
static boost::shared_ptr< DeviceBackend > createInstance(std::string, std::map< std::string, std::string > parameters)
size_t nElementsPerChannel()
BOOST_FIXTURE_TEST_CASE(testSlowReader, DeviceFixture)
BOOST_AUTO_TEST_CASE(testUnified)