4#define BOOST_TEST_DYN_LINK
5#define BOOST_TEST_MODULE AsyncReadTest
6#include <boost/test/unit_test.hpp>
7using namespace boost::unit_test_framework;
10#include "DeviceAccessVersion.h"
14#include <boost/thread.hpp>
22using namespace boost::unit_test_framework;
25std::string
cdd =
"(AsyncTestDummy)";
37 static boost::shared_ptr<DeviceBackend>
createInstance(std::string, std::map<std::string, std::string>) {
41 template<
typename UserType>
63 if constexpr(!std::is_same<UserType, Void>::value) {
65 if(!hasNewData)
return;
71 [[nodiscard]]
bool isReadOnly()
const override {
return false; }
72 [[nodiscard]]
bool isReadable()
const override {
return true; }
73 [[nodiscard]]
bool isWriteable()
const override {
return true; }
76 return {this->shared_from_this()};
88 template<
typename UserType>
91 assert(numberOfWords == 1);
92 assert(wordOffsetInRegister == 0);
94 (void)wordOffsetInRegister;
95 boost::shared_ptr<NDRegisterAccessor<UserType>> retval =
96 boost::make_shared<Accessor<UserType>>(
this, registerPathName, flags);
97 retval->setExceptionBackend(shared_from_this());
135 std::cout <<
"testAsyncRead" << std::endl;
140 BOOST_CHECK(backend !=
nullptr);
146 backend->registers[
"/REG"] = 5;
148 auto waitForRead = std::async(std::launch::async, [&accessor] { accessor.read(); });
149 auto waitStatus = waitForRead.wait_for(std::chrono::seconds(1));
150 BOOST_CHECK(waitStatus != std::future_status::ready);
152 backend->notificationQueue[
"/REG"].push();
155 BOOST_CHECK(accessor == 5);
156 BOOST_CHECK(backend->notificationQueue[
"/REG"].empty());
158 backend->registers[
"/REG"] = 6;
159 waitForRead = std::async(std::launch::async, [&accessor] { accessor.read(); });
160 waitStatus = waitForRead.wait_for(std::chrono::seconds(1));
161 BOOST_CHECK(waitStatus != std::future_status::ready);
163 backend->notificationQueue[
"/REG"].push();
166 BOOST_CHECK(accessor == 6);
167 BOOST_CHECK(backend->notificationQueue[
"/REG"].empty());
175 std::cout <<
"testReadAny" << std::endl;
180 BOOST_CHECK(backend !=
nullptr);
195 backend->registers[
"/a1"] = 42;
196 backend->registers[
"/a2"] = 123;
197 backend->registers[
"/a3"] = 120;
198 backend->registers[
"/a4"] = 345;
202 BOOST_TEST(a1.getReadAnyGroup() ==
nullptr);
204 BOOST_TEST(a1.getReadAnyGroup() == &group);
215 std::atomic<bool> flag{
false};
216 std::thread thread([&group, &flag, &
id] {
223 BOOST_CHECK(flag ==
false);
226 backend->notificationQueue[
"/a1"].push();
228 BOOST_CHECK(a1 == 42);
229 BOOST_CHECK(a2 == 2);
230 BOOST_CHECK(a3 == 3);
231 BOOST_CHECK(a4 == 4);
232 BOOST_CHECK(
id == a1.getId());
238 std::atomic<bool> flag{
false};
239 std::thread thread([&group, &flag, &
id] {
246 BOOST_CHECK(flag ==
false);
249 backend->notificationQueue[
"/a3"].push();
251 BOOST_CHECK(a1 == 42);
252 BOOST_CHECK(a2 == 2);
253 BOOST_CHECK(a3 == 120);
254 BOOST_CHECK(a4 == 4);
255 BOOST_CHECK(
id == a3.getId());
261 std::atomic<bool> flag{
false};
262 std::thread thread([&group, &flag, &
id] {
269 BOOST_CHECK(flag ==
false);
272 backend->registers[
"/a3"] = 121;
273 backend->notificationQueue[
"/a3"].push();
275 BOOST_CHECK(a1 == 42);
276 BOOST_CHECK(a2 == 2);
277 BOOST_CHECK(a3 == 121);
278 BOOST_CHECK(a4 == 4);
279 BOOST_CHECK(
id == a3.getId());
285 std::atomic<bool> flag{
false};
286 std::thread thread([&group, &flag, &
id] {
293 BOOST_CHECK(flag ==
false);
296 backend->notificationQueue[
"/a2"].push();
298 BOOST_CHECK(a1 == 42);
299 BOOST_CHECK(a2 == 123);
300 BOOST_CHECK(a3 == 121);
301 BOOST_CHECK(a4 == 4);
302 BOOST_CHECK(
id == a2.getId());
308 std::atomic<bool> flag{
false};
309 std::thread thread([&group, &flag, &
id] {
316 BOOST_CHECK(flag ==
false);
319 backend->notificationQueue[
"/a4"].push();
321 BOOST_CHECK(a1 == 42);
322 BOOST_CHECK(a2 == 123);
323 BOOST_CHECK(a3 == 121);
324 BOOST_CHECK(a4 == 345);
325 BOOST_CHECK(
id == a4.getId());
331 std::atomic<bool> flag{
false};
332 std::thread thread([&group, &flag, &
id] {
339 BOOST_CHECK(flag ==
false);
342 backend->notificationQueue[
"/a4"].push();
344 BOOST_CHECK(a1 == 42);
345 BOOST_CHECK(a2 == 123);
346 BOOST_CHECK(a3 == 121);
347 BOOST_CHECK(a4 == 345);
348 BOOST_CHECK(
id == a4.getId());
354 std::atomic<bool> flag{
false};
355 std::thread thread([&group, &flag, &
id] {
362 BOOST_CHECK(flag ==
false);
365 backend->registers[
"/a3"] = 122;
366 backend->notificationQueue[
"/a3"].push();
368 BOOST_CHECK(a1 == 42);
369 BOOST_CHECK(a2 == 123);
370 BOOST_CHECK(a3 == 122);
371 BOOST_CHECK(a4 == 345);
372 BOOST_CHECK(
id == a3.getId());
378 backend->registers[
"/a1"] = 55;
379 backend->notificationQueue[
"/a1"].push();
382 backend->registers[
"/a2"] = 66;
383 backend->notificationQueue[
"/a2"].push();
385 BOOST_CHECK_EQUAL((
int)a1, 42);
386 BOOST_CHECK_EQUAL((
int)a2, 123);
390 BOOST_CHECK(a1.getId() == r);
391 BOOST_CHECK_EQUAL((
int)a1, 55);
392 BOOST_CHECK_EQUAL((
int)a2, 123);
395 BOOST_CHECK(a2.getId() == r);
396 BOOST_CHECK(a1 == 55);
397 BOOST_CHECK(a2 == 66);
403 backend->registers[
"/a4"] = 11;
404 backend->notificationQueue[
"/a4"].push();
407 backend->registers[
"/a2"] = 22;
408 backend->notificationQueue[
"/a2"].push();
411 backend->registers[
"/a3"] = 33;
412 backend->notificationQueue[
"/a3"].push();
415 backend->registers[
"/a1"] = 44;
416 backend->notificationQueue[
"/a1"].push();
420 BOOST_CHECK(a4.getId() == r);
421 BOOST_CHECK(a1 == 55);
422 BOOST_CHECK(a2 == 66);
423 BOOST_CHECK(a3 == 122);
424 BOOST_CHECK(a4 == 11);
427 BOOST_CHECK(a2.getId() == r);
428 BOOST_CHECK(a1 == 55);
429 BOOST_CHECK(a2 == 22);
430 BOOST_CHECK(a3 == 122);
431 BOOST_CHECK(a4 == 11);
434 BOOST_CHECK(a3.getId() == r);
435 BOOST_CHECK(a1 == 55);
436 BOOST_CHECK(a2 == 22);
437 BOOST_CHECK(a3 == 33);
438 BOOST_CHECK(a4 == 11);
441 BOOST_CHECK(a1.getId() == r);
442 BOOST_CHECK(a1 == 44);
443 BOOST_CHECK(a2 == 22);
444 BOOST_CHECK(a3 == 33);
445 BOOST_CHECK(a4 == 11);
454 std::cout <<
"testReadAnyWithPoll" << std::endl;
459 BOOST_CHECK(backend !=
nullptr);
474 backend->registers[
"/a1"] = 42;
475 backend->registers[
"/a2"] = 123;
476 backend->registers[
"/a3"] = 120;
477 backend->registers[
"/a4"] = 345;
492 std::atomic<bool> flag{
false};
493 std::thread thread([&group, &flag, &
id] {
500 BOOST_CHECK(flag ==
false);
503 backend->notificationQueue[
"/a1"].push();
505 BOOST_CHECK(a1 == 42);
506 BOOST_CHECK(a2 == 2);
507 BOOST_CHECK(a3 == 120);
508 BOOST_CHECK(a4 == 345);
509 BOOST_CHECK(
id == a1.getId());
512 backend->registers[
"/a3"] = 121;
513 backend->registers[
"/a4"] = 346;
518 std::atomic<bool> flag{
false};
519 std::thread thread([&group, &flag, &
id] {
526 BOOST_CHECK(flag ==
false);
529 backend->notificationQueue[
"/a2"].push();
531 BOOST_CHECK(a1 == 42);
532 BOOST_CHECK(a2 == 123);
533 BOOST_CHECK(a3 == 121);
534 BOOST_CHECK(a4 == 346);
535 BOOST_CHECK(
id == a2.getId());
544 std::cout <<
"testWaitAny" << std::endl;
549 BOOST_CHECK(backend !=
nullptr);
564 backend->registers[
"/a1"] = 42;
565 backend->registers[
"/a2"] = 123;
566 backend->registers[
"/a3"] = 120;
567 backend->registers[
"/a4"] = 345;
582 std::atomic<bool> flag{
false};
583 std::thread thread([&group, &flag, ¬ification] {
584 notification = group.
waitAny();
590 BOOST_CHECK(flag ==
false);
593 backend->notificationQueue[
"/a1"].push();
595 BOOST_CHECK(notification.
getId() == a1.getId());
596 BOOST_CHECK(a1 == 1);
597 BOOST_CHECK(a2 == 2);
598 BOOST_CHECK(a3 == 3);
599 BOOST_CHECK(a4 == 4);
600 BOOST_CHECK(notification.
accept());
601 BOOST_CHECK(a1 == 42);
602 BOOST_CHECK(a2 == 2);
603 BOOST_CHECK(a3 == 3);
604 BOOST_CHECK(a4 == 4);
606 BOOST_CHECK(a1 == 42);
607 BOOST_CHECK(a2 == 2);
608 BOOST_CHECK(a3 == 120);
609 BOOST_CHECK(a4 == 345);
612 backend->registers[
"/a3"] = 121;
613 backend->registers[
"/a4"] = 346;
618 std::atomic<bool> flag{
false};
619 std::thread thread([&group, &flag, ¬ification] {
620 notification = group.
waitAny();
626 BOOST_CHECK(flag ==
false);
629 backend->notificationQueue[
"/a2"].push();
631 BOOST_CHECK(notification.
getId() == a2.getId());
632 BOOST_CHECK(a1 == 42);
633 BOOST_CHECK(a2 == 2);
634 BOOST_CHECK(a3 == 120);
635 BOOST_CHECK(a4 == 345);
636 BOOST_CHECK(notification.
accept());
638 BOOST_CHECK(a1 == 42);
639 BOOST_CHECK(a2 == 123);
640 BOOST_CHECK(a3 == 121);
641 BOOST_CHECK(a4 == 346);
650 std::cout <<
"testReadAnyException" << std::endl;
655 BOOST_CHECK(backend !=
nullptr);
662 auto a1_casted = boost::dynamic_pointer_cast<AsyncTestDummy::Accessor<uint8_t>>(a1.getHighLevelImplElement());
672 backend->registers[
"/a1"] = 42;
673 backend->registers[
"/a2"] = 123;
674 backend->registers[
"/a3"] = 120;
675 backend->registers[
"/a4"] = 345;
687 auto nPostReadCalledReference = a1_casted->nPostReadCalled;
688 auto versionNumReference = a1.getVersionNumber();
691 bool exceptionFound{
false};
692 std::thread thread([&group, &exceptionFound] {
697 exceptionFound =
true;
706 backend->notificationQueue[
"/a1"].push_exception(std::current_exception());
709 BOOST_TEST(exceptionFound ==
true);
710 BOOST_TEST(a1_casted->nPostReadCalled == nPostReadCalledReference + 1);
711 BOOST_TEST(a1.getVersionNumber() == versionNumReference);
716 auto nPostReadCalledReference = a1_casted->nPostReadCalled;
717 auto versionNumReference = a1.getVersionNumber();
720 bool exceptionFound{
false};
721 std::thread thread([&group, &exceptionFound] {
725 catch(boost::thread_interrupted&) {
726 exceptionFound =
true;
731 backend->notificationQueue[
"/a1"].push_exception(
732 std::make_exception_ptr(boost::thread_interrupted()));
735 BOOST_TEST(exceptionFound ==
true);
736 BOOST_TEST(a1_casted->nPostReadCalled == nPostReadCalledReference + 1);
737 BOOST_TEST(a1.getVersionNumber() == versionNumReference);
746 std::cout <<
"testReadAnyInvalid" << std::endl;
751 BOOST_CHECK(backend !=
nullptr);
#define FILL_VIRTUAL_FUNCTION_TEMPLATE_VTABLE(functionName)
Fill the vtable of a virtual function template defined with DEFINE_VIRTUAL_FUNCTION_TEMPLATE.
void doPreWrite(TransferType, VersionNumber) override
Backend specific implementation of preWrite().
bool doWriteTransfer(ChimeraTK::VersionNumber) override
Implementation version of writeTransfer().
bool isReadOnly() const override
Check if transfer element is read only, i.e.
bool isWriteable() const override
Check if transfer element is writeable.
AsyncTestDummy * _backend
void doReadTransferSynchronously() override
Implementation version of readTransfer() for synchronous reads.
std::list< boost::shared_ptr< TransferElement > > getInternalElements() override
Obtain the full list of TransferElements internally used by this TransferElement.
bool isReadable() const override
Check if transfer element is readable.
void doPostWrite(TransferType, VersionNumber) override
Backend specific implementation of postWrite().
void doPreRead(TransferType) override
Backend specific implementation of preRead().
std::vector< boost::shared_ptr< TransferElement > > getHardwareAccessingElements() override
Obtain the underlying TransferElements with actual hardware access.
void doPostRead(TransferType, bool hasNewData) override
Backend specific implementation of postRead().
Accessor(AsyncTestDummy *backend, const RegisterPath ®isterPathName, AccessModeFlags &flags)
RegisterCatalogue getRegisterCatalogue() const override
Return the register catalogue with detailed information on all registers.
DEFINE_VIRTUAL_FUNCTION_TEMPLATE_VTABLE_FILLER(AsyncTestDummy, getRegisterAccessor_impl, 4)
void open() override
Open the device.
void close() override
Close the device.
boost::shared_ptr< NDRegisterAccessor< UserType > > getRegisterAccessor_impl(const RegisterPath ®isterPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
static boost::shared_ptr< DeviceBackend > createInstance(std::string, std::map< std::string, std::string >)
std::map< std::string, size_t > registers
void setExceptionImpl() noexcept override
Function to be (optionally) implemented by backends if additional actions are needed when switching t...
std::map< std::string, cppext::future_queue< void > > notificationQueue
std::string readDeviceInfo() override
Return a device information string containing hardware details like the firmware version number or th...
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.
void setDMapFilePath(std::string dMapFilePath)
This function sets the _DMapFilePath.
DeviceBackendImpl implements some basic functionality which should be available for all backends.
std::atomic< bool > _opened
flag if backend is opened
Class allows to read/write registers from device.
void close()
Close the device.
ScalarRegisterAccessor< UserType > getScalarRegisterAccessor(const RegisterPath ®isterPathName, size_t wordOffsetInRegister=0, const AccessModeFlags &flags=AccessModeFlags({})) const
Get a ScalarRegisterObject object for the given register.
void open(std::string const &aliasName)
Open a device by the given alias name from the DMAP file.
N-dimensional register accessor.
std::vector< std::vector< UserType > > buffer_2D
Buffer of converted data elements.
Notification object returned by waitAny().
bool accept()
Accept the notification.
TransferElementID getId()
Return the ID of the transfer element for which this notification has been generated.
Group several registers (= TransferElement) to allow waiting for an update of any of the registers.
Notification waitAny()
Wait until one of the elements received an update notification, but do not actually process the updat...
TransferElementID readAny()
Wait until one of the elements in this group has received an update.
void add(TransferElementAbstractor &element)
Add register to group.
void processPolled()
Process polled transfer elements (update them if new values are available).
void finalise()
Finalise the group.
Catalogue of register information.
Class to store a register path name.
VersionNumber _versionNumber
The version number of the last successful transfer.
cppext::future_queue< void > _readQueue
The queue for asynchronous read transfers.
const std::string & getName() const
Returns the name that identifies the process variable.
Simple class holding a unique ID for a TransferElement.
Class for generating and holding version numbers without exposing a numeric representation.
Exception thrown when a logic error has occured.
Exception thrown when a runtime error has occured.
TransferType
Used to indicate the applicable operation on a Transferelement.
BOOST_AUTO_TEST_CASE(testAsyncRead)