ChimeraTK-DeviceAccess 03.25.00
Loading...
Searching...
No Matches
testAsyncRead.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
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;
8
9#include "Device.h"
10#include "DeviceAccessVersion.h"
11#include "DeviceBackendImpl.h"
12#include "ReadAnyGroup.h"
13
14#include <boost/thread.hpp>
15
16#include <algorithm>
17#include <atomic>
18#include <chrono>
19#include <future>
20#include <thread>
21
22using namespace boost::unit_test_framework;
23using namespace ChimeraTK;
24
25std::string cdd = "(AsyncTestDummy)";
26
27/**********************************************************************************************************************/
28
30 public:
32
33 std::string readDeviceInfo() override { return "AsyncTestDummy"; }
34
35 RegisterCatalogue getRegisterCatalogue() const override { throw; }
36
37 static boost::shared_ptr<DeviceBackend> createInstance(std::string, std::map<std::string, std::string>) {
38 return boost::shared_ptr<DeviceBackend>(new AsyncTestDummy());
39 }
40
41 template<typename UserType>
42 class Accessor : public NDRegisterAccessor<UserType> {
43 public:
44 Accessor(AsyncTestDummy* backend, const RegisterPath& registerPathName, AccessModeFlags& flags)
45 : NDRegisterAccessor<UserType>(registerPathName, flags), _backend(backend) {
46 buffer_2D.resize(1);
47 buffer_2D[0].resize(1);
48 this->_readQueue = {3}; // this accessor is using a queue length of 3
50 }
51
52 void doReadTransferSynchronously() override {}
53
54 bool doWriteTransfer(ChimeraTK::VersionNumber) override { return false; }
55
57
59
60 void doPreRead(TransferType) override {}
61
62 void doPostRead(TransferType, bool hasNewData) override {
63 if constexpr(!std::is_same<UserType, Void>::value) { // Will not be used for Void-Type
65 if(!hasNewData) return;
66 buffer_2D[0][0] = _backend->registers.at(getName());
67 this->_versionNumber = {};
68 }
69 }
70
71 [[nodiscard]] bool isReadOnly() const override { return false; }
72 [[nodiscard]] bool isReadable() const override { return true; }
73 [[nodiscard]] bool isWriteable() const override { return true; }
74
75 std::vector<boost::shared_ptr<TransferElement>> getHardwareAccessingElements() override {
76 return {this->shared_from_this()};
77 }
78 std::list<boost::shared_ptr<TransferElement>> getInternalElements() override { return {}; }
79
80 size_t nPostReadCalled{0};
81
82 protected:
84 using NDRegisterAccessor<UserType>::getName;
85 using NDRegisterAccessor<UserType>::buffer_2D;
86 };
87
88 template<typename UserType>
89 boost::shared_ptr<NDRegisterAccessor<UserType>> getRegisterAccessor_impl(
90 const RegisterPath& registerPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags) {
91 assert(numberOfWords == 1);
92 assert(wordOffsetInRegister == 0);
93 (void)numberOfWords;
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());
98 return retval;
99 }
100
102
103 void open() override {
104 _opened = true;
105 _hasActiveException = false;
106 }
107
108 void close() override { _opened = false; }
109
110 std::map<std::string, cppext::future_queue<void>> notificationQueue;
111 std::map<std::string, size_t> registers;
112
113 void setExceptionImpl() noexcept override {
114 // FIXME !!!!
115 assert(false); // Wrong implementation. All notification queues must see an exception.
116 }
118};
119
120/**********************************************************************************************************************/
121
122struct Fixture {
125 "AsyncTestDummy", &AsyncTestDummy::createInstance, {}, CHIMERATK_DEVICEACCESS_VERSION);
127 }
128};
129static Fixture fixture;
130
131/**********************************************************************************************************************/
132/**********************************************************************************************************************/
133
134BOOST_AUTO_TEST_CASE(testAsyncRead) {
135 std::cout << "testAsyncRead" << std::endl;
136
138 device.open(cdd);
139 auto backend = boost::dynamic_pointer_cast<AsyncTestDummy>(BackendFactory::getInstance().createBackend(cdd));
140 BOOST_CHECK(backend != nullptr);
141
142 // obtain register accessor with integral type
143 auto accessor = device.getScalarRegisterAccessor<int>("REG", 0, {AccessMode::wait_for_new_data});
144
145 // simple reading through readAsync without actual need
146 backend->registers["/REG"] = 5;
147
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); // future not ready yet, i.e. read() not fninished.
151
152 backend->notificationQueue["/REG"].push(); // trigger transfer
153 waitForRead.wait(); // wait for the read to finish
154
155 BOOST_CHECK(accessor == 5);
156 BOOST_CHECK(backend->notificationQueue["/REG"].empty());
157
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); // future not ready yet, i.e. read() not fninished.
162
163 backend->notificationQueue["/REG"].push(); // trigger transfer
164 waitForRead.wait();
165
166 BOOST_CHECK(accessor == 6);
167 BOOST_CHECK(backend->notificationQueue["/REG"].empty());
168
169 device.close();
170}
171
172/**********************************************************************************************************************/
173
175 std::cout << "testReadAny" << std::endl;
176
178 device.open(cdd);
179 auto backend = boost::dynamic_pointer_cast<AsyncTestDummy>(BackendFactory::getInstance().createBackend(cdd));
180 BOOST_CHECK(backend != nullptr);
181
182 // obtain register accessor with integral type
183 auto a1 = device.getScalarRegisterAccessor<uint8_t>("a1", 0, {AccessMode::wait_for_new_data});
184 auto a2 = device.getScalarRegisterAccessor<int32_t>("a2", 0, {AccessMode::wait_for_new_data});
185 auto a3 = device.getScalarRegisterAccessor<int32_t>("a3", 0, {AccessMode::wait_for_new_data});
186 auto a4 = device.getScalarRegisterAccessor<int32_t>("a4", 0, {AccessMode::wait_for_new_data});
187
188 // initialise the buffers of the accessors
189 a1 = 1;
190 a2 = 2;
191 a3 = 3;
192 a4 = 4;
193
194 // initialise the dummy registers
195 backend->registers["/a1"] = 42;
196 backend->registers["/a2"] = 123;
197 backend->registers["/a3"] = 120;
198 backend->registers["/a4"] = 345;
199
200 // Create ReadAnyGroup
201 ReadAnyGroup group;
202 BOOST_TEST(a1.getReadAnyGroup() == nullptr);
203 group.add(a1);
204 BOOST_TEST(a1.getReadAnyGroup() == &group);
205 group.add(a2);
206 group.add(a3);
207 group.add(a4);
208 group.finalise();
209
211
212 // register 1
213 {
214 // launch the readAny in a background thread
215 std::atomic<bool> flag{false};
216 std::thread thread([&group, &flag, &id] {
217 id = group.readAny();
218 flag = true;
219 });
220
221 // check that it doesn't return too soon
222 usleep(100000);
223 BOOST_CHECK(flag == false);
224
225 // write register and check that readAny() completes
226 backend->notificationQueue["/a1"].push(); // trigger transfer
227 thread.join();
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());
233 }
234
235 // register 3
236 {
237 // launch the readAny in a background thread
238 std::atomic<bool> flag{false};
239 std::thread thread([&group, &flag, &id] {
240 id = group.readAny();
241 flag = true;
242 });
243
244 // check that it doesn't return too soon
245 usleep(100000);
246 BOOST_CHECK(flag == false);
247
248 // write register and check that readAny() completes
249 backend->notificationQueue["/a3"].push(); // trigger transfer
250 thread.join();
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());
256 }
257
258 // register 3 again
259 {
260 // launch the readAny in a background thread
261 std::atomic<bool> flag{false};
262 std::thread thread([&group, &flag, &id] {
263 id = group.readAny();
264 flag = true;
265 });
266
267 // check that it doesn't return too soon
268 usleep(100000);
269 BOOST_CHECK(flag == false);
270
271 // write register and check that readAny() completes
272 backend->registers["/a3"] = 121;
273 backend->notificationQueue["/a3"].push(); // trigger transfer
274 thread.join();
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());
280 }
281
282 // register 2
283 {
284 // launch the readAny in a background thread
285 std::atomic<bool> flag{false};
286 std::thread thread([&group, &flag, &id] {
287 id = group.readAny();
288 flag = true;
289 });
290
291 // check that it doesn't return too soon
292 usleep(100000);
293 BOOST_CHECK(flag == false);
294
295 // write register and check that readAny() completes
296 backend->notificationQueue["/a2"].push(); // trigger transfer
297 thread.join();
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());
303 }
304
305 // register 4
306 {
307 // launch the readAny in a background thread
308 std::atomic<bool> flag{false};
309 std::thread thread([&group, &flag, &id] {
310 id = group.readAny();
311 flag = true;
312 });
313
314 // check that it doesn't return too soon
315 usleep(100000);
316 BOOST_CHECK(flag == false);
317
318 // write register and check that readAny() completes
319 backend->notificationQueue["/a4"].push(); // trigger transfer
320 thread.join();
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());
326 }
327
328 // register 4 again
329 {
330 // launch the readAny in a background thread
331 std::atomic<bool> flag{false};
332 std::thread thread([&group, &flag, &id] {
333 id = group.readAny();
334 flag = true;
335 });
336
337 // check that it doesn't return too soon
338 usleep(100000);
339 BOOST_CHECK(flag == false);
340
341 // write register and check that readAny() completes
342 backend->notificationQueue["/a4"].push(); // trigger transfer
343 thread.join();
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());
349 }
350
351 // register 3 a 3rd time
352 {
353 // launch the readAny in a background thread
354 std::atomic<bool> flag{false};
355 std::thread thread([&group, &flag, &id] {
356 id = group.readAny();
357 flag = true;
358 });
359
360 // check that it doesn't return too soon
361 usleep(100000);
362 BOOST_CHECK(flag == false);
363
364 // write register and check that readAny() completes
365 backend->registers["/a3"] = 122;
366 backend->notificationQueue["/a3"].push(); // trigger transfer
367 thread.join();
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());
373 }
374
375 // register 1 and then register 2 (order should be guaranteed)
376 {
377 // write to register 1 and trigger transfer
378 backend->registers["/a1"] = 55;
379 backend->notificationQueue["/a1"].push(); // trigger transfer
380
381 // same with register 2
382 backend->registers["/a2"] = 66;
383 backend->notificationQueue["/a2"].push(); // trigger transfer
384
385 BOOST_CHECK_EQUAL((int)a1, 42);
386 BOOST_CHECK_EQUAL((int)a2, 123);
387
388 // no point to use a thread here
389 auto r = group.readAny();
390 BOOST_CHECK(a1.getId() == r);
391 BOOST_CHECK_EQUAL((int)a1, 55);
392 BOOST_CHECK_EQUAL((int)a2, 123);
393
394 r = group.readAny();
395 BOOST_CHECK(a2.getId() == r);
396 BOOST_CHECK(a1 == 55);
397 BOOST_CHECK(a2 == 66);
398 }
399
400 // registers in order: 4, 2, 3 and 1
401 {
402 // register 4 (see above for explanation)
403 backend->registers["/a4"] = 11;
404 backend->notificationQueue["/a4"].push(); // trigger transfer
405
406 // register 2
407 backend->registers["/a2"] = 22;
408 backend->notificationQueue["/a2"].push(); // trigger transfer
409
410 // register 3
411 backend->registers["/a3"] = 33;
412 backend->notificationQueue["/a3"].push(); // trigger transfer
413
414 // register 1
415 backend->registers["/a1"] = 44;
416 backend->notificationQueue["/a1"].push(); // trigger transfer
417
418 // no point to use a thread here
419 auto r = group.readAny();
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);
425
426 r = group.readAny();
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);
432
433 r = group.readAny();
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);
439
440 r = group.readAny();
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);
446 }
447
448 device.close();
449}
450
451/**********************************************************************************************************************/
452
453BOOST_AUTO_TEST_CASE(testReadAnyWithPoll) {
454 std::cout << "testReadAnyWithPoll" << std::endl;
455
457 device.open(cdd);
458 auto backend = boost::dynamic_pointer_cast<AsyncTestDummy>(BackendFactory::getInstance().createBackend(cdd));
459 BOOST_CHECK(backend != nullptr);
460
461 // obtain register accessor with integral type
462 auto a1 = device.getScalarRegisterAccessor<uint8_t>("a1", 0, {AccessMode::wait_for_new_data});
463 auto a2 = device.getScalarRegisterAccessor<int32_t>("a2", 0, {AccessMode::wait_for_new_data});
464 auto a3 = device.getScalarRegisterAccessor<int32_t>("a3");
465 auto a4 = device.getScalarRegisterAccessor<int32_t>("a4");
466
467 // initialise the buffers of the accessors
468 a1 = 1;
469 a2 = 2;
470 a3 = 3;
471 a4 = 4;
472
473 // initialise the dummy registers
474 backend->registers["/a1"] = 42;
475 backend->registers["/a2"] = 123;
476 backend->registers["/a3"] = 120;
477 backend->registers["/a4"] = 345;
478
479 // Create ReadAnyGroup
480 ReadAnyGroup group;
481 group.add(a1);
482 group.add(a2);
483 group.add(a3);
484 group.add(a4);
485 group.finalise();
486
488
489 // register 1
490 {
491 // launch the readAny in a background thread
492 std::atomic<bool> flag{false};
493 std::thread thread([&group, &flag, &id] {
494 id = group.readAny();
495 flag = true;
496 });
497
498 // check that it doesn't return too soon
499 usleep(100000);
500 BOOST_CHECK(flag == false);
501
502 // write register and check that readAny() completes
503 backend->notificationQueue["/a1"].push(); // trigger transfer
504 thread.join();
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());
510 }
511
512 backend->registers["/a3"] = 121;
513 backend->registers["/a4"] = 346;
514
515 // register 2
516 {
517 // launch the readAny in a background thread
518 std::atomic<bool> flag{false};
519 std::thread thread([&group, &flag, &id] {
520 id = group.readAny();
521 flag = true;
522 });
523
524 // check that it doesn't return too soon
525 usleep(100000);
526 BOOST_CHECK(flag == false);
527
528 // write register and check that readAny() completes
529 backend->notificationQueue["/a2"].push(); // trigger transfer
530 thread.join();
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());
536 }
537
538 device.close();
539}
540
541/**********************************************************************************************************************/
542
544 std::cout << "testWaitAny" << std::endl;
545
547 device.open(cdd);
548 auto backend = boost::dynamic_pointer_cast<AsyncTestDummy>(BackendFactory::getInstance().createBackend(cdd));
549 BOOST_CHECK(backend != nullptr);
550
551 // obtain register accessor with integral type
552 auto a1 = device.getScalarRegisterAccessor<uint8_t>("a1", 0, {AccessMode::wait_for_new_data});
553 auto a2 = device.getScalarRegisterAccessor<int32_t>("a2", 0, {AccessMode::wait_for_new_data});
554 auto a3 = device.getScalarRegisterAccessor<int32_t>("a3");
555 auto a4 = device.getScalarRegisterAccessor<int32_t>("a4");
556
557 // initialise the buffers of the accessors
558 a1 = 1;
559 a2 = 2;
560 a3 = 3;
561 a4 = 4;
562
563 // initialise the dummy registers
564 backend->registers["/a1"] = 42;
565 backend->registers["/a2"] = 123;
566 backend->registers["/a3"] = 120;
567 backend->registers["/a4"] = 345;
568
569 // Create ReadAnyGroup
570 ReadAnyGroup group;
571 group.add(a1);
572 group.add(a2);
573 group.add(a3);
574 group.add(a4);
575 group.finalise();
576
577 ReadAnyGroup::Notification notification;
578
579 // register 1
580 {
581 // launch the readAny in a background thread
582 std::atomic<bool> flag{false};
583 std::thread thread([&group, &flag, &notification] {
584 notification = group.waitAny();
585 flag = true;
586 });
587
588 // check that it doesn't return too soon
589 usleep(100000);
590 BOOST_CHECK(flag == false);
591
592 // write register and check that readAny() completes
593 backend->notificationQueue["/a1"].push(); // trigger transfer
594 thread.join();
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);
605 group.processPolled();
606 BOOST_CHECK(a1 == 42);
607 BOOST_CHECK(a2 == 2);
608 BOOST_CHECK(a3 == 120);
609 BOOST_CHECK(a4 == 345);
610 }
611
612 backend->registers["/a3"] = 121;
613 backend->registers["/a4"] = 346;
614
615 // register 2
616 {
617 // launch the readAny in a background thread
618 std::atomic<bool> flag{false};
619 std::thread thread([&group, &flag, &notification] {
620 notification = group.waitAny();
621 flag = true;
622 });
623
624 // check that it doesn't return too soon
625 usleep(100000);
626 BOOST_CHECK(flag == false);
627
628 // write register and check that readAny() completes
629 backend->notificationQueue["/a2"].push(); // trigger transfer
630 thread.join();
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());
637 group.processPolled();
638 BOOST_CHECK(a1 == 42);
639 BOOST_CHECK(a2 == 123);
640 BOOST_CHECK(a3 == 121);
641 BOOST_CHECK(a4 == 346);
642 }
643
644 device.close();
645}
646
647/**********************************************************************************************************************/
648
649BOOST_AUTO_TEST_CASE(testReadAnyException) {
650 std::cout << "testReadAnyException" << std::endl;
651
653 device.open(cdd);
654 auto backend = boost::dynamic_pointer_cast<AsyncTestDummy>(BackendFactory::getInstance().createBackend(cdd));
655 BOOST_CHECK(backend != nullptr);
656
657 // obtain register accessor with integral type
658 auto a1 = device.getScalarRegisterAccessor<uint8_t>("a1", 0, {AccessMode::wait_for_new_data});
659 auto a2 = device.getScalarRegisterAccessor<int32_t>("a2", 0, {AccessMode::wait_for_new_data});
660 auto a3 = device.getScalarRegisterAccessor<int32_t>("a3", 0, {AccessMode::wait_for_new_data});
661 auto a4 = device.getScalarRegisterAccessor<int32_t>("a4", 0, {AccessMode::wait_for_new_data});
662 auto a1_casted = boost::dynamic_pointer_cast<AsyncTestDummy::Accessor<uint8_t>>(a1.getHighLevelImplElement());
663 assert(a1_casted);
664
665 // initialise the buffers of the accessors
666 a1 = 1;
667 a2 = 2;
668 a3 = 3;
669 a4 = 4;
670
671 // initialise the dummy registers
672 backend->registers["/a1"] = 42;
673 backend->registers["/a2"] = 123;
674 backend->registers["/a3"] = 120;
675 backend->registers["/a4"] = 345;
676
677 // Create ReadAnyGroup
678 ReadAnyGroup group;
679 group.add(a1);
680 group.add(a2);
681 group.add(a3);
682 group.add(a4);
683 group.finalise();
684
685 // ChimeraTK::runtime_error
686 {
687 auto nPostReadCalledReference = a1_casted->nPostReadCalled;
688 auto versionNumReference = a1.getVersionNumber();
689
690 // launch the readAny in a background thread
691 bool exceptionFound{false};
692 std::thread thread([&group, &exceptionFound] {
693 try {
694 group.readAny();
695 }
697 exceptionFound = true;
698 }
699 });
700
701 // put exception to queue
702 try {
703 throw ChimeraTK::runtime_error("Test exception");
704 }
705 catch(...) {
706 backend->notificationQueue["/a1"].push_exception(std::current_exception()); // trigger transfer
707 }
708 thread.join();
709 BOOST_TEST(exceptionFound == true);
710 BOOST_TEST(a1_casted->nPostReadCalled == nPostReadCalledReference + 1);
711 BOOST_TEST(a1.getVersionNumber() == versionNumReference);
712 }
713
714 // boost::thread_interrupted
715 {
716 auto nPostReadCalledReference = a1_casted->nPostReadCalled;
717 auto versionNumReference = a1.getVersionNumber();
718
719 // launch the readAny in a background thread
720 bool exceptionFound{false};
721 std::thread thread([&group, &exceptionFound] {
722 try {
723 group.readAny();
724 }
725 catch(boost::thread_interrupted&) {
726 exceptionFound = true;
727 }
728 });
729
730 // put exception to queue
731 backend->notificationQueue["/a1"].push_exception(
732 std::make_exception_ptr(boost::thread_interrupted())); // trigger transfer
733
734 thread.join();
735 BOOST_TEST(exceptionFound == true);
736 BOOST_TEST(a1_casted->nPostReadCalled == nPostReadCalledReference + 1);
737 BOOST_TEST(a1.getVersionNumber() == versionNumReference);
738 }
739
740 device.close();
741}
742
743/**********************************************************************************************************************/
744
745BOOST_AUTO_TEST_CASE(testReadAnyInvalid) {
746 std::cout << "testReadAnyInvalid" << std::endl;
747
749 device.open(cdd);
750 auto backend = boost::dynamic_pointer_cast<AsyncTestDummy>(BackendFactory::getInstance().createBackend(cdd));
751 BOOST_CHECK(backend != nullptr);
752
753 // obtain register accessor with integral type
754 auto a1 = device.getScalarRegisterAccessor<uint8_t>("a1", 0, {AccessMode::wait_for_new_data});
755
756 // Create ReadAnyGroup
757 ReadAnyGroup group{a1};
758
759 {
760 // direct read on accessor in ReadAnyGroup is not allowed and should issue exception.
761 BOOST_CHECK_THROW(a1.read(), ChimeraTK::logic_error);
762 }
763
764 device.close();
765}
766
767/**********************************************************************************************************************/
#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 &registerPathName, 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 &registerPathName, 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.
Definition AccessMode.h:48
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.
Definition Device.h:39
void close()
Close the device.
Definition Device.cc:66
ScalarRegisterAccessor< UserType > getScalarRegisterAccessor(const RegisterPath &registerPathName, size_t wordOffsetInRegister=0, const AccessModeFlags &flags=AccessModeFlags({})) const
Get a ScalarRegisterObject object for the given register.
Definition Device.h:266
void open(std::string const &aliasName)
Open a device by the given alias name from the DMAP file.
Definition Device.cc:58
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.
Definition Exception.h:51
Exception thrown when a runtime error has occured.
Definition Exception.h:18
TransferType
Used to indicate the applicable operation on a Transferelement.
BOOST_AUTO_TEST_CASE(testAsyncRead)
std::string cdd
ctk::Device device