ChimeraTK-DeviceAccess 03.25.00
Loading...
Searching...
No Matches
testTransferGroup.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 TransferGroupTest
6#include <boost/test/unit_test.hpp>
7using namespace boost::unit_test_framework;
8
10#include "Device.h"
15#include "TransferGroup.h"
16
17using namespace boost::unit_test_framework;
18using namespace ChimeraTK;
19
20boost::shared_ptr<TransferElementTestAccessor<int32_t>> makeTETA(AccessModeFlags flags = {}) {
21 return boost::make_shared<TransferElementTestAccessor<int32_t>>(flags);
22}
23
24/**********************************************************************************************************************/
25/**********************************************************************************************************************/
27/**********************************************************************************************************************/
28/**********************************************************************************************************************/
29
34BOOST_AUTO_TEST_CASE(test_B_12_1_3) {
35 std::cout << "test_B_12_1_3" << std::endl;
36 std::map<TransferElementID, bool> foundIds;
37
38 auto A = makeTETA();
39 auto B = makeTETA();
40
41 for(size_t i = 0; i < 3; ++i) A->_internalElements.push_back(makeTETA());
42 for(size_t i = 0; i < 4; ++i) B->_internalElements.push_back(makeTETA());
43
44 TransferGroup group;
45
46 A->resetCounters();
47 group.addAccessor(A);
48 foundIds.clear();
49 for(auto& id : A->_listReplacementElements) foundIds[id] = true;
50 for(auto& e : A->_internalElements) BOOST_CHECK(foundIds[e->getId()] == true);
51
52 A->resetCounters();
53 B->resetCounters();
54 group.addAccessor(B);
55 foundIds.clear();
56 for(auto& id : A->_listReplacementElements) foundIds[id] = true;
57 for(auto& e : A->_internalElements) BOOST_CHECK(foundIds[e->getId()] == true);
58 for(auto& e : B->_internalElements) BOOST_CHECK(foundIds[e->getId()] == true);
59 foundIds.clear();
60 for(auto& id : B->_listReplacementElements) foundIds[id] = true;
61 for(auto& e : A->_internalElements) BOOST_CHECK(foundIds[e->getId()] == true);
62 for(auto& e : B->_internalElements) BOOST_CHECK(foundIds[e->getId()] == true);
63}
64
65/**********************************************************************************************************************/
66
72 std::cout << "test_B_12_3" << std::endl;
73 auto A = makeTETA();
74 TransferGroup group;
75 group.addAccessor(A);
76 BOOST_CHECK_THROW(A->read(), ChimeraTK::logic_error);
77 BOOST_CHECK_THROW(A->readNonBlocking(), ChimeraTK::logic_error);
78 BOOST_CHECK_THROW(A->readLatest(), ChimeraTK::logic_error);
79 BOOST_CHECK_THROW(A->write(), ChimeraTK::logic_error);
80 BOOST_CHECK_THROW(A->writeDestructively(), ChimeraTK::logic_error);
81}
82
83/**********************************************************************************************************************/
84
90 std::cout << "test_B_12_4" << std::endl;
91
92 // situation: all accessors are read-write
93 {
94 auto A = makeTETA();
95 auto B = makeTETA();
96 TransferGroup group;
97 group.addAccessor(A);
98 group.addAccessor(B);
99 BOOST_CHECK(group.isReadOnly() == false);
100 }
101
102 // situation: accessor read-only while adding
103 {
104 auto A = makeTETA();
105 auto B = makeTETA();
106 B->_writeable = false;
107 TransferGroup group;
108 group.addAccessor(A);
109 BOOST_CHECK(group.isReadOnly() == false);
110 group.addAccessor(B);
111 BOOST_CHECK(group.isReadOnly() == true);
112 }
113
114 // situation: accessor becomes read-only only later (at runtime error)
115 {
116 auto A = makeTETA();
117 auto B = makeTETA();
118 TransferGroup group;
119 group.addAccessor(A);
120 group.addAccessor(B);
121 B->_throwRuntimeErrInTransfer = true;
122 BOOST_CHECK_THROW(group.read(), ChimeraTK::runtime_error); // no check intended, just catch
123 B->_writeable = false;
124 B->_throwRuntimeErrInTransfer = false;
125 BOOST_CHECK(group.isReadOnly() == true);
126 }
127}
128
129/**********************************************************************************************************************/
130
136 std::cout << "test_B_12_5" << std::endl;
137 auto A = makeTETA();
138 TransferGroup group;
139 group.addAccessor(A);
140 BOOST_CHECK_THROW(group.addAccessor(A), ChimeraTK::logic_error);
141}
142
143/**********************************************************************************************************************/
144
150 std::cout << "test_B_12_6" << std::endl;
151 auto A = makeTETA();
152 TransferGroup group1;
153 TransferGroup group2;
154 group1.addAccessor(A);
155 BOOST_CHECK_THROW(group2.addAccessor(A), ChimeraTK::logic_error);
156}
157
158/**********************************************************************************************************************/
159
165 std::cout << "test_B_12_7" << std::endl;
166 auto A = makeTETA({AccessMode::wait_for_new_data});
167 TransferGroup group;
168 BOOST_CHECK_THROW(group.addAccessor(A), ChimeraTK::logic_error);
169}
170
171/**********************************************************************************************************************/
172
178 std::cout << "test_B_12_8" << std::endl;
179 auto A = makeTETA();
180 auto B = makeTETA();
181 A->_listMayReplaceElements.insert(B->getId());
184
185 TransferGroup group;
186 group.addAccessor(a);
187 group.addAccessor(b);
188
189 BOOST_CHECK(b.getHighLevelImplElement() != B);
190
191 A->_setPostReadData = 42;
192 group.read();
193 BOOST_CHECK_EQUAL(a[0][0], 42);
194 BOOST_CHECK_EQUAL(b[0][0], 42);
195}
196
197/**********************************************************************************************************************/
198
199// helper macros for next test
200// we use macros so line numbers make more sens if checks fail
201#define CHECK_COUNTERS_HIGH_LEVEL(te, isWrite, expectedOrderPreMax, expectedOrderPostMin, expectTransfer) \
202 if(!isWrite) { \
203 BOOST_CHECK_EQUAL(te->_preRead_counter, 1); \
204 BOOST_CHECK_EQUAL(te->_preWrite_counter, 0); \
205 BOOST_CHECK_EQUAL(te->_postRead_counter, 1); \
206 BOOST_CHECK_EQUAL(te->_postWrite_counter, 0); \
207 if(expectTransfer) { \
208 BOOST_CHECK_EQUAL(te->_readTransfer_counter, 1); \
209 } \
210 } \
211 else { \
212 BOOST_CHECK_EQUAL(te->_preRead_counter, 0); \
213 BOOST_CHECK_EQUAL(te->_preWrite_counter, 1); \
214 BOOST_CHECK_EQUAL(te->_postRead_counter, 0); \
215 BOOST_CHECK_EQUAL(te->_postWrite_counter, 1); \
216 if(expectTransfer) { \
217 BOOST_CHECK_EQUAL(te->_writeTransfer_counter, 1); \
218 } \
219 } \
220 if(!expectTransfer) { \
221 BOOST_CHECK_EQUAL(te->_readTransfer_counter, 0); \
222 BOOST_CHECK_EQUAL(te->_writeTransfer_counter, 0); \
223 } \
224 \
225 BOOST_CHECK(te->_preIndex <= expectedOrderPreMax); \
226 BOOST_CHECK(te->_postIndex >= expectedOrderPostMin)
227
228#define CHECK_COUNTERS_MID_LEVEL(te) \
229 BOOST_CHECK_EQUAL(te->_preRead_counter, 0); /* our test accessor does not delegate this...*/ \
230 BOOST_CHECK_EQUAL(te->_preWrite_counter, 0); \
231 BOOST_CHECK_EQUAL(te->_readTransfer_counter, 0); \
232 BOOST_CHECK_EQUAL(te->_writeTransfer_counter, 0); \
233 BOOST_CHECK_EQUAL(te->_postRead_counter, 0); /* our test accessor does not delegate this... */ \
234 BOOST_CHECK_EQUAL(te->_postWrite_counter, 0)
235
236#define CHECK_COUNTERS_LOW_LEVEL(te, isWrite, expectedOrderMin, expectedOrderMax) \
237 BOOST_CHECK_EQUAL(te->_preRead_counter, 0); /* our test accessor does not delegate this... */ \
238 BOOST_CHECK_EQUAL(te->_preWrite_counter, 0); \
239 if(!isWrite) { \
240 BOOST_CHECK_EQUAL(te->_readTransfer_counter, 1); \
241 BOOST_CHECK_EQUAL(te->_writeTransfer_counter, 0); \
242 } \
243 else { \
244 BOOST_CHECK_EQUAL(te->_readTransfer_counter, 0); \
245 BOOST_CHECK_EQUAL(te->_writeTransfer_counter, 1); \
246 } \
247 BOOST_CHECK_EQUAL(te->_postRead_counter, 0); /* our test accessor does not delegate this... */ \
248 BOOST_CHECK_EQUAL(te->_postWrite_counter, 0); \
249 \
250 BOOST_CHECK(te->_transferIndex >= expectedOrderMin); \
251 BOOST_CHECK(te->_transferIndex <= expectedOrderMax)
252
253/**********************************************************************************************************************/
254
260 std::cout << "test_B_12_9" << std::endl;
261 auto A = makeTETA();
262 auto B = makeTETA();
263
264 // A has just one low-level transfer element
265 for(size_t i = 0; i < 1; ++i) {
266 A->_internalElements.push_back(makeTETA());
267 A->_hardwareAccessingElements.push_back(A->_internalElements[i]);
268 }
269 // B has two low-level transfer elements and one additional internal
270 for(size_t i = 0; i < 2; ++i) {
271 B->_internalElements.push_back(makeTETA());
272 B->_hardwareAccessingElements.push_back(B->_internalElements[i]);
273 }
274 B->_internalElements.push_back(makeTETA());
275
276 TransferGroup group;
277
278 group.addAccessor(A);
279 group.addAccessor(B);
280
281 A->resetCounters();
282 B->resetCounters();
283 for(auto& e : A->_internalElements) e->resetCounters();
284 for(auto& e : B->_internalElements) e->resetCounters();
285 group.read();
286 CHECK_COUNTERS_HIGH_LEVEL(A, false, 1, 5, false);
287 CHECK_COUNTERS_HIGH_LEVEL(B, false, 1, 5, false);
288 CHECK_COUNTERS_MID_LEVEL(B->_internalElements[2]);
289 CHECK_COUNTERS_LOW_LEVEL(A->_internalElements[0], false, 2, 4);
290 CHECK_COUNTERS_LOW_LEVEL(B->_internalElements[0], false, 2, 4);
291 CHECK_COUNTERS_LOW_LEVEL(B->_internalElements[1], false, 2, 4);
292}
293
294/**********************************************************************************************************************/
295
302BOOST_AUTO_TEST_CASE(test_B_12_10_3) {
303 std::cout << "test_B_12_10_3" << std::endl;
304 auto A = makeTETA();
305 auto B = makeTETA();
306
307 // A has just one low-level transfer element
308 for(size_t i = 0; i < 1; ++i) {
309 A->_internalElements.push_back(makeTETA());
310 A->_hardwareAccessingElements.push_back(A->_internalElements[i]);
311 }
312
313 TransferGroup group;
314
315 group.addAccessor(A);
316 group.addAccessor(B);
317
318 A->resetCounters();
319 B->resetCounters();
320 for(auto& e : A->_internalElements) e->resetCounters();
321 A->_internalElements[0]->_throwRuntimeErrInTransfer = true;
322 BOOST_CHECK_THROW(group.read(), ChimeraTK::runtime_error); // (no test intended, just catch)
323 BOOST_CHECK(A->_seenActiveException == A->_internalElements[0]->_thrownException);
324 BOOST_CHECK(A->_internalElements[0]->_seenActiveException == nullptr); // the test TE does not delegate the exception
325}
326
327/**********************************************************************************************************************/
328
333BOOST_AUTO_TEST_CASE(test_B_12_10_4) {
334 std::cout << "test_B_12_10_4" << std::endl;
335 auto A = makeTETA();
336 auto B = makeTETA();
337
338 TransferGroup group;
339
340 group.addAccessor(A);
341 group.addAccessor(B);
342
343 // A throws in read
344 A->resetCounters();
345 B->resetCounters();
346 A->_throwRuntimeErrInTransfer = true;
347 BOOST_CHECK_THROW(group.read(), ChimeraTK::runtime_error);
348 CHECK_COUNTERS_HIGH_LEVEL(A, false, 1, 4, true);
349 CHECK_COUNTERS_HIGH_LEVEL(B, false, 1, 4, true);
350
351 // B throws in read
352 A->resetCounters();
353 B->resetCounters();
354 B->_throwRuntimeErrInTransfer = true;
355 BOOST_CHECK_THROW(group.read(), ChimeraTK::runtime_error);
356 CHECK_COUNTERS_HIGH_LEVEL(A, false, 1, 4, true);
357 CHECK_COUNTERS_HIGH_LEVEL(B, false, 1, 4, true);
358
359 // both throw in read
360 A->resetCounters();
361 B->resetCounters();
362 A->_throwRuntimeErrInTransfer = true;
363 B->_throwRuntimeErrInTransfer = true;
364 BOOST_CHECK_THROW(group.read(), ChimeraTK::runtime_error);
365 CHECK_COUNTERS_HIGH_LEVEL(A, false, 1, 4, true);
366 CHECK_COUNTERS_HIGH_LEVEL(B, false, 1, 4, true);
367
368 // A throws in write
369 A->resetCounters();
370 B->resetCounters();
371 A->_throwRuntimeErrInTransfer = true;
372 BOOST_CHECK_THROW(group.write(), ChimeraTK::runtime_error);
373 CHECK_COUNTERS_HIGH_LEVEL(A, true, 1, 4, true);
374 CHECK_COUNTERS_HIGH_LEVEL(B, true, 1, 4, true);
375
376 // B throws in write
377 A->resetCounters();
378 B->resetCounters();
379 B->_throwRuntimeErrInTransfer = true;
380 BOOST_CHECK_THROW(group.write(), ChimeraTK::runtime_error);
381 CHECK_COUNTERS_HIGH_LEVEL(A, true, 1, 4, true);
382 CHECK_COUNTERS_HIGH_LEVEL(B, true, 1, 4, true);
383
384 // both throw in write
385 A->resetCounters();
386 B->resetCounters();
387 A->_throwRuntimeErrInTransfer = true;
388 B->_throwRuntimeErrInTransfer = true;
389 BOOST_CHECK_THROW(group.write(), ChimeraTK::runtime_error);
390 CHECK_COUNTERS_HIGH_LEVEL(A, true, 1, 4, true);
391 CHECK_COUNTERS_HIGH_LEVEL(B, true, 1, 4, true);
392}
393
394/**********************************************************************************************************************/
395
402BOOST_AUTO_TEST_CASE(test_B_12_10_5) {
403 std::cout << "test_B_12_10_5" << std::endl;
404 auto A = makeTETA();
405 auto B = makeTETA();
406
407 TransferGroup group;
408
409 group.addAccessor(A);
410 group.addAccessor(B);
411
412 // both throw in read
413 A->resetCounters();
414 B->resetCounters();
415 A->_throwRuntimeErrInTransfer = true;
416 B->_throwRuntimeErrInTransfer = true;
417 std::exception_ptr e;
418 try {
419 group.read();
420 }
421 catch(...) {
422 e = std::current_exception();
423 }
424 assert(A->_thrownException != B->_thrownException); // make sure our test is sensitive
425 if(A->_transferIndex < B->_transferIndex) {
426 // A has thrown first
427 BOOST_CHECK(e == A->_thrownException);
428 }
429 else {
430 // B has thrown first
431 BOOST_CHECK(e == B->_thrownException);
432 }
433
434 // both throw in write
435 A->resetCounters();
436 B->resetCounters();
437 A->_throwRuntimeErrInTransfer = true;
438 B->_throwRuntimeErrInTransfer = true;
439 try {
440 group.write();
441 }
442 catch(...) {
443 e = std::current_exception();
444 }
445 assert(A->_thrownException != B->_thrownException); // make sure our test is sensitive
446 if(A->_transferIndex < B->_transferIndex) {
447 // A has thrown first
448 BOOST_CHECK(e == A->_thrownException);
449 }
450 else {
451 // B has thrown first
452 BOOST_CHECK(e == B->_thrownException);
453 }
454}
455
456/**********************************************************************************************************************/
457
462BOOST_AUTO_TEST_CASE(test_B_12_11_1) {
463 std::cout << "test_B_12_11_1" << std::endl;
464 auto A = makeTETA();
465 auto B = makeTETA();
466
467 TransferGroup group;
468
469 group.addAccessor(A);
470 group.addAccessor(B);
471
472 // A throws in read
473 A->resetCounters();
474 B->resetCounters();
475 A->_throwRuntimeErrInTransfer = true;
476 BOOST_CHECK_THROW(group.read(), ChimeraTK::runtime_error); // (no check intended, just catch)
477 BOOST_CHECK(A->_updateDataBuffer == false);
478 BOOST_CHECK(B->_updateDataBuffer == false);
479
480 // A throws in write
481 A->resetCounters();
482 B->resetCounters();
483 A->_throwRuntimeErrInTransfer = true;
484 BOOST_CHECK_THROW(group.write(), ChimeraTK::runtime_error); // (no check intended, just catch)
485 BOOST_CHECK(A->_updateDataBuffer == false);
486 BOOST_CHECK(B->_updateDataBuffer == false);
487
488 // Nothing throws in read
489 A->resetCounters();
490 B->resetCounters();
491 group.read();
492 BOOST_CHECK(A->_updateDataBuffer == true);
493 BOOST_CHECK(B->_updateDataBuffer == true);
494
495 // Nothing throws in write
496 A->resetCounters();
497 B->resetCounters();
498 group.write();
499 BOOST_CHECK(A->_updateDataBuffer == true);
500 BOOST_CHECK(B->_updateDataBuffer == true);
501}
502
503/**********************************************************************************************************************/
504/**********************************************************************************************************************/
506/**********************************************************************************************************************/
507/**********************************************************************************************************************/
508
509BOOST_AUTO_TEST_CASE(testExceptionHandling) {
510 const std::string EXCEPTION_DUMMY_CDD = "(ExceptionDummy:1?map=test3.map)";
512 ChimeraTK::Device device1;
513 ChimeraTK::Device device2;
514 ChimeraTK::Device device3;
515
516 device1.open("DUMMYD1");
517 auto exceptionDummy = boost::dynamic_pointer_cast<ChimeraTK::ExceptionDummy>(
518 ChimeraTK::BackendFactory::getInstance().createBackend(EXCEPTION_DUMMY_CDD));
519 device2.open(EXCEPTION_DUMMY_CDD);
520 device3.open("DUMMYD2");
521
522 auto accessor1 = device1.getScalarRegisterAccessor<int>("/BOARD/WORD_FIRMWARE");
523 auto accessor1w = device1.getScalarRegisterAccessor<int>("/BOARD/WORD_FIRMWARE");
524 // accessors 2 and 3 will be merged to a single low level transfer element
525 auto accessor2 = device2.getScalarRegisterAccessor<int>("/Integers/signed32");
526 auto accessor2w = device2.getScalarRegisterAccessor<int>("/Integers/signed32");
527 auto accessor3 = device2.getScalarRegisterAccessor<uint32_t>("/Integers/unsigned32");
528 auto accessor3w = device2.getScalarRegisterAccessor<uint32_t>("/Integers/unsigned32");
529 auto accessor4 = device2.getScalarRegisterAccessor<float>("/FixedPoint/value");
530 auto accessor4w = device2.getScalarRegisterAccessor<float>("/FixedPoint/value");
531 auto accessor5 = device3.getScalarRegisterAccessor<int>("/BOARD/WORD_FIRMWARE");
532 auto accessor5w = device3.getScalarRegisterAccessor<int>("/BOARD/WORD_FIRMWARE");
533
534 TransferGroup tg;
535 tg.addAccessor(accessor2);
536 tg.addAccessor(accessor3);
537 tg.addAccessor(accessor1);
538 tg.addAccessor(accessor4);
539 tg.addAccessor(accessor5);
540
541 accessor1 = 1;
542 accessor2 = 2;
543 accessor3 = 3;
544 accessor4 = 4;
545 accessor5 = 5;
546
547 accessor1w = int(0xdeadcafe);
548 accessor2w = 815;
549 accessor3w = 4711;
550 accessor4w = 10101010;
551 accessor5w = int(0xc01dcafe);
552 accessor1w.write();
553 accessor2w.write();
554 accessor3w.write();
555 accessor4w.write();
556 accessor5w.write();
557
558 exceptionDummy->throwExceptionRead = true;
559 BOOST_CHECK_THROW(tg.read(), ChimeraTK::runtime_error);
560
561 BOOST_CHECK_EQUAL(static_cast<int>(accessor1), 1);
562 BOOST_CHECK_EQUAL(static_cast<int>(accessor2), 2);
563 BOOST_CHECK_EQUAL(static_cast<int>(accessor3), 3);
564 BOOST_CHECK_EQUAL(static_cast<int>(accessor4), 4);
565 BOOST_CHECK_EQUAL(static_cast<int>(accessor5), 5);
566}
567
568/**********************************************************************************************************************/
569
573
574 device.open("DUMMYD3");
575
576 auto a1 = device.getOneDRegisterAccessor<int>("ADC/AREA_DMAABLE");
577 auto a2 = device.getOneDRegisterAccessor<int>("ADC/AREA_DMAABLE");
578 auto a3 = device.getOneDRegisterAccessor<int>("BOARD/WORD_STATUS");
579 auto a4 = device.getOneDRegisterAccessor<unsigned int>("ADC/AREA_DMAABLE");
580
581 // slightly redundant to do this test here, this is just a control test still
582 // independent of the TransferGroup
583 a1[0] = 42;
584 a2[0] = 120;
585 a3[0] = 123;
586 a4[0] = 456;
587 BOOST_CHECK(a1[0] == 42);
588 a1.write();
589 a3.write();
590 a3[0] = 654;
591 BOOST_CHECK(a2[0] == 120);
592 BOOST_CHECK(a3[0] == 654);
593 BOOST_CHECK(a4[0] == 456);
594 a2.read();
595 BOOST_CHECK(a1[0] == 42);
596 BOOST_CHECK(a2[0] == 42);
597 BOOST_CHECK(a3[0] == 654);
598 BOOST_CHECK(a4[0] == 456);
599 a3.read();
600 BOOST_CHECK(a1[0] == 42);
601 BOOST_CHECK(a2[0] == 42);
602 BOOST_CHECK(a3[0] == 123);
603 BOOST_CHECK(a4[0] == 456);
604 a4.read();
605 BOOST_CHECK(a1[0] == 42);
606 BOOST_CHECK(a2[0] == 42);
607 BOOST_CHECK(a3[0] == 123);
608 BOOST_CHECK(a4[0] == 42);
609
610 // add accessors to the transfer group
611 TransferGroup group;
612 group.addAccessor(a1);
613 BOOST_CHECK(!group.isReadOnly());
614 group.addAccessor(a2);
615 BOOST_CHECK(group.isReadOnly());
616 group.addAccessor(a3);
617 group.addAccessor(a4);
618 BOOST_CHECK(group.isReadOnly());
619
620 // check if adding an accessor to another group throws an exception
621 TransferGroup group2;
622 BOOST_CHECK_THROW(group2.addAccessor(a1), ChimeraTK::logic_error);
623
624 // check that reading and writing the accessors which are part of the group
625 // throws
626 BOOST_CHECK_THROW(a1.read(), ChimeraTK::logic_error);
627 BOOST_CHECK_THROW(a1.write(), ChimeraTK::logic_error);
628 BOOST_CHECK_THROW(a3.read(), ChimeraTK::logic_error);
629 BOOST_CHECK_THROW(a4.write(), ChimeraTK::logic_error);
630
631 // during the replace operation, user buffers will be reset, if a replacement
632 BOOST_CHECK(a1[0] == 42);
633 BOOST_CHECK(a2[0] == 0); // this one was replaced
634 BOOST_CHECK(a3[0] == 123);
635 BOOST_CHECK(a4[0] == 42);
636
637 // Writing to the register accessor (cooked) buffers should not influence the
638 // other accessors in the group.
639 a1[0] = 333;
640 BOOST_CHECK(a1[0] == 333);
641 BOOST_CHECK(a2[0] == 0);
642 BOOST_CHECK(a3[0] == 123);
643 BOOST_CHECK(a4[0] == 42);
644 a2[0] = 666;
645 BOOST_CHECK(a1[0] == 333);
646 BOOST_CHECK(a2[0] == 666);
647 BOOST_CHECK(a3[0] == 123);
648 BOOST_CHECK(a4[0] == 42);
649 a3[0] = 999;
650 BOOST_CHECK(a1[0] == 333);
651 BOOST_CHECK(a2[0] == 666);
652 BOOST_CHECK(a3[0] == 999);
653 BOOST_CHECK(a4[0] == 42);
654 a4[0] = 111;
655 BOOST_CHECK(a1[0] == 333);
656 BOOST_CHECK(a2[0] == 666);
657 BOOST_CHECK(a3[0] == 999);
658 BOOST_CHECK(a4[0] == 111);
659
660 device.close();
661}
662
663/**********************************************************************************************************************/
664
665template<typename T>
667 // if fakeLowLevel is set to true, the decorator will pretend to be the
668 // low-level TransferElement.
669 CountingDecorator(const boost::shared_ptr<ChimeraTK::TransferElement>& target, bool _fakeLowLevel = false)
670 : NDRegisterAccessorDecorator<T>(boost::dynamic_pointer_cast<NDRegisterAccessor<T>>(target)),
671 fakeLowLevel(_fakeLowLevel) {
672 assert(boost::dynamic_pointer_cast<NDRegisterAccessor<T>>(target) != nullptr); // a bit late but better than
673 // nothing...
674 this->_name = "CD:" + this->_name;
675 }
676
677 void doPreRead(TransferType type) override {
678 nPreRead++;
680 }
681
682 void doPostRead(TransferType type, bool hasNewData) override {
683 nPostRead++;
685 }
686
687 void doPreWrite(TransferType type, VersionNumber versionNumber) override {
688 nPreWrite++;
690 }
691
692 void doPostWrite(TransferType type, VersionNumber versionNumber) override {
693 nPostWrite++;
695 }
696
701
702 bool doWriteTransfer(ChimeraTK::VersionNumber versionNumber) override {
703 nWrite++;
705 }
706
707 std::vector<boost::shared_ptr<ChimeraTK::TransferElement>> getHardwareAccessingElements() override {
708 if(fakeLowLevel) {
709 return {boost::enable_shared_from_this<TransferElement>::shared_from_this()};
710 }
711 else {
713 }
714 }
715
716 void replaceTransferElement(boost::shared_ptr<TransferElement> newElement) override {
717 if(fakeLowLevel) return;
718 if(_target->mayReplaceOther(newElement)) {
719 _target = boost::static_pointer_cast<NDRegisterAccessor<T>>(newElement);
720 }
721 else {
722 _target->replaceTransferElement(newElement);
723 }
724 }
725
726 std::list<boost::shared_ptr<TransferElement>> getInternalElements() override {
727 if(fakeLowLevel) {
728 return {};
729 }
730 else {
732 }
733 }
734
735 bool mayReplaceOther(const boost::shared_ptr<TransferElement const>& other) const override {
736 auto casted = boost::dynamic_pointer_cast<CountingDecorator<T> const>(other);
737 if(this == casted.get()) {
738 return false;
739 }
740 if(!casted) return false;
741 if(_target == casted->_target) return true;
742 if(_target->mayReplaceOther(casted->_target)) return true;
743 return false;
744 }
745
747 nPreRead = 0;
748 nPostRead = 0;
749 nPreWrite = 0;
750 nPostWrite = 0;
751 nRead = 0;
753 nReadLatest = 0;
754 nWrite = 0;
755 }
756
758 size_t nPreRead{0};
759 size_t nPostRead{0};
760 size_t nPreWrite{0};
761 size_t nPostWrite{0};
762 size_t nRead{0};
764 size_t nReadLatest{0};
765 size_t nWrite{0};
766
767 using NDRegisterAccessorDecorator<T>::_target;
768};
769
770/**********************************************************************************************************************/
771
772BOOST_AUTO_TEST_CASE(testCallsToPrePostFunctionsInDecorator) {
775
776 device.open("DUMMYD3");
777
778 // create register accessors of four registers with adjecent addresses, one of
779 // the registers is in the group two times
780 auto mux0 = device.getScalarRegisterAccessor<int>("/ADC/WORD_CLK_MUX_0");
781 auto mux0_2 = device.getScalarRegisterAccessor<int>("/ADC/WORD_CLK_MUX_0");
782 auto mux2 = device.getScalarRegisterAccessor<int>("/ADC/WORD_CLK_MUX_2");
783 auto mux3 = device.getScalarRegisterAccessor<int>("/ADC/WORD_CLK_MUX_3");
784
785 // decorate the accessors which we will put into the transfer group, so we can
786 // count how often the functions are called
787 auto mux0d = boost::make_shared<CountingDecorator<int>>(mux0.getHighLevelImplElement());
788 auto mux0_2d = boost::make_shared<CountingDecorator<int>>(mux0_2.getHighLevelImplElement());
789 auto mux2d = boost::make_shared<CountingDecorator<int>>(mux2.getHighLevelImplElement());
790 auto mux3d = boost::make_shared<CountingDecorator<int>>(mux3.getHighLevelImplElement());
791
792 // place the decorated registers inside the abstractors
793 mux0.replace(mux0d);
794 mux0_2.replace(mux0_2d);
795 mux2.replace(mux2d);
796 mux3.replace(mux3d);
797
798 // create the same register accessors again, so we have a second set not part
799 // of the transfer group
800 auto mux0b = device.getScalarRegisterAccessor<int>("/ADC/WORD_CLK_MUX_0");
801 auto mux2b = device.getScalarRegisterAccessor<int>("/ADC/WORD_CLK_MUX_2");
802 auto mux3b = device.getScalarRegisterAccessor<int>("/ADC/WORD_CLK_MUX_3");
803
804 BOOST_CHECK(mux0d->_target != mux0_2d->_target);
805 BOOST_CHECK(mux0.getHighLevelImplElement() == mux0d);
806 BOOST_CHECK(mux0_2.getHighLevelImplElement() == mux0_2d);
807
808 // add accessors to the transfer group
809 TransferGroup group;
810 group.addAccessor(mux0);
811 group.addAccessor(mux0_2);
812 group.addAccessor(mux2);
813 group.addAccessor(mux3);
814
815 BOOST_CHECK(mux0.getHighLevelImplElement() == mux0d);
816 BOOST_CHECK(mux0_2.getHighLevelImplElement() != mux0_2d);
817 BOOST_CHECK(
818 boost::dynamic_pointer_cast<ChimeraTK::CopyRegisterDecoratorTrait>(mux0_2.getHighLevelImplElement()) != nullptr);
819
820 // write some data to the registers (without the TransferGroup)
821 mux0b = 18;
822 mux0b.write();
823 mux2b = 22;
824 mux2b.write();
825 mux3b = 23;
826 mux3b.write();
827
828 // read through transfer group
829 group.read();
830
831 BOOST_CHECK_EQUAL(int(mux0), 18);
832 BOOST_CHECK_EQUAL(int(mux0_2), 18);
833
834 // we don't know which of the accessors has been eliminated (and this is
835 // actually a random choice at runtime)
836 BOOST_CHECK((mux0d->nPreRead == 1 && mux0_2d->nPreRead == 0) || (mux0d->nPreRead == 0 && mux0_2d->nPreRead == 1));
837 if(mux0d->nPreRead == 1) {
838 BOOST_CHECK_EQUAL(mux0d->nPostRead, 1);
839 BOOST_CHECK_EQUAL(mux0_2d->nPreRead, 0);
840 BOOST_CHECK_EQUAL(mux0_2d->nPostRead, 0);
841 }
842 else {
843 BOOST_CHECK_EQUAL(mux0_2d->nPostRead, 1);
844 BOOST_CHECK_EQUAL(mux0d->nPreRead, 0);
845 BOOST_CHECK_EQUAL(mux0d->nPostRead, 0);
846 }
847 BOOST_CHECK_EQUAL(mux0d->nRead, 0);
848 BOOST_CHECK_EQUAL(mux0_2d->nRead, 0);
849 BOOST_CHECK_EQUAL(mux0d->nPreWrite, 0);
850 BOOST_CHECK_EQUAL(mux0d->nPostWrite, 0);
851 BOOST_CHECK_EQUAL(mux0d->nReadNonBlocking, 0);
852 BOOST_CHECK_EQUAL(mux0d->nReadLatest, 0);
853 BOOST_CHECK_EQUAL(mux0d->nWrite, 0);
854 BOOST_CHECK_EQUAL(mux0_2d->nPreWrite, 0);
855 BOOST_CHECK_EQUAL(mux0_2d->nPostWrite, 0);
856 BOOST_CHECK_EQUAL(mux0_2d->nReadNonBlocking, 0);
857 BOOST_CHECK_EQUAL(mux0_2d->nReadLatest, 0);
858 BOOST_CHECK_EQUAL(mux0_2d->nWrite, 0);
859
860 BOOST_CHECK_EQUAL(int(mux2), 22);
861 BOOST_CHECK_EQUAL(mux2d->nPreRead, 1);
862 BOOST_CHECK_EQUAL(mux2d->nPostRead, 1);
863 BOOST_CHECK_EQUAL(mux2d->nPreWrite, 0);
864 BOOST_CHECK_EQUAL(mux2d->nPostWrite, 0);
865 BOOST_CHECK_EQUAL(mux2d->nRead, 0);
866 BOOST_CHECK_EQUAL(mux2d->nReadNonBlocking, 0);
867 BOOST_CHECK_EQUAL(mux2d->nReadLatest, 0);
868 BOOST_CHECK_EQUAL(mux2d->nWrite, 0);
869
870 BOOST_CHECK_EQUAL(int(mux3), 23);
871 BOOST_CHECK_EQUAL(mux3d->nPreRead, 1);
872 BOOST_CHECK_EQUAL(mux3d->nPostRead, 1);
873 BOOST_CHECK_EQUAL(mux3d->nPreWrite, 0);
874 BOOST_CHECK_EQUAL(mux3d->nPostWrite, 0);
875 BOOST_CHECK_EQUAL(mux3d->nRead, 0);
876 BOOST_CHECK_EQUAL(mux3d->nReadNonBlocking, 0);
877 BOOST_CHECK_EQUAL(mux3d->nReadLatest, 0);
878 BOOST_CHECK_EQUAL(mux3d->nWrite, 0);
879
880 mux0d->resetCounters();
881 mux0_2d->resetCounters();
882 mux2d->resetCounters();
883 mux3d->resetCounters();
884
885 // write through transfer group is not possible, since it is read-only
886 mux0 = 24;
887 mux0_2 = 24;
888 mux2 = 30;
889 mux3 = 33;
890 BOOST_CHECK_THROW(group.write(), ChimeraTK::logic_error);
891}
892
893/**********************************************************************************************************************/
894
895BOOST_AUTO_TEST_CASE(testCallsToPrePostFunctionsInLowLevel) {
898
899 device.open("DUMMYD3");
900
901 // create register accessors of four registers with adjacent addresses
902 auto mux0 = device.getScalarRegisterAccessor<int>("/ADC/WORD_CLK_MUX_0");
903 auto mux0_2 = mux0; // make duplicate of one accessor
904 auto mux2 = device.getScalarRegisterAccessor<int>("/ADC/WORD_CLK_MUX_2");
905 auto mux3 = device.getScalarRegisterAccessor<int>("/ADC/WORD_CLK_MUX_3");
906
907 // decorate the accessors which we will put into the transfer group, so we can
908 // count how often the functions are called
909 auto mux0d = boost::make_shared<CountingDecorator<int>>(mux0.getHighLevelImplElement(), true);
910 auto mux0_2d = boost::make_shared<CountingDecorator<int>>(mux0_2.getHighLevelImplElement(), true);
911 auto mux2d = boost::make_shared<CountingDecorator<int>>(mux2.getHighLevelImplElement(), true);
912 auto mux3d = boost::make_shared<CountingDecorator<int>>(mux3.getHighLevelImplElement(), true);
913
914 // decorate another time
915 auto mux0d2 = boost::make_shared<CountingDecorator<int>>(mux0d);
916 auto mux0_2d2 = boost::make_shared<CountingDecorator<int>>(mux0_2d);
917 auto mux2d2 = boost::make_shared<CountingDecorator<int>>(mux2d);
918 auto mux3d2 = boost::make_shared<CountingDecorator<int>>(mux3d);
919
920 // place the decorated registers inside the abstractors
921 mux0.replace(mux0d2);
922 mux0_2.replace(mux0_2d2);
923 mux2.replace(mux2d2);
924 mux3.replace(mux3d2);
925
926 // create the same register accessors again, so we have a second set not part
927 // of the transfer group
928 auto mux0b = device.getScalarRegisterAccessor<int>("/ADC/WORD_CLK_MUX_0");
929 auto mux2b = device.getScalarRegisterAccessor<int>("/ADC/WORD_CLK_MUX_2");
930 auto mux3b = device.getScalarRegisterAccessor<int>("/ADC/WORD_CLK_MUX_3");
931
932 BOOST_CHECK(mux0d->_target == mux0_2d->_target);
933 BOOST_CHECK(mux0d2->_target == mux0d);
934 BOOST_CHECK(mux0_2d2->_target == mux0_2d);
935 BOOST_CHECK(mux2d2->_target == mux2d);
936 BOOST_CHECK(mux3d2->_target == mux3d);
937
938 // add accessors to the transfer group
939 TransferGroup group;
940 group.addAccessor(mux0);
941 group.addAccessor(mux0_2);
942 group.addAccessor(mux2);
943 group.addAccessor(mux3);
944
945 BOOST_CHECK(mux0d->_target == mux0_2d->_target);
946 BOOST_CHECK(
947 boost::dynamic_pointer_cast<ChimeraTK::CopyRegisterDecoratorTrait>(mux0_2.getHighLevelImplElement()) != nullptr);
948 BOOST_CHECK(mux2d2->_target == mux2d);
949 BOOST_CHECK(mux3d2->_target == mux3d);
950
951 // write some data to the registers (without the TransferGroup)
952 mux0b = 18;
953 mux0b.write();
954 mux2b = 22;
955 mux2b.write();
956 mux3b = 23;
957 mux3b.write();
958
959 // read through transfer group
960 group.read();
961
962 BOOST_CHECK_EQUAL(int(mux0), 18);
963 BOOST_CHECK_EQUAL(int(mux0_2), 18);
964
965 // we don't know which of the accessors has been eliminated (and this is
966 // actually a random choice at runtime)
967 BOOST_CHECK((mux0d->nRead == 1 && mux0_2d->nRead == 0) || (mux0d->nRead == 0 && mux0_2d->nRead == 1));
968 if(mux0d->nRead == 1) {
969 BOOST_CHECK_EQUAL(mux0d->nPreRead, 1);
970 BOOST_CHECK_EQUAL(mux0d->nPostRead, 1);
971 BOOST_CHECK_EQUAL(mux0_2d->nPreRead, 0);
972 BOOST_CHECK_EQUAL(mux0_2d->nPostRead, 0);
973 }
974 else {
975 BOOST_CHECK_EQUAL(mux0_2d->nPreRead, 1);
976 BOOST_CHECK_EQUAL(mux0_2d->nPostRead, 1);
977 BOOST_CHECK_EQUAL(mux0d->nPreRead, 0);
978 BOOST_CHECK_EQUAL(mux0d->nPostRead, 0);
979 }
980 BOOST_CHECK_EQUAL(mux0d->nPreWrite, 0);
981 BOOST_CHECK_EQUAL(mux0d->nPostWrite, 0);
982 BOOST_CHECK_EQUAL(mux0d->nReadNonBlocking, 0);
983 BOOST_CHECK_EQUAL(mux0d->nReadLatest, 0);
984 BOOST_CHECK_EQUAL(mux0d->nWrite, 0);
985 BOOST_CHECK_EQUAL(mux0_2d->nPreWrite, 0);
986 BOOST_CHECK_EQUAL(mux0_2d->nPostWrite, 0);
987 BOOST_CHECK_EQUAL(mux0_2d->nReadNonBlocking, 0);
988 BOOST_CHECK_EQUAL(mux0_2d->nReadLatest, 0);
989 BOOST_CHECK_EQUAL(mux0_2d->nWrite, 0);
990
991 BOOST_CHECK_EQUAL(int(mux2), 22);
992 BOOST_CHECK_EQUAL(mux2d->nPreRead, 1);
993 BOOST_CHECK_EQUAL(mux2d->nPostRead, 1);
994 BOOST_CHECK_EQUAL(mux2d->nPreWrite, 0);
995 BOOST_CHECK_EQUAL(mux2d->nPostWrite, 0);
996 BOOST_CHECK_EQUAL(mux2d->nRead, 1);
997 BOOST_CHECK_EQUAL(mux2d->nReadNonBlocking, 0);
998 BOOST_CHECK_EQUAL(mux2d->nReadLatest, 0);
999 BOOST_CHECK_EQUAL(mux2d->nWrite, 0);
1000
1001 BOOST_CHECK_EQUAL(int(mux3), 23);
1002 BOOST_CHECK_EQUAL(mux3d->nPreRead, 1);
1003 BOOST_CHECK_EQUAL(mux3d->nPostRead, 1);
1004 BOOST_CHECK_EQUAL(mux3d->nPreWrite, 0);
1005 BOOST_CHECK_EQUAL(mux3d->nPostWrite, 0);
1006 BOOST_CHECK_EQUAL(mux3d->nRead, 1);
1007 BOOST_CHECK_EQUAL(mux3d->nReadNonBlocking, 0);
1008 BOOST_CHECK_EQUAL(mux3d->nReadLatest, 0);
1009 BOOST_CHECK_EQUAL(mux3d->nWrite, 0);
1010
1011 mux0d->resetCounters();
1012 mux0_2d->resetCounters();
1013 mux2d->resetCounters();
1014 mux3d->resetCounters();
1015
1016 // write through transfer group
1018 mux0 = 24;
1019 mux0_2 = 24;
1020 mux2 = 30;
1021 mux3 = 33;
1022 BOOST_CHECK_THROW(group.write(), ChimeraTK::logic_error);
1023}
1024
1025BOOST_AUTO_TEST_CASE(testTemporaryAbstractorWorks) {
1026 // Testing that adding a plain transfer element to the TransferGroup still works as expected.
1029
1030 device.open("(dummy?map=mtcadummy.map)");
1031
1032 TransferGroup group;
1033 auto a = device.getScalarRegisterAccessor<int32_t>("BOARD.WORD_FIRMWARE");
1034 auto b = device.getBackend()->getRegisterAccessor<int32_t>("BOARD.WORD_FIRMWARE", 1, 0, {});
1035
1036 auto c = device.getScalarRegisterAccessor<int32_t>("BOARD.WORD_FIRMWARE");
1037
1038 group.addAccessor(a);
1039 c = 12;
1040 c.write();
1041
1042 group.read();
1043 BOOST_CHECK_EQUAL(a, 12);
1044 group.addAccessor(b);
1045
1046 c = 13;
1047 c.write();
1048
1049 group.read();
1050 BOOST_CHECK_EQUAL(a, 13);
1051 BOOST_CHECK_EQUAL(b->accessChannel(0)[0], 13);
1052}
1053
1054/**********************************************************************************************************************/
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 setDMapFilePath(std::string dMapFilePath)
This function sets the _DMapFilePath.
Class allows to read/write registers from device.
Definition Device.h:39
void close()
Close the device.
Definition Device.cc:66
boost::shared_ptr< DeviceBackend > getBackend()
Obtain the backend.
Definition Device.cc:111
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
OneDRegisterAccessor< UserType > getOneDRegisterAccessor(const RegisterPath &registerPathName, size_t numberOfWords=0, size_t wordOffsetInRegister=0, const AccessModeFlags &flags=AccessModeFlags({})) const
Get a OneDRegisterAccessor object for the given register.
Definition Device.h:276
void open(std::string const &aliasName)
Open a device by the given alias name from the DMAP file.
Definition Device.cc:58
Base class for decorators of the NDRegisterAccessor.
std::list< boost::shared_ptr< TransferElement > > getInternalElements() override
Obtain the full list of TransferElements internally used by this TransferElement.
void doReadTransferSynchronously() override
Implementation version of readTransfer() for synchronous reads.
void doPreRead(TransferType type) override
Backend specific implementation of preRead().
std::vector< boost::shared_ptr< ChimeraTK::TransferElement > > getHardwareAccessingElements() override
Obtain the underlying TransferElements with actual hardware access.
bool doWriteTransfer(ChimeraTK::VersionNumber versionNumber) override
Implementation version of writeTransfer().
N-dimensional register accessor.
const boost::shared_ptr< TransferElement > & getHighLevelImplElement()
Obtain the highest level implementation TransferElement.
std::string _name
Identifier uniquely identifying the TransferElement.
Group multiple data accessors to efficiently trigger data transfers on the whole group.
void addAccessor(TransferElementAbstractor &accessor)
Add a register accessor to the group.
bool isReadOnly()
Check if transfer group is read-only.
void write(VersionNumber versionNumber={})
Trigger write transfer for all accessors in the group.
void read()
Trigger read transfer for all accessors in the group.
Accessor class to read and write 2D registers.
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.
void doPostRead(TransferType type, bool hasNewData) override
Backend specific implementation of postRead().
bool mayReplaceOther(const boost::shared_ptr< TransferElement const > &other) const override
Check whether the TransferElement can be used in places where the TransferElement "other" is currentl...
void doPostWrite(TransferType type, VersionNumber versionNumber) override
Backend specific implementation of postWrite().
void doReadTransferSynchronously() override
Implementation version of readTransfer() for synchronous reads.
bool doWriteTransfer(ChimeraTK::VersionNumber versionNumber) override
Implementation version of writeTransfer().
CountingDecorator(const boost::shared_ptr< ChimeraTK::TransferElement > &target, bool _fakeLowLevel=false)
void replaceTransferElement(boost::shared_ptr< TransferElement > newElement) override
Search for all underlying TransferElements which are considered identical (see sameRegister()) with t...
std::vector< boost::shared_ptr< ChimeraTK::TransferElement > > getHardwareAccessingElements() override
Obtain the underlying TransferElements with actual hardware access.
std::list< boost::shared_ptr< TransferElement > > getInternalElements() override
Obtain the full list of TransferElements internally used by this TransferElement.
void doPreWrite(TransferType type, VersionNumber versionNumber) override
Backend specific implementation of preWrite().
void doPreRead(TransferType type) override
Backend specific implementation of preRead().
auto exceptionDummy
ctk::Device device
#define CHECK_COUNTERS_LOW_LEVEL(te, isWrite, expectedOrderMin, expectedOrderMax)
BOOST_AUTO_TEST_CASE(test_B_12_1_3)
Tests for single specification points.
#define CHECK_COUNTERS_HIGH_LEVEL(te, isWrite, expectedOrderPreMax, expectedOrderPostMin, expectTransfer)
#define CHECK_COUNTERS_MID_LEVEL(te)
boost::shared_ptr< TransferElementTestAccessor< int32_t > > makeTETA(AccessModeFlags flags={})