ChimeraTK-ApplicationCore 04.06.00
Loading...
Searching...
No Matches
testBidirectionalVariables.cc
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#include <future>
4
5#define BOOST_TEST_MODULE testBidirectionalVariables
6
7#include "Application.h"
8#include "ApplicationModule.h"
9#include "check_timeout.h"
10#include "ScalarAccessor.h"
11#include "TestFacility.h"
12
13#include <ChimeraTK/BackendFactory.h>
14
15#include <boost/mpl/list.hpp>
16#include <boost/thread/latch.hpp>
17
18#define BOOST_NO_EXCEPTIONS
19#include <boost/test/included/unit_test.hpp>
20#undef BOOST_NO_EXCEPTIONS
21
22using namespace boost::unit_test_framework;
23namespace ctk = ChimeraTK;
24
26
27 /********************************************************************************************************************/
28
29 /* Module which converts the input data from inches to centimetres - and the
30 * other way round for the return channel. In case of the return channel, the
31 * data is rounded downwards to integer inches and sent again forward. */
33 using ctk::ApplicationModule::ApplicationModule;
34
35 ctk::ScalarPushInputWB<int> var1{this, "var1", "inches", "A length, for some reason rounded to integer"};
36 ctk::ScalarOutputPushRB<double> var2{this, "var2", "centimetres", "Same length converted to centimetres"};
37
38 void prepare() override {
39 incrementDataFaultCounter(); // force all outputs to invalid
40 writeAll(); // write initial values
41 decrementDataFaultCounter(); // validity according to input validity
42 }
43
44 void mainLoop() override {
45 auto group = readAnyGroup();
46 while(true) {
47 auto var = group.readAny();
48 if(var == var2.getId()) {
49 var1 = std::floor(var2 / 2.54);
50 var1.write();
51 }
52 var2 = var1 * 2.54;
53 var2.write();
54 }
55 }
56 };
57
58 /********************************************************************************************************************/
59
60 /* Module which limits a value to stay below a maximum value. */
62 using ctk::ApplicationModule::ApplicationModule;
63
64 ctk::ScalarPushInputWB<double> var2{this, "var2", "centimetres", "Some length, confined to a configurable range"};
65 ctk::ScalarPushInput<double> max{this, "max", "centimetres", "Maximum length"};
66 ctk::ScalarOutput<double> var3{this, "var3", "centimetres", "The limited length"};
67
68 void prepare() override {
69 incrementDataFaultCounter(); // force all outputs to invalid
70 writeAll(); // write initial values
71 decrementDataFaultCounter(); // validity according to input validity
72 }
73
74 void mainLoop() override {
75 auto group = readAnyGroup();
76 while(true) {
77 auto var = group.readAny();
78 bool write = var == var2.getId();
79 if(var2 > max) {
80 var2 = static_cast<double>(max);
81 var2.write();
82 write = true;
83 }
84 if(write) { // write only if var2 was received or the value was changed
85 // due to a reduced limit
86 var3 = static_cast<double>(var2);
87 var3.write();
88 }
89 }
90 }
91 };
92
93 /********************************************************************************************************************/
94
96 using ApplicationModule::ApplicationModule;
97
98 ctk::ScalarPushInput<int> var1{this, "var1", "inches", "A length, for some reason rounded to integer"};
99 ctk::ScalarOutput<int> varOut{this, "var1_out", "inches", "A length, for some reason rounded to integer"};
100
101 void mainLoop() override {
102 // Copy everything from in to out - this is done because the test runs in testable mode
103 // And would stall if we do not read var1 in here
104 // By propagating the value to varOut, it is possible to selectively read the values from the CS instead
105 // as before with the double connection "trick".
106 while(true) {
107 var1.read();
108 varOut = static_cast<int>(var1);
109 varOut.write();
110 }
111 }
112 };
113
114 /********************************************************************************************************************/
115
117 using ApplicationModule::ApplicationModule;
118
119 ctk::ScalarPushInputWB<int> var1{this, "/var1", "", "Input with funneled return channel"};
120 ctk::ScalarOutput<int> var1out{this, "var1out", "", ""};
121 ctk::ScalarPushInput<int> var1in{this, "var1in", "", ""};
122
123 void mainLoop() override {
124 // This module essentially splits up the forward and return channel of the PushInputWB "var1".
125 auto group = readAnyGroup();
126
127 auto change = var1.getId();
128
129 while(true) {
130 if(change == var1.getId()) {
132 }
133 else if(change == var1in.getId()) {
135 }
136
137 change = group.readAny();
138 }
139 }
140 };
141
142 /********************************************************************************************************************/
143
152
153 /********************************************************************************************************************/
154
156 FunnelApplication() : Application("testSuite") {}
157 ~FunnelApplication() override { shutdown(); }
158
159 ModuleFunnel f1{this, "Funnel1", ""};
160 ModuleFunnel f2{this, "Funnel2", ""};
161 };
162
163 /********************************************************************************************************************/
164
166 using ctk::ApplicationModule::ApplicationModule;
167
168 ctk::ScalarPushInputWB<int> var1{this, "var1", "", ""};
169
170 void mainLoop() override {
171 auto group = readAnyGroup();
172
173 var1 = 42;
174 var1.write();
175
176 while(true) {
177 auto var = group.readAny();
178 if(var == var1.getId()) {
179 var1 = var1 + 1;
180 var1.write();
181 }
182 }
183 }
184 };
185
186 /********************************************************************************************************************/
187
191
192 ModuleC c{this, "ModuleC", ""};
193 };
194
195 /********************************************************************************************************************/
196
197 BOOST_AUTO_TEST_CASE(testDirectAppToCSConnections) {
198 std::cout << "*** testDirectAppToCSConnections" << std::endl;
199
200 TestApplication app;
201 app.b = {&app, ".", ""};
202
203 ctk::TestFacility test(app);
204 test.runApplication();
205 auto var2 = test.getScalar<double>("var2");
206 auto var3 = test.getScalar<double>("var3");
207 auto max = test.getScalar<double>("max");
208
209 // set maximum in B
210 max = 49.5;
211 max.write();
212 test.stepApplication();
213
214 // inject value which does not get limited
215 var2 = 49;
216 var2.write();
217 test.stepApplication();
218 var3.read();
219 BOOST_CHECK_CLOSE(static_cast<double>(var3), 49, 0.001);
220 BOOST_CHECK(var2.readNonBlocking() == false);
221 BOOST_CHECK(var3.readNonBlocking() == false);
222
223 // inject value which gets limited
224 var2 = 50;
225 var2.write();
226 test.stepApplication();
227 var2.read();
228 BOOST_CHECK_CLOSE(static_cast<double>(var2), 49.5, 0.001);
229 var3.read();
230 BOOST_CHECK_CLOSE(static_cast<double>(var3), 49.5, 0.001);
231 BOOST_CHECK(var2.readNonBlocking() == false);
232 BOOST_CHECK(var3.readNonBlocking() == false);
233
234 // change the limit so the current value gets changed
235 max = 48.5;
236 max.write();
237 test.stepApplication();
238 var2.read();
239 BOOST_CHECK_CLOSE(static_cast<double>(var2), 48.5, 0.001);
240 var3.read();
241 BOOST_CHECK_CLOSE(static_cast<double>(var3), 48.5, 0.001);
242 BOOST_CHECK(var2.readNonBlocking() == false);
243 BOOST_CHECK(var3.readNonBlocking() == false);
244 }
245
246 /********************************************************************************************************************/
247
248 BOOST_AUTO_TEST_CASE(testRealisticExample) {
249 std::cout << "*** testRealisticExample" << std::endl;
250
251 TestApplication app;
252 app.a = {&app, ".", ""};
253 app.b = {&app, ".", ""};
254 app.copy = {&app, ".", ""};
255
256 ctk::TestFacility test(app);
257 auto var1 = test.getScalar<int>("var1");
258 auto var1_copied = test.getScalar<int>("var1_out");
259 auto var2 = test.getScalar<double>("var2");
260 auto var3 = test.getScalar<double>("var3");
261 auto max = test.getScalar<double>("max");
262 test.runApplication();
263
264 // set maximum in B, so that var1=49 is still below maximum but var2=50 is
265 // already above and rounding in ModuleB will change the value again
266 max = 49.5 * 2.54;
267 max.write();
268 test.stepApplication();
269
270 // inject value which does not get limited
271 var1 = 49;
272 var1.write();
273 test.stepApplication();
274 var1_copied.read();
275 var2.read();
276 var3.read();
277 BOOST_CHECK_EQUAL(static_cast<int>(var1_copied), 49);
278 BOOST_CHECK_CLOSE(static_cast<double>(var2), 49 * 2.54, 0.001);
279 BOOST_CHECK_CLOSE(static_cast<double>(var3), 49 * 2.54, 0.001);
280 BOOST_CHECK(var1.readNonBlocking() == false); // nothing was sent through the return channel
281 BOOST_CHECK(var1_copied.readLatest() == false);
282 BOOST_CHECK(var2.readNonBlocking() == false);
283 BOOST_CHECK(var3.readNonBlocking() == false);
284
285 // inject value which gets limited
286 var1 = 50;
287 var1.write();
288 test.stepApplication();
289 var1.read();
290 BOOST_CHECK_EQUAL(static_cast<int>(var1), 49);
291 var1_copied.read();
292 BOOST_CHECK_EQUAL(static_cast<int>(var1_copied), 50);
293 var1_copied.read();
294 BOOST_CHECK_EQUAL(static_cast<int>(var1_copied), 49);
295 var2.read();
296 BOOST_CHECK_CLOSE(static_cast<double>(var2), 50 * 2.54, 0.001);
297 var2.read();
298 BOOST_CHECK_CLOSE(static_cast<double>(var2), 49.5 * 2.54, 0.001);
299 var2.read();
300 BOOST_CHECK_CLOSE(static_cast<double>(var2), 49 * 2.54, 0.001);
301 var3.read();
302 BOOST_CHECK_CLOSE(static_cast<double>(var3), 49.5 * 2.54, 0.001);
303 var3.read();
304 BOOST_CHECK_CLOSE(static_cast<double>(var3), 49 * 2.54, 0.001);
305 BOOST_CHECK(var1.readNonBlocking() == false);
306 BOOST_CHECK(var1_copied.readLatest() == false);
307 BOOST_CHECK(var2.readNonBlocking() == false);
308 BOOST_CHECK(var3.readNonBlocking() == false);
309
310 // change the limit so the current value gets changed
311 max = 48.5 * 2.54;
312 max.write();
313 test.stepApplication();
314 var1.read();
315 BOOST_CHECK_EQUAL(static_cast<int>(var1), 48);
316 var1_copied.read();
317 BOOST_CHECK_EQUAL(static_cast<int>(var1_copied), 48);
318 var2.read();
319 BOOST_CHECK_CLOSE(static_cast<double>(var2), 48.5 * 2.54, 0.001);
320 var2.read();
321 BOOST_CHECK_CLOSE(static_cast<double>(var2), 48 * 2.54, 0.001);
322 var3.read();
323 BOOST_CHECK_CLOSE(static_cast<double>(var3), 48.5 * 2.54, 0.001);
324 var3.read();
325 BOOST_CHECK_CLOSE(static_cast<double>(var3), 48 * 2.54, 0.001);
326 BOOST_CHECK(var1.readNonBlocking() == false);
327 BOOST_CHECK(var1_copied.readLatest() == false);
328 BOOST_CHECK(var2.readNonBlocking() == false);
329 BOOST_CHECK(var3.readNonBlocking() == false);
330
331 // Run the following tests a couple of times, as they are testing for the
332 // absence of race conditions. This makes it more likely to find failures in a
333 // single run of the test
334 for(size_t i = 0; i < 10; ++i) {
335 // feed in some default values (so the tests can be executed multiple times
336 // in a row)
337 max = 48.5 * 2.54;
338 max.write();
339 test.stepApplication();
340 var1 = 50;
341 var1.write();
342 test.stepApplication();
343 var1.readLatest(); // empty the queues
344 var1_copied.readLatest();
345 var2.readLatest();
346 var3.readLatest();
347 BOOST_CHECK_EQUAL(static_cast<int>(var1), 48);
348 BOOST_CHECK_EQUAL(static_cast<int>(var1_copied), 48);
349 BOOST_CHECK_CLOSE(static_cast<double>(var2), 48 * 2.54, 0.001);
350 BOOST_CHECK_CLOSE(static_cast<double>(var3), 48 * 2.54, 0.001);
351 BOOST_CHECK(var1.readNonBlocking() == false);
352 BOOST_CHECK(var1_copied.readLatest() == false);
353 BOOST_CHECK(var2.readNonBlocking() == false);
354 BOOST_CHECK(var3.readNonBlocking() == false);
355
356 // concurrent change of value and limit. Note: The final result must be
357 // deterministic, but which values are seen in between is subject to race
358 // conditions between the two concurrent updates. Thus we are using
359 // readLatest() in some cases here.
360 var1 = 30;
361 max = 25.5 * 2.54;
362 var1.write();
363 max.write();
364 test.stepApplication();
365 var1.read();
366 BOOST_CHECK_EQUAL(static_cast<int>(var1), 25);
367 var1_copied.read();
368 BOOST_CHECK_EQUAL(static_cast<int>(var1_copied), 30);
369 BOOST_CHECK(var1_copied.readLatest() == true);
370 BOOST_CHECK_EQUAL(static_cast<int>(var1_copied), 25);
371 BOOST_CHECK(var2.readLatest() == true);
372 BOOST_CHECK_CLOSE(static_cast<double>(var2), 25 * 2.54, 0.001);
373 BOOST_CHECK(var3.readLatest() == true);
374 BOOST_CHECK_CLOSE(static_cast<double>(var3), 25 * 2.54, 0.001);
375 BOOST_CHECK(var1.readNonBlocking() == false);
376 BOOST_CHECK(var1_copied.readLatest() == false);
377 BOOST_CHECK(var2.readNonBlocking() == false);
378 BOOST_CHECK(var3.readNonBlocking() == false);
379
380 // concurrent change of value and limit - other order than before
381 var1 = 15;
382 max = 20.5 * 2.54;
383 max.write();
384 var1.write();
385 test.stepApplication();
386 var1_copied.read();
387 BOOST_CHECK_EQUAL(static_cast<int>(var1_copied), 15);
388 BOOST_CHECK(var2.readLatest() == true);
389 BOOST_CHECK_CLOSE(static_cast<double>(var2), 15 * 2.54, 0.001);
390 BOOST_CHECK(var3.readLatest() == true);
391 BOOST_CHECK_CLOSE(static_cast<double>(var3), 15 * 2.54, 0.001);
392 BOOST_CHECK(var1.readNonBlocking() == false);
393 BOOST_CHECK(var1_copied.readLatest() == false);
394 BOOST_CHECK(var2.readNonBlocking() == false);
395 BOOST_CHECK(var3.readNonBlocking() == false);
396 }
397 }
398
399 /********************************************************************************************************************/
400
402 std::cout << "*** testFunnel" << std::endl;
403
405
406 ctk::TestFacility test(app);
407
408 auto var1 = test.getScalar<int>("var1");
409 auto funnel1out = test.getScalar<int>("/Funnel1/var1out");
410 auto funnel1in = test.getScalar<int>("/Funnel1/var1in");
411 auto funnel2out = test.getScalar<int>("/Funnel2/var1out");
412 auto funnel2in = test.getScalar<int>("/Funnel2/var1in");
413
414 test.runApplication();
415
416 // discard initial values
417 funnel1out.readLatest();
418 funnel2out.readLatest();
419
420 var1.setAndWrite(42);
421 test.stepApplication();
422 BOOST_TEST(!var1.readNonBlocking());
423 BOOST_TEST(funnel1out.readNonBlocking());
424 BOOST_TEST(funnel1out == 42);
425 BOOST_TEST(funnel2out.readNonBlocking());
426 BOOST_TEST(funnel2out == 42);
427
428 funnel1in.setAndWrite(43);
429 test.stepApplication();
430 BOOST_TEST(!funnel1out.readNonBlocking());
431 BOOST_TEST(var1.readNonBlocking());
432 BOOST_TEST(var1 == 43);
433 BOOST_TEST(funnel2out.readNonBlocking());
434 BOOST_TEST(funnel2out == 43);
435
436 funnel2in.setAndWrite(44);
437 test.stepApplication();
438 BOOST_TEST(!funnel2out.readNonBlocking());
439 BOOST_TEST(var1.readNonBlocking());
440 BOOST_TEST(var1 == 44);
441 BOOST_TEST(funnel1out.readNonBlocking());
442 BOOST_TEST(funnel1out == 44);
443 }
444
445 /********************************************************************************************************************/
446
447 BOOST_AUTO_TEST_CASE(testStartup) {
448 std::cout << "*** testStartup" << std::endl;
449
450 InitTestApplication testApp;
451 ChimeraTK::TestFacility testFacility(testApp);
452
453 testFacility.setScalarDefault<int>("/ModuleC/var1", 22);
454
455 testFacility.runApplication();
456
457 // The default value should be overwritten when ModuleC enters its mainLoop
458 BOOST_CHECK_EQUAL(testFacility.readScalar<int>("ModuleC/var1"), 42);
459 }
460
461 /********************************************************************************************************************/
462
464 TestApplication2() : Application("testSuite") {}
465 ~TestApplication2() override { shutdown(); }
466
467 // void defineConnections() override { lower.connectTo(upper); }
468
469 template<typename ACCESSOR>
471 using ctk::ApplicationModule::ApplicationModule;
472
473 ACCESSOR var{this, "var", "", ""};
474
475 bool sendInitialValue{ctk::VariableNetworkNode(var).getDirection().dir == ctk::VariableDirection::feeding};
476 void prepare() override {
477 if(sendInitialValue) {
478 var.write();
479 }
480 }
481
482 boost::latch mainLoopStarted{1};
483 void mainLoop() override { mainLoopStarted.count_down(); }
484 };
487 };
488
489 /********************************************************************************************************************/
490
491 BOOST_AUTO_TEST_CASE(testReadWriteAll) {
492 std::cout << "*** testReadWriteAll" << std::endl;
493
495 ChimeraTK::TestFacility test{app};
496
497 test.runApplication();
498
499 // forward channel writeAll/readAll
500 app.upper.var = 42;
501 app.upper.writeAll();
502 app.lower.readAll();
503 BOOST_CHECK_EQUAL(app.lower.var, 42);
504
505 // return channel writeAll
506 app.lower.var = 43;
507 app.lower.writeAll();
508 BOOST_CHECK(app.upper.var.readNonBlocking() == false);
509
510 // return channel readAll
511 app.lower.var.write();
512 app.upper.readAll();
513 BOOST_CHECK_NE(app.upper.var, 43);
514 BOOST_CHECK(app.upper.var.readNonBlocking() == true);
515 }
516
517 /********************************************************************************************************************/
518
519 BOOST_AUTO_TEST_CASE(testDataValidityReturn) {
520 std::cout << "*** testDataValidityReturn" << std::endl;
521
522 // forward channel
523 {
525 ChimeraTK::TestFacility test{app};
526
527 test.runApplication();
528 assert(app.lower.getDataValidity() == ctk::DataValidity::ok);
529
530 app.upper.incrementDataFaultCounter();
531 app.upper.var = 666;
532 app.upper.var.write();
533 app.upper.decrementDataFaultCounter();
534 app.lower.var.read();
535 BOOST_CHECK(app.lower.var.dataValidity() == ctk::DataValidity::faulty);
536 BOOST_CHECK(app.lower.getDataValidity() == ctk::DataValidity::faulty);
537 }
538
539 // return channel
540 {
542 ChimeraTK::TestFacility test{app};
543
544 test.runApplication();
545 assert(app.upper.getDataValidity() == ctk::DataValidity::ok);
546 app.lower.incrementDataFaultCounter();
547 app.lower.var = 120;
548 app.lower.var.write();
549 app.upper.var.read();
550 BOOST_CHECK(app.upper.var.dataValidity() == ctk::DataValidity::ok);
551 BOOST_CHECK(app.upper.getDataValidity() == ctk::DataValidity::ok);
552 app.lower.decrementDataFaultCounter();
553
554 // Manually setting the validity of the return channel
555 app.lower.var = 130;
556 app.lower.var.setDataValidity(ctk::DataValidity::faulty);
557 app.lower.var.write();
558 app.upper.var.read();
559 BOOST_CHECK(app.upper.var.dataValidity() == ctk::DataValidity::faulty);
560 BOOST_CHECK(app.upper.getDataValidity() == ctk::DataValidity::faulty);
561
562 //=====================================================================
563 }
564 }
565
566 /********************************************************************************************************************/
567
568 BOOST_AUTO_TEST_CASE(testInitialValues) {
569 std::cout << "*** testInitialValues" << std::endl;
570
572 app.upper.sendInitialValue = false;
573 ChimeraTK::TestFacility test(app, false);
574
575 test.runApplication();
576
577 // return channel: upper must start without lower sending anything through the return channel
578 CHECK_TIMEOUT(app.upper.mainLoopStarted.try_wait(), 10000);
579
580 // forward channel: lower must not start without upper sending the initial value
581 usleep(10000);
582 BOOST_CHECK(!app.lower.mainLoopStarted.try_wait());
583 app.upper.var = 666;
584 app.upper.var.write();
585 CHECK_TIMEOUT(app.lower.mainLoopStarted.try_wait(), 10000);
586 BOOST_CHECK_EQUAL(app.lower.var, 666);
587 }
588
589 /********************************************************************************************************************/
590 /********************************************************************************************************************/
591
594
595 ChimeraTK::ScalarOutputPushRB<int> out{this, "/output", "", ""};
596 ChimeraTK::ScalarPushInput<int> in{this, "/input", "", ""};
597 void mainLoop() override {
598 auto g = readAnyGroup();
599 ChimeraTK::TransferElementID id;
600 while(true) {
602 id = g.readAny();
603 }
604 }
605 };
606
607 /********************************************************************************************************************/
608
611
612 ChimeraTK::ScalarPushInputWB<int> in{this, "/output", "", ""};
613 void mainLoop() override {
614 auto g = readAnyGroup();
615 while(true) {
616 g.readAny();
617 }
618 }
619 };
620
621 /********************************************************************************************************************/
622
630
631 /********************************************************************************************************************/
632
633 BOOST_AUTO_TEST_CASE(testShutdownWithFeedingFanOut) {
634 // This test checks that the FeedingFanOut does not try to propagate the boost::thread_interrupted exception through
635 // the return channel, which will fail with a logic_error (in this particular case) because the return channel has
636 // not yet been written yet and hence its VersionNumber is still 0.
637
638 TestApplicationShutdownIssue app("TestApplicationShutdownIssue");
639 ChimeraTK::TestFacility test(app, true);
640 test.runApplication();
641
642 test.writeScalar("/input", 1);
643 test.stepApplication();
644
645 std::cout << "Will shutdown now" << std::endl;
646 }
647
648 /********************************************************************************************************************/
649 /********************************************************************************************************************/
650
651} // namespace Tests::testBidirectionalVariables
void shutdown() override
This will remove the global pointer to the instance and allows creating another instance afterwards.
ApplicationModule()=default
Default constructor: Allows late initialisation of modules (e.g.
void decrementDataFaultCounter() override
Decrement the fault counter and set the data validity flag to ok if the counter has reached 0.
void incrementDataFaultCounter() override
Set the data validity flag to fault and increment the fault counter.
friend class Application
Definition ModuleGroup.h:47
ChimeraTK::ReadAnyGroup readAnyGroup()
Create a ChimeraTK::ReadAnyGroup for all readable variables in this Module.
Definition Module.cc:54
void writeAll(bool includeReturnChannels=false)
Just call write() on all writable variables in the group.
Definition Module.cc:157
bool write(ChimeraTK::VersionNumber versionNumber)=delete
void setAndWrite(UserType newValue, VersionNumber versionNumber)=delete
Convenience class for input scalar accessors with UpdateMode::push.
Convenience class for input scalar accessors with return channel ("write back") and UpdateMode::push.
Helper class to facilitate tests of applications based on ApplicationCore.
TYPE readScalar(const std::string &name)
Convenience function to read the latest value of a scalar process variable in a single call.
void writeScalar(const std::string &name, TYPE value)
Convenience function to write a scalar process variable in a single call.
void stepApplication(bool waitForDeviceInitialisation=true) const
Perform a "step" of the application.
ChimeraTK::ScalarRegisterAccessor< T > getScalar(const ChimeraTK::RegisterPath &name) const
Obtain a scalar process variable from the application, which is published to the control system.
void runApplication() const
Start the application in testable mode.
void setScalarDefault(const ChimeraTK::RegisterPath &name, const T &value)
Set default value for scalar process variable.
Class describing a node of a variable network.
VariableDirection getDirection() const
InvalidityTracer application module.
BOOST_AUTO_TEST_CASE(testDirectAppToCSConnections)
Convenience class for output scalar accessors (always UpdateMode::push)
Convenience class for output scalar accessors with return channel ("read back") (always UpdateMode::p...
enum ChimeraTK::VariableDirection::@0 dir
Enum to define directions of variables.
void mainLoop() override
To be implemented by the user: function called in a separate thread executing the main loop of the mo...
void prepare() override
Prepare the execution of the module.
void prepare() override
Prepare the execution of the module.
void mainLoop() override
To be implemented by the user: function called in a separate thread executing the main loop of the mo...
void mainLoop() override
To be implemented by the user: function called in a separate thread executing the main loop of the mo...
void mainLoop() override
To be implemented by the user: function called in a separate thread executing the main loop of the mo...
void mainLoop() override
To be implemented by the user: function called in a separate thread executing the main loop of the mo...
void mainLoop() override
To be implemented by the user: function called in a separate thread executing the main loop of the mo...
void mainLoop() override
To be implemented by the user: function called in a separate thread executing the main loop of the mo...
void mainLoop() override
To be implemented by the user: function called in a separate thread executing the main loop of the mo...
void prepare() override
Prepare the execution of the module.
#define CHECK_TIMEOUT(condition, maxMilliseconds)