ChimeraTK-ApplicationCore 04.08.00
Loading...
Searching...
No Matches
testPropagateDataFaultFlag.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 testPropagateDataFaultFlag
6
7#include "Application.h"
8#include "ApplicationModule.h"
9#include "ArrayAccessor.h"
10#include "check_timeout.h"
11#include "DeviceModule.h"
12#include "ScalarAccessor.h"
13#include "StatusAccessor.h"
14#include "TestFacility.h"
15#include "VariableGroup.h"
16
17#include <ChimeraTK/BackendFactory.h>
18#include <ChimeraTK/Device.h>
19#include <ChimeraTK/DummyRegisterAccessor.h>
20#include <ChimeraTK/ExceptionDummyBackend.h>
21#include <ChimeraTK/NDRegisterAccessor.h>
22
23#include <boost/mpl/list.hpp>
24
25#define BOOST_NO_EXCEPTIONS
26#include <boost/test/included/unit_test.hpp>
27#undef BOOST_NO_EXCEPTIONS
28
30
31 using namespace boost::unit_test_framework;
32 namespace ctk = ChimeraTK;
33
34 /* dummy application */
35
36 /********************************************************************************************************************/
37
39 using ctk::ApplicationModule::ApplicationModule;
40 ctk::ScalarPushInput<int> i1{this, "i1", "", ""};
41 ctk::ArrayPushInput<int> i2{this, "i2", "", 2, ""};
42 ctk::ScalarPushInputWB<int> i3{this, "i3", "", ""};
43 ctk::ScalarOutput<int> o1{this, "o1", "", ""};
44 ctk::ArrayOutput<int> o2{this, "o2", "", 2, ""};
45 ctk::StatusOutput oStat{this, "oStat", ""};
46 void mainLoop() override {
47 auto group = readAnyGroup();
48 while(true) {
49 if(i3 > 10) {
50 i3 = 10;
51 i3.write();
52 }
53 o1 = int(i1);
54 o2[0] = i2[0];
55 o2[1] = i2[1];
56 o1.write();
57 o2.write();
58 if(i1 < 0) {
59 oStat.setDataValidity(ctk::DataValidity::faulty);
60 }
61 oStat.write();
62 group.readAny();
63 }
64 }
65 };
66
67 /********************************************************************************************************************/
68
70 using ctk::ApplicationModule::ApplicationModule;
71 ctk::ScalarPushInput<int> i1{this, "i1", "", ""};
72 ctk::ArrayPushInput<int> i2{this, "i2", "", 2, ""};
73 ctk::ScalarPushInputWB<int> i3{this, "i3", "", ""};
74 ctk::ScalarPushInput<int> o1{this, "o1", "", ""};
75 ctk::ArrayPushInput<int> o2{this, "o2", "", 2, ""};
76 void mainLoop() override {
77 auto group = readAnyGroup();
78 while(true) {
79 group.readAny();
80 }
81 }
82 };
83
84 /********************************************************************************************************************/
85
87 TestApplication1() : Application("testSuite") {}
88 ~TestApplication1() override { shutdown(); }
89
90 TestModule1 t1{this, "t1", ""};
91 };
92
93 /********************************************************************************************************************/
94
96 TestApplication2() : Application("testSuite") {}
97 ~TestApplication2() override { shutdown(); }
98
99 TestModule1 a{this, "A", ""};
100 TestModule2 b{this, "A", ""};
101 };
102
103 /********************************************************************************************************************/
104 /********************************************************************************************************************/
105
106 // first test without FanOuts of any kind
107 BOOST_AUTO_TEST_CASE(testDirectConnections) {
108 std::cout << "testDirectConnections" << std::endl;
110 ctk::TestFacility test(app);
111
112 auto i1 = test.getScalar<int>("/t1/i1");
113 auto i2 = test.getArray<int>("/t1/i2");
114 auto i3 = test.getScalar<int>("/t1/i3");
115 auto o1 = test.getScalar<int>("/t1/o1");
116 auto o2 = test.getArray<int>("/t1/o2");
117 auto oStat = test.getArray<int>("/t1/oStat");
118
119 test.runApplication();
120
121 // test if fault flag propagates to all outputs
122 i1 = 1;
123 i1.setDataValidity(ctk::DataValidity::faulty);
124 i1.write();
125 test.stepApplication();
126 o1.read();
127 o2.read();
128 oStat.read();
129 BOOST_CHECK(o1.dataValidity() == ctk::DataValidity::faulty);
130 BOOST_CHECK(o2.dataValidity() == ctk::DataValidity::faulty);
131 BOOST_CHECK(oStat.dataValidity() == ctk::DataValidity::faulty);
132 BOOST_CHECK_EQUAL(int(o1), 1);
133 BOOST_CHECK_EQUAL(o2[0], 0);
134 BOOST_CHECK_EQUAL(o2[1], 0);
135
136 // write another value but keep fault flag
137 i1 = 42;
138 BOOST_CHECK(i1.dataValidity() == ctk::DataValidity::faulty);
139 i1.write();
140 test.stepApplication();
141 o1.read();
142 o2.read();
143 BOOST_CHECK(o1.dataValidity() == ctk::DataValidity::faulty);
144 BOOST_CHECK(o2.dataValidity() == ctk::DataValidity::faulty);
145 BOOST_CHECK_EQUAL(int(o1), 42);
146 BOOST_CHECK_EQUAL(o2[0], 0);
147 BOOST_CHECK_EQUAL(o2[1], 0);
148
149 // a write on the ok variable should not clear the flag
150 i2[0] = 10;
151 i2[1] = 11;
152 BOOST_CHECK(i2.dataValidity() == ctk::DataValidity::ok);
153 i2.write();
154 test.stepApplication();
155 o1.read();
156 o2.read();
157 BOOST_CHECK(o1.dataValidity() == ctk::DataValidity::faulty);
158 BOOST_CHECK(o2.dataValidity() == ctk::DataValidity::faulty);
159 BOOST_CHECK_EQUAL(int(o1), 42);
160 BOOST_CHECK_EQUAL(o2[0], 10);
161 BOOST_CHECK_EQUAL(o2[1], 11);
162
163 // the return channel does not receive the flag
164 BOOST_CHECK(i3.readNonBlocking() == false);
165 BOOST_CHECK(i3.dataValidity() == ctk::DataValidity::ok);
166 i3 = 20;
167 i3.write();
168 test.stepApplication();
169 o1.read();
170 o2.read();
171 i3.read();
172 BOOST_CHECK(o1.dataValidity() == ctk::DataValidity::faulty);
173 BOOST_CHECK(o2.dataValidity() == ctk::DataValidity::faulty);
174 BOOST_CHECK(i3.dataValidity() == ctk::DataValidity::ok);
175 BOOST_CHECK_EQUAL(int(o1), 42);
176 BOOST_CHECK_EQUAL(o2[0], 10);
177 BOOST_CHECK_EQUAL(o2[1], 11);
178 BOOST_CHECK_EQUAL(int(i3), 10);
179
180 // clear the flag on i1, i3 does not get it
181 i1 = 3;
182 i1.setDataValidity(ctk::DataValidity::ok);
183 i1.write();
184 test.stepApplication();
185 o1.read();
186 o2.read();
187 BOOST_CHECK(i3.readNonBlocking() == false);
188 BOOST_CHECK(o1.dataValidity() == ctk::DataValidity::ok);
189 BOOST_CHECK(o2.dataValidity() == ctk::DataValidity::ok);
190 BOOST_CHECK_EQUAL(int(o1), 3);
191 BOOST_CHECK_EQUAL(o2[0], 10);
192 BOOST_CHECK_EQUAL(o2[1], 11);
193 BOOST_CHECK(i3.dataValidity() == ctk::DataValidity::ok);
194 BOOST_CHECK_EQUAL(int(i3), 10);
195
196 // send two data fault flags. both need to be cleared before the outputs go back to ok
197 i1 = 120;
198 i1.setDataValidity(ctk::DataValidity::faulty);
199 i1.write();
200 i3 = 121;
201 i3.setDataValidity(ctk::DataValidity::faulty);
202 i3.write();
203 test.stepApplication();
204 o1.readLatest();
205 o2.readLatest();
206 i3.read();
207 BOOST_CHECK(o1.dataValidity() == ctk::DataValidity::faulty);
208 BOOST_CHECK(o2.dataValidity() == ctk::DataValidity::faulty);
209 BOOST_CHECK_EQUAL(int(o1), 120);
210 BOOST_CHECK_EQUAL(o2[0], 10);
211 BOOST_CHECK_EQUAL(o2[1], 11);
212 BOOST_CHECK(i3.dataValidity() == ctk::DataValidity::faulty);
213 BOOST_CHECK_EQUAL(int(i3), 10);
214
215 // clear first flag
216 i1 = 122;
217 i1.setDataValidity(ctk::DataValidity::ok);
218 i1.write();
219 test.stepApplication();
220 o1.read();
221 o2.read();
222 BOOST_CHECK(i3.readNonBlocking() == false);
223 BOOST_CHECK(o1.dataValidity() == ctk::DataValidity::faulty);
224 BOOST_CHECK(o2.dataValidity() == ctk::DataValidity::faulty);
225 BOOST_CHECK_EQUAL(int(o1), 122);
226 BOOST_CHECK_EQUAL(o2[0], 10);
227 BOOST_CHECK_EQUAL(o2[1], 11);
228 BOOST_CHECK(i3.dataValidity() == ctk::DataValidity::faulty);
229 BOOST_CHECK_EQUAL(int(i3), 10);
230
231 // clear second flag
232 i3 = 123;
233 i3.setDataValidity(ctk::DataValidity::ok);
234 i3.write();
235 test.stepApplication();
236 o1.read();
237 o2.read();
238 i3.read();
239 BOOST_CHECK(o1.dataValidity() == ctk::DataValidity::ok);
240 BOOST_CHECK(o2.dataValidity() == ctk::DataValidity::ok);
241 BOOST_CHECK_EQUAL(int(o1), 122);
242 BOOST_CHECK_EQUAL(o2[0], 10);
243 BOOST_CHECK_EQUAL(o2[1], 11);
244 BOOST_CHECK(i3.dataValidity() == ctk::DataValidity::ok);
245 BOOST_CHECK_EQUAL(int(i3), 10);
246
247 // check that oStat can also receive data faulty, if set explicitly
248 i1 = -1;
249 i1.write();
250 test.stepApplication();
251 oStat.readLatest();
252 BOOST_CHECK(oStat.dataValidity() == ctk::DataValidity::faulty);
253 }
254
255 /********************************************************************************************************************/
256
257 BOOST_AUTO_TEST_CASE(testWithFanOut) {
258 std::cout << "testWithFanOut" << std::endl;
260 ctk::TestFacility test(app);
261
262 auto Ai1 = test.getScalar<int>("A/i1");
263 auto Ai2 = test.getArray<int>("A/i2");
264 auto Ai3 = test.getScalar<int>("A/i3");
265 auto Ao1 = test.getScalar<int>("A/o1");
266 auto Ao2 = test.getArray<int>("A/o2");
267
268 test.runApplication();
269
270 // app.dumpConnections();
271
272 // test if fault flag propagates to all outputs
273 Ai1 = 1;
274 Ai1.setDataValidity(ctk::DataValidity::faulty);
275 Ai1.write();
276 test.stepApplication();
277 Ao1.read();
278 Ao2.read();
279 BOOST_CHECK(Ao1.dataValidity() == ctk::DataValidity::faulty);
280 BOOST_CHECK(Ao2.dataValidity() == ctk::DataValidity::faulty);
281 BOOST_CHECK_EQUAL(int(Ao1), 1);
282 BOOST_CHECK_EQUAL(Ao2[0], 0);
283 BOOST_CHECK_EQUAL(Ao2[1], 0);
284 BOOST_CHECK(app.b.o1.dataValidity() == ctk::DataValidity::faulty);
285 BOOST_CHECK(app.b.o2.dataValidity() == ctk::DataValidity::faulty);
286 BOOST_CHECK_EQUAL(int(app.b.o1), 1);
287 BOOST_CHECK_EQUAL(app.b.o2[0], 0);
288 BOOST_CHECK_EQUAL(app.b.o2[1], 0);
289 BOOST_CHECK(app.b.i1.dataValidity() == ctk::DataValidity::faulty);
290 BOOST_CHECK_EQUAL(int(app.b.i1), 1);
291
292 // send fault flag on a second variable
293 Ai2[0] = 2;
294 Ai2[1] = 3;
295 Ai2.setDataValidity(ctk::DataValidity::faulty);
296 Ai2.write();
297 test.stepApplication();
298 Ao1.read();
299 Ao2.read();
300 BOOST_CHECK(Ao1.dataValidity() == ctk::DataValidity::faulty);
301 BOOST_CHECK(Ao2.dataValidity() == ctk::DataValidity::faulty);
302 BOOST_CHECK_EQUAL(int(Ao1), 1);
303 BOOST_CHECK_EQUAL(Ao2[0], 2);
304 BOOST_CHECK_EQUAL(Ao2[1], 3);
305 BOOST_CHECK(app.b.o1.dataValidity() == ctk::DataValidity::faulty);
306 BOOST_CHECK(app.b.o2.dataValidity() == ctk::DataValidity::faulty);
307 BOOST_CHECK_EQUAL(int(app.b.o1), 1);
308 BOOST_CHECK_EQUAL(app.b.o2[0], 2);
309 BOOST_CHECK_EQUAL(app.b.o2[1], 3);
310 BOOST_CHECK(app.b.i2.dataValidity() == ctk::DataValidity::faulty);
311 BOOST_CHECK_EQUAL(app.b.i2[0], 2);
312 BOOST_CHECK_EQUAL(app.b.i2[1], 3);
313
314 // clear fault flag on a second variable
315 Ai2[0] = 4;
316 Ai2[1] = 5;
317 Ai2.setDataValidity(ctk::DataValidity::ok);
318 Ai2.write();
319 test.stepApplication();
320 Ao1.read();
321 Ao2.read();
322 BOOST_CHECK(Ao1.dataValidity() == ctk::DataValidity::faulty);
323 BOOST_CHECK(Ao2.dataValidity() == ctk::DataValidity::faulty);
324 BOOST_CHECK_EQUAL(int(Ao1), 1);
325 BOOST_CHECK_EQUAL(Ao2[0], 4);
326 BOOST_CHECK_EQUAL(Ao2[1], 5);
327 BOOST_CHECK(app.b.o1.dataValidity() == ctk::DataValidity::faulty);
328 BOOST_CHECK(app.b.o2.dataValidity() == ctk::DataValidity::faulty);
329 BOOST_CHECK_EQUAL(int(app.b.o1), 1);
330 BOOST_CHECK_EQUAL(app.b.o2[0], 4);
331 BOOST_CHECK_EQUAL(app.b.o2[1], 5);
332 BOOST_CHECK(app.b.i2.dataValidity() == ctk::DataValidity::ok);
333 BOOST_CHECK_EQUAL(app.b.i2[0], 4);
334 BOOST_CHECK_EQUAL(app.b.i2[1], 5);
335
336 // clear fault flag on a first variable
337 Ai1 = 6;
338 Ai1.setDataValidity(ctk::DataValidity::ok);
339 Ai1.write();
340 test.stepApplication();
341 Ao1.read();
342 Ao2.read();
343 BOOST_CHECK(Ao1.dataValidity() == ctk::DataValidity::ok);
344 BOOST_CHECK(Ao2.dataValidity() == ctk::DataValidity::ok);
345 BOOST_CHECK_EQUAL(int(Ao1), 6);
346 BOOST_CHECK_EQUAL(Ao2[0], 4);
347 BOOST_CHECK_EQUAL(Ao2[1], 5);
348 BOOST_CHECK(app.b.o1.dataValidity() == ctk::DataValidity::ok);
349 BOOST_CHECK(app.b.o2.dataValidity() == ctk::DataValidity::ok);
350 BOOST_CHECK_EQUAL(int(app.b.o1), 6);
351 BOOST_CHECK_EQUAL(app.b.o2[0], 4);
352 BOOST_CHECK_EQUAL(app.b.o2[1], 5);
353 BOOST_CHECK(app.b.i1.dataValidity() == ctk::DataValidity::ok);
354 BOOST_CHECK_EQUAL(int(app.b.i1), 6);
355 }
356
357 /********************************************************************************************************************/
358 /********************************************************************************************************************/
359 /*
360 * Tests below verify data fault flag propagation on:
361 * - Threaded FanOut
362 * - Consuming FanOut
363 * - Triggers
364 */
365
367 using ctk::ApplicationModule::ApplicationModule;
368 ctk::ScalarPushInput<int> fromThreadedFanout{this, "o1", "", "", {"DEVICE1", "CS"}};
369
370 // As a workaround the device side connection is done manually for
371 // acheiving this consumingFanout; see:
372 // TestApplication3::defineConnections
374
375 ctk::ScalarPollInput<int> fromDevice{this, "i2", "", "", {"DEVICE2"}};
376 ctk::ScalarOutput<int> result{this, "Module1_result", "", "", {"CS"}};
377
378 void mainLoop() override {
379 while(true) {
381 writeAll();
382 readAll(); // read last, so initial values are written in the first round
383 }
384 }
385 };
386
388 using ctk::ApplicationModule::ApplicationModule;
389
390 struct : public ctk::VariableGroup {
391 using ctk::VariableGroup::VariableGroup;
392 ctk::ScalarPushInput<int> result{this, "Module1_result", "", "", {"CS"}};
393 } m1VarsFromCS{this, "../m1", ""}; // "m1" being in there
394 // not good for a general case
395 ctk::ScalarOutput<int> result{this, "Module2_result", "", "", {"CS"}};
396
397 void mainLoop() override {
398 while(true) {
399 result = static_cast<int>(m1VarsFromCS.result);
400 writeAll();
401 readAll(); // read last, so initial values are written in the first round
402 }
403 }
404 };
405
406 // struct TestApplication3 : ctk::ApplicationModule {
408 /*
409 * CS +-----> threaded fanout +------------------+
410 * + v
411 * +---------+ +Device1+
412 * | | |
413 * Feeding v | |
414 * CS <----- fanout --+ Module1 <-----+ v |
415 * | ^ +Consuming |
416 * | +--------+ fanout |
417 * +------+ + + |
418 * v Device2 | |
419 * CS <-----------+ Module2 | |
420 * | |
421 * CS <-----------------------------------+ |
422 * |
423 * |
424 * CS <-----------+ Trigger fanout <------------------+
425 * ^
426 * |
427 * +
428 * CS
429 */
430
431 constexpr static char const* ExceptionDummyCDD1 = "(ExceptionDummy:1?map=testDataValidity1.map)";
432 constexpr static char const* ExceptionDummyCDD2 = "(ExceptionDummy:1?map=testDataValidity2.map)";
433 TestApplication3() : Application("testDataFlagPropagation") {}
434 ~TestApplication3() override { shutdown(); }
435
436 Module1 m1{this, "m1", ""};
437 Module2 m2{this, "m2", ""};
438
441
442 /* void defineConnections() override {
443 device1["m1"]("i1") >> m1("i1");
444 findTag("CS").connectTo(cs);
445 findTag("DEVICE1").connectTo(device1);
446 findTag("DEVICE2").connectTo(device2);
447 device1["m1"]("i3")[cs("trigger", typeid(int), 1)] >> cs("i3", typeid(int), 1);
448 }*/
449 };
450
451 /********************************************************************************************************************/
452 /********************************************************************************************************************/
453
456 : device1DummyBackend(boost::dynamic_pointer_cast<ctk::ExceptionDummy>(
457 ChimeraTK::BackendFactory::getInstance().createBackend(TestApplication3::ExceptionDummyCDD1))),
458 device2DummyBackend(boost::dynamic_pointer_cast<ctk::ExceptionDummy>(
459 ChimeraTK::BackendFactory::getInstance().createBackend(TestApplication3::ExceptionDummyCDD2))) {
460 device1DummyBackend->open();
461 device2DummyBackend->open();
463 }
464
466 device1DummyBackend->throwExceptionRead = false;
467 device2DummyBackend->throwExceptionWrite = false;
468 }
469
470 boost::shared_ptr<ctk::ExceptionDummy> device1DummyBackend;
471 boost::shared_ptr<ctk::ExceptionDummy> device2DummyBackend;
474 };
475
476 BOOST_FIXTURE_TEST_SUITE(data_validity_propagation, FixtureTestFacility)
477
478 /********************************************************************************************************************/
479
480 BOOST_AUTO_TEST_CASE(testThreadedFanout) {
481 std::cout << "testThreadedFanout" << std::endl;
482 auto threadedFanoutInput = test.getScalar<int>("m1/o1");
483 auto m1_result = test.getScalar<int>("m1/Module1_result");
484 auto m2_result = test.getScalar<int>("m2/Module2_result");
485
486 threadedFanoutInput = 20;
487 threadedFanoutInput.write();
488 // write to register: m1.i1 linked with the consumingFanout.
489 auto consumingFanoutSource =
490 ctk::Device(TestApplication3::ExceptionDummyCDD1).getScalarRegisterAccessor<int>("/m1/i1/DUMMY_WRITEABLE");
491 consumingFanoutSource = 10;
492 consumingFanoutSource.write();
493
494 auto pollRegister =
495 ctk::Device(TestApplication3::ExceptionDummyCDD2).getScalarRegisterAccessor<int>("/m1/i2/DUMMY_WRITEABLE");
496 pollRegister = 5;
497 pollRegister.write();
498
499 test.stepApplication();
500
501 m1_result.read();
502 m2_result.read();
503 BOOST_CHECK_EQUAL(m1_result, 35);
504 BOOST_CHECK(m1_result.dataValidity() == ctk::DataValidity::ok);
505
506 BOOST_CHECK_EQUAL(m2_result, 35);
507 BOOST_CHECK(m2_result.dataValidity() == ctk::DataValidity::ok);
508
509 threadedFanoutInput = 10;
510 threadedFanoutInput.setDataValidity(ctk::DataValidity::faulty);
511 threadedFanoutInput.write();
512 test.stepApplication();
513
514 m1_result.read();
515 m2_result.read();
516 BOOST_CHECK_EQUAL(m1_result, 25);
517 BOOST_CHECK(m1_result.dataValidity() == ctk::DataValidity::faulty);
518 BOOST_CHECK_EQUAL(m2_result, 25);
519 BOOST_CHECK(m2_result.dataValidity() == ctk::DataValidity::faulty);
520
521 threadedFanoutInput = 40;
522 threadedFanoutInput.setDataValidity(ctk::DataValidity::ok);
523 threadedFanoutInput.write();
524 test.stepApplication();
525
526 m1_result.read();
527 m2_result.read();
528 BOOST_CHECK_EQUAL(m1_result, 55);
529 BOOST_CHECK(m1_result.dataValidity() == ctk::DataValidity::ok);
530 BOOST_CHECK_EQUAL(m2_result, 55);
531 BOOST_CHECK(m2_result.dataValidity() == ctk::DataValidity::ok);
532 }
533
534 /********************************************************************************************************************/
535
536 BOOST_AUTO_TEST_CASE(testInvalidTrigger) {
537 std::cout << "testInvalidTrigger" << std::endl;
538
539 auto deviceRegister =
540 ctk::Device(TestApplication3::ExceptionDummyCDD1).getScalarRegisterAccessor<int>("/m1/i3/DUMMY_WRITEABLE");
541 deviceRegister = 20;
542 deviceRegister.write();
543
544 auto trigger = test.getVoid("trigger");
545 auto result = test.getScalar<int>("/m1/i3"); // Cs hook into reg: m1.i3
546
547 /*----------------------------------------------------------------------------------------------------------------*/
548 // trigger works as expected
549 trigger.write();
550
551 test.stepApplication();
552
553 result.read();
554 BOOST_CHECK_EQUAL(result, 20);
555 BOOST_CHECK(result.dataValidity() == ctk::DataValidity::ok);
556
557 /*----------------------------------------------------------------------------------------------------------------*/
558 // faulty trigger
559 deviceRegister = 30;
560 deviceRegister.write();
561 trigger.setDataValidity(ctk::DataValidity::faulty);
562 trigger.write();
563
564 test.stepApplication();
565
566 result.read();
567 BOOST_CHECK_EQUAL(result, 30);
568 BOOST_CHECK(result.dataValidity() == ctk::DataValidity::faulty);
569
570 /*----------------------------------------------------------------------------------------------------------------*/
571 // recovery
572 deviceRegister = 50;
573 deviceRegister.write();
574
575 trigger.setDataValidity(ctk::DataValidity::ok);
576 trigger.write();
577
578 test.stepApplication();
579
580 result.read();
581 BOOST_CHECK_EQUAL(result, 50);
582 BOOST_CHECK(result.dataValidity() == ctk::DataValidity::ok);
583 }
584
585 BOOST_AUTO_TEST_SUITE_END()
586
587 /********************************************************************************************************************/
588 /********************************************************************************************************************/
589
592 : device1DummyBackend(boost::dynamic_pointer_cast<ctk::ExceptionDummy>(
593 ChimeraTK::BackendFactory::getInstance().createBackend(TestApplication3::ExceptionDummyCDD1))),
594 device2DummyBackend(boost::dynamic_pointer_cast<ctk::ExceptionDummy>(
595 ChimeraTK::BackendFactory::getInstance().createBackend(TestApplication3::ExceptionDummyCDD2))) {
596 device1Status.replace(test.getScalar<int32_t>(ctk::RegisterPath("/Devices") /
597 ctk::Utilities::escapeName(TestApplication3::ExceptionDummyCDD1, false) / "status"));
598 device2Status.replace(test.getScalar<int32_t>(ctk::RegisterPath("/Devices") /
599 ctk::Utilities::escapeName(TestApplication3::ExceptionDummyCDD2, false) / "status"));
600
601 device1DummyBackend->open();
602 device2DummyBackend->open();
603 }
604
606 // the block below is a work around for a race condition; make sure
607 // all values are propagated to the device registers before starting
608 // the test.
609 static const int DEFAULT = 1234567;
610 test.setScalarDefault("m1/o1", DEFAULT);
611
612 test.runApplication();
613 CHECK_EQUAL_TIMEOUT((device1Status.readLatest(), device1Status), 0, 100000);
614 CHECK_EQUAL_TIMEOUT((device2Status.readLatest(), device2Status), 0, 100000);
615
616 // Making sure the default is written to the device before proceeding.
617 auto m1o1 = device1DummyBackend->getRegisterAccessor<int>("m1/o1", 1, 0, {});
618 CHECK_EQUAL_TIMEOUT((m1o1->read(), m1o1->accessData(0)), DEFAULT, 10000);
619 }
620
622 device1DummyBackend->throwExceptionRead = false;
623 device2DummyBackend->throwExceptionWrite = false;
624 }
625
626 boost::shared_ptr<ctk::ExceptionDummy> device1DummyBackend;
627 boost::shared_ptr<ctk::ExceptionDummy> device2DummyBackend;
629 ctk::TestFacility test{app, false};
630 ChimeraTK::ScalarRegisterAccessor<int> device1Status;
631 ChimeraTK::ScalarRegisterAccessor<int> device2Status;
632 };
633
634 /********************************************************************************************************************/
635
636 BOOST_AUTO_TEST_SUITE(data_validity_propagation_noTestFacility)
637
639 std::cout << "testDeviceReadFailure" << std::endl;
640 waitForDevices();
641
642 auto consumingFanoutSource =
643 ctk::Device(TestApplication3::ExceptionDummyCDD1).getScalarRegisterAccessor<int>("/m1/i1/DUMMY_WRITEABLE");
644 auto pollRegister =
645 ctk::Device(TestApplication3::ExceptionDummyCDD2).getScalarRegisterAccessor<int>("/m1/i2/DUMMY_WRITEABLE");
646
647 auto threadedFanoutInput = test.getScalar<int>("m1/o1");
648 auto result = test.getScalar<int>("m1/Module1_result");
649
650 threadedFanoutInput = 10000;
651 consumingFanoutSource = 1000;
652 consumingFanoutSource.write();
653 pollRegister = 1;
654 pollRegister.write();
655
656 // -------------------------------------------------------------//
657 // without errors
658 threadedFanoutInput.write();
659
660 CHECK_TIMEOUT((result.readLatest(), result == 11001), 10000);
661 BOOST_CHECK(result.dataValidity() == ctk::DataValidity::ok);
662
663 // -------------------------------------------------------------//
664 // device module exception
665 threadedFanoutInput = 20000;
666 pollRegister = 0;
667 pollRegister.write();
668
669 device2DummyBackend->throwExceptionRead = true;
670
671 threadedFanoutInput.write();
672 // The new value from the fanout input should have been propagated,
673 // the new value of the poll input is not seen, because it gets skipped
674 result.read();
675 BOOST_CHECK_EQUAL(result, 21001);
676 BOOST_CHECK(result.dataValidity() == ctk::DataValidity::faulty);
677
678 // make sure the device module has seen the exception
679 CHECK_EQUAL_TIMEOUT((device2Status.readLatest(), device2Status), 1, 100000);
680
681 // -------------------------------------------------------------//
682
683 threadedFanoutInput = 30000;
684 threadedFanoutInput.write();
685 // Further reads to the poll input are skipped
686 result.read();
687 BOOST_CHECK_EQUAL(result, 31001);
688 BOOST_CHECK(result.dataValidity() == ctk::DataValidity::faulty);
689
690 // -------------------------------------------------------------//
691
692 // recovery from device module exception
693 device2DummyBackend->throwExceptionRead = false;
694 CHECK_EQUAL_TIMEOUT((device2Status.readLatest(), device2Status), 0, 100000);
695
696 threadedFanoutInput = 40000;
697 threadedFanoutInput.write();
698 result.read();
699 // Now we expect also the last value written to the pollRegister being
700 // propagated and the DataValidity should be ok again.
701 BOOST_CHECK_EQUAL(result, 41000);
702 BOOST_CHECK(result.dataValidity() == ctk::DataValidity::ok);
703 }
704
705 /********************************************************************************************************************/
706
707 BOOST_FIXTURE_TEST_CASE(testReadDeviceWithTrigger, FixtureNoTestableMode) {
708 std::cout << "testReadDeviceWithTrigger" << std::endl;
709 waitForDevices();
710
711 auto trigger = test.getVoid("trigger");
712 auto fromDevice = test.getScalar<int>("/m1/i3"); // cs side display: m1.i3
713 /*----------------------------------------------------------------------------------------------------------------*/
714 fromDevice.read(); // there is an initial value
715 BOOST_CHECK_EQUAL(fromDevice, 0);
716
717 auto deviceRegister =
718 ctk::Device(TestApplication3::ExceptionDummyCDD1).getScalarRegisterAccessor<int>("/m1/i3/DUMMY_WRITEABLE");
719 deviceRegister = 30;
720 deviceRegister.write();
721
722 // trigger works as expected
723 trigger.write();
724
725 fromDevice.read();
726 BOOST_CHECK_EQUAL(fromDevice, 30);
727 BOOST_CHECK(fromDevice.dataValidity() == ctk::DataValidity::ok);
728
729 /*----------------------------------------------------------------------------------------------------------------*/
730 // Device module exception
731 deviceRegister = 10;
732 deviceRegister.write();
733
734 device1DummyBackend->throwExceptionRead = true;
735
736 trigger.write();
737
738 fromDevice.read();
739 BOOST_CHECK_EQUAL(fromDevice, 30);
740 BOOST_CHECK(fromDevice.dataValidity() == ctk::DataValidity::faulty);
741 /*----------------------------------------------------------------------------------------------------------------*/
742 // Recovery
743 device1DummyBackend->throwExceptionRead = false;
744
745 // Wait until the device has recovered. Otherwise the read might be skipped and we still read the previous value
746 // with the faulty flag.
747 while((void)device1Status.read(), device1Status == 1) {
748 usleep(1000);
749 }
750
751 trigger.write();
752
753 fromDevice.read();
754 BOOST_CHECK_EQUAL(fromDevice, 10);
755 BOOST_CHECK(fromDevice.dataValidity() == ctk::DataValidity::ok);
756 }
757
758 /********************************************************************************************************************/
759
761 std::cout << "testConsumingFanout" << std::endl;
762 waitForDevices();
763
764 auto threadedFanoutInput = test.getScalar<int>("m1/o1");
765 auto fromConsumingFanout = test.getScalar<int>("m1/i1"); // consumingfanout variable on cs side
766 auto result = test.getScalar<int>("m1/Module1_result");
767 fromConsumingFanout.read(); // initial value, don't care for this test
768 result.read(); // initial value, don't care for this test
769
770 auto pollRegisterSource =
771 ctk::Device(TestApplication3::ExceptionDummyCDD2).getScalarRegisterAccessor<int>("/m1/i2.DUMMY_WRITEABLE");
772 pollRegisterSource = 100;
773 pollRegisterSource.write();
774
775 threadedFanoutInput = 10;
776
777 auto consumingFanoutSource =
778 ctk::Device(TestApplication3::ExceptionDummyCDD1).getScalarRegisterAccessor<int>("/m1/i1.DUMMY_WRITEABLE");
779 consumingFanoutSource = 1;
780 consumingFanoutSource.write();
781
782 /*----------------------------------------------------------------------------------------------------------------*/
783 // no device module exception
784 threadedFanoutInput.write();
785
786 result.read();
787 BOOST_CHECK_EQUAL(result, 111);
788 BOOST_CHECK(result.dataValidity() == ctk::DataValidity::ok);
789
790 fromConsumingFanout.read();
791 BOOST_CHECK_EQUAL(fromConsumingFanout, 1);
792 BOOST_CHECK(fromConsumingFanout.dataValidity() == ctk::DataValidity::ok);
793
794 // --------------------------------------------------------//
795 // device exception on consuming fanout source read
796 consumingFanoutSource = 0;
797 consumingFanoutSource.write();
798
799 device1DummyBackend->throwExceptionRead = true;
800 threadedFanoutInput = 20;
801 threadedFanoutInput.write();
802
803 CHECK_TIMEOUT(result.readLatest(), 10000);
804 BOOST_CHECK_EQUAL(result, 121);
805 BOOST_CHECK(result.dataValidity() == ctk::DataValidity::faulty);
806
807 CHECK_TIMEOUT(fromConsumingFanout.readLatest(), 10000);
808 BOOST_CHECK_EQUAL(fromConsumingFanout, 1);
809 BOOST_CHECK(fromConsumingFanout.dataValidity() == ctk::DataValidity::faulty);
810
811 // --------------------------------------------------------//
812 // Recovery
813 device1DummyBackend->throwExceptionRead = false;
814
815 // Wait until the device has recovered. Otherwise the read might be skipped and we still read the previous value
816 // with the faulty flag.
817 while((void)device1Status.read(), device1Status == 1) {
818 usleep(1000);
819 }
820
821 threadedFanoutInput = 30;
822 threadedFanoutInput.write();
823
824 CHECK_TIMEOUT(result.readLatest(), 10000);
825 BOOST_CHECK_EQUAL(result, 130);
826 BOOST_CHECK(result.dataValidity() == ctk::DataValidity::ok);
827
828 CHECK_TIMEOUT(fromConsumingFanout.readLatest(), 10000);
829 BOOST_CHECK_EQUAL(fromConsumingFanout, 0);
830 BOOST_CHECK(fromConsumingFanout.dataValidity() == ctk::DataValidity::ok);
831 }
832
833 /********************************************************************************************************************/
834
835 BOOST_FIXTURE_TEST_CASE(testDataFlowOnDeviceException, FixtureNoTestableMode) {
836 std::cout << "testDataFlowOnDeviceException" << std::endl;
837
838 auto threadedFanoutInput = test.getScalar<int>("m1/o1");
839 auto m1_result = test.getScalar<int>("m1/Module1_result");
840 auto m2_result = test.getScalar<int>("m2/Module2_result");
841
842 auto consumingFanoutSource = ctk::ScalarRegisterAccessor<int>(
843 device1DummyBackend->getRegisterAccessor<int>("/m1/i1.DUMMY_WRITEABLE", 0, 0, {}));
844 consumingFanoutSource = 1000;
845 consumingFanoutSource.write();
846
847 auto pollRegister = ctk::ScalarRegisterAccessor<int>(
848 device2DummyBackend->getRegisterAccessor<int>("/m1/i2.DUMMY_WRITEABLE", 0, 0, {}));
849 pollRegister = 100;
850 pollRegister.write();
851
852 waitForDevices();
853
854 // get rid of initial values
855 m1_result.read();
856 m2_result.read();
857
858 threadedFanoutInput = 1;
859
860 // ------------------------------------------------------------------//
861 // without exception
862 threadedFanoutInput.write();
863
864 CHECK_TIMEOUT(m1_result.readNonBlocking(), 10000);
865 BOOST_CHECK_EQUAL(m1_result, 1101);
866 BOOST_CHECK(m1_result.dataValidity() == ctk::DataValidity::ok);
867
868 CHECK_TIMEOUT(m2_result.readNonBlocking(), 10000);
869 BOOST_CHECK_EQUAL(m2_result, 1101);
870 BOOST_CHECK(m2_result.dataValidity() == ctk::DataValidity::ok);
871
872 // ------------------------------------------------------------------//
873 // faulty threadedFanout input
874 threadedFanoutInput.setDataValidity(ctk::DataValidity::faulty);
875 threadedFanoutInput.write();
876
877 CHECK_TIMEOUT(m1_result.readNonBlocking(), 10000);
878 BOOST_CHECK_EQUAL(m1_result, 1101);
879 BOOST_CHECK(m1_result.dataValidity() == ctk::DataValidity::faulty);
880
881 CHECK_TIMEOUT(m2_result.readLatest(), 10000);
882 BOOST_CHECK_EQUAL(m2_result, 1101);
883 BOOST_CHECK(m2_result.dataValidity() == ctk::DataValidity::faulty);
884
885 auto deviceStatus = test.getScalar<int32_t>(ctk::RegisterPath("/Devices") /
887 // the device is still OK
888 CHECK_EQUAL_TIMEOUT((deviceStatus.readLatest(), deviceStatus), 0, 10000);
889
890 // ---------------------------------------------------------------------//
891 // device module exception
892 device2DummyBackend->throwExceptionRead = true;
893 pollRegister = 200;
894 pollRegister.write();
895 threadedFanoutInput = 0;
896 threadedFanoutInput.write();
897
898 // Now the device has to go into the error state
899 CHECK_EQUAL_TIMEOUT((deviceStatus.readLatest(), deviceStatus), 1, 10000);
900
901 // The new value of the threadedFanoutInput should be propagated, the
902 // pollRegister is skipped, see testDataValidPropagationOnException.
903 m1_result.read();
904 BOOST_CHECK_EQUAL(m1_result, 1100);
905 BOOST_CHECK(m1_result.dataValidity() == ctk::DataValidity::faulty);
906 m2_result.read();
907 // Same for m2
908 BOOST_CHECK_EQUAL(m2_result, 1100);
909 BOOST_CHECK(m2_result.dataValidity() == ctk::DataValidity::faulty);
910
911 // ---------------------------------------------------------------------//
912 // device exception recovery
913 device2DummyBackend->throwExceptionRead = false;
914
915 // device error recovers. There must be exactly one new status values with the right value.
916 deviceStatus.read();
917 BOOST_CHECK(deviceStatus == 0);
918 // nothing else in the queue
919 BOOST_CHECK(deviceStatus.readNonBlocking() == false);
920
921 // ---------------------------------------------------------------------//
922 // Now both, threadedFanoutInput and pollRegister should propagate
923 pollRegister = 300;
924 pollRegister.write();
925 threadedFanoutInput = 2;
926 threadedFanoutInput.write();
927
928 m1_result.read();
929 BOOST_CHECK_EQUAL(m1_result, 1302);
930 // Data validity still faulty because the input from the fan is invalid
931 BOOST_CHECK(m1_result.dataValidity() == ctk::DataValidity::faulty);
932 // again, nothing else in the queue
933 BOOST_CHECK(m1_result.readNonBlocking() == false);
934
935 // same for m2
936 m2_result.read();
937 BOOST_CHECK_EQUAL(m2_result, 1302);
938 BOOST_CHECK(m2_result.dataValidity() == ctk::DataValidity::faulty);
939 BOOST_CHECK(m2_result.readNonBlocking() == false);
940
941 // ---------------------------------------------------------------------//
942 // recovery: fanout input
943 threadedFanoutInput = 3;
944 threadedFanoutInput.setDataValidity(ctk::DataValidity::ok);
945 threadedFanoutInput.write();
946
947 m1_result.read();
948 BOOST_CHECK_EQUAL(m1_result, 1303);
949 BOOST_CHECK(m1_result.dataValidity() == ctk::DataValidity::ok);
950 BOOST_CHECK(m1_result.readNonBlocking() == false);
951
952 m2_result.read();
953 BOOST_CHECK_EQUAL(m2_result, 1303);
954 BOOST_CHECK(m2_result.dataValidity() == ctk::DataValidity::ok);
955 BOOST_CHECK(m1_result.readNonBlocking() == false);
956 }
957
958 BOOST_AUTO_TEST_SUITE_END()
959
960 /********************************************************************************************************************/
961 /********************************************************************************************************************/
962
963 // Module and Application for test case "testDataValidPropagationOnException"
964 struct Module3 : ctk::ApplicationModule {
965 using ctk::ApplicationModule::ApplicationModule;
966 ctk::ScalarPushInput<int> pushTypeInputFromCS{this, "o1", "", "", {"CS"}};
967
968 ctk::ScalarPollInput<int> pollInputFromDevice{this, "/m1/i2", "", "", {"DEVICE2"}};
969 ctk::ScalarOutput<int> result{this, "Module3_result", "", "", {"CS"}};
970
971 void mainLoop() override {
972 while(true) {
973 result = pushTypeInputFromCS + pollInputFromDevice;
974 result.write();
975 readAll(); // read last, so initial values are written in the first round
976 }
977 }
978 };
979
981 /*
982 *
983 */
984
985 constexpr static char const* ExceptionDummyCDD2 = "(ExceptionDummy:1?map=testDataValidity2.map)";
986 TestApplication4() : Application("testDataFlagPropagation") {}
987 ~TestApplication4() override { shutdown(); }
988
989 Module3 module{this, "module", ""};
990
992
993 /* void defineConnections() override {
994 findTag("CS").connectTo(cs);
995 findTag("DEVICE2").flatten().connectTo(device2["m1"]);
996 }*/
997 };
998
999 /********************************************************************************************************************/
1000
1001 BOOST_AUTO_TEST_CASE(testDataValidPropagationOnException) {
1002 std::cout << "testDataValidPropagationOnException" << std::endl;
1003
1004 boost::shared_ptr<ctk::ExceptionDummy> device2DummyBackend(boost::dynamic_pointer_cast<ctk::ExceptionDummy>(
1005 ChimeraTK::BackendFactory::getInstance().createBackend(TestApplication3::ExceptionDummyCDD2)));
1007
1008 TestApplication4 app;
1009 ctk::TestFacility test{app, false};
1010
1011 auto pollRegister = device2.getScalarRegisterAccessor<int>("/m1/i2.DUMMY_WRITEABLE");
1012 device2.open();
1013 pollRegister = 1;
1014 pollRegister.write();
1015 device2.close();
1016
1017 test.runApplication();
1018
1019 auto pushInput = test.getScalar<int>("module/o1");
1020 auto result = test.getScalar<int>("module/Module3_result");
1021
1022 auto deviceStatus = test.getScalar<int32_t>(ctk::RegisterPath("/Devices") /
1023 ctk::Utilities::escapeName(TestApplication3::ExceptionDummyCDD2, false) / "status");
1024
1025 pushInput = 10;
1026 pushInput.write();
1027
1028 CHECK_TIMEOUT((result.readLatest(), result == 11), 10000);
1029 BOOST_CHECK(result.dataValidity() == ctk::DataValidity::ok);
1030 CHECK_EQUAL_TIMEOUT((deviceStatus.readLatest(), deviceStatus), 0, 10000);
1031
1032 // Set data validity to faulty and trigger excetion in the same update
1033 pollRegister = 2;
1034 pollRegister.write();
1035 pushInput = 20;
1036 pushInput.setDataValidity(ctk::DataValidity::faulty);
1037 device2DummyBackend->throwExceptionRead = true;
1038 pushInput.write();
1039
1040 CHECK_EQUAL_TIMEOUT((deviceStatus.readLatest(), deviceStatus), 1, 10000);
1041 result.read();
1042 BOOST_CHECK(result.readLatest() == false);
1043 // The new data from the pushInput and the DataValidity::faulty should have been propagated to the outout,
1044 // the pollRegister should be skipped (Exceptionhandling spec B.2.2.3), so we don't expect the latest assigned value of 2
1045 BOOST_CHECK_EQUAL(result, 21);
1046 BOOST_CHECK(result.dataValidity() == ctk::DataValidity::faulty);
1047
1048 // Writeing the pushInput should still trigger module execution and
1049 // update the result value. Result validity should still be faulty because
1050 // the device still has the exception
1051 pushInput = 30;
1052 pushInput.setDataValidity(ctk::DataValidity::ok);
1053 pushInput.write();
1054 result.read();
1055 BOOST_CHECK_EQUAL(result, 31);
1056 BOOST_CHECK(result.dataValidity() == ctk::DataValidity::faulty);
1057
1058 // let the device recover
1059 device2DummyBackend->throwExceptionRead = false;
1060 CHECK_EQUAL_TIMEOUT((deviceStatus.readLatest(), deviceStatus), 0, 10000);
1061
1062 // Everything should be back to normal, also the value of the pollRegister
1063 // should be reflected in the output
1064 pushInput = 40;
1065 pollRegister = 3;
1066 pollRegister.write();
1067 pushInput.write();
1068 result.read();
1069 BOOST_CHECK_EQUAL(result, 43);
1070 BOOST_CHECK(result.dataValidity() == ctk::DataValidity::ok);
1071 // nothing more in the queue
1072 BOOST_CHECK(result.readLatest() == false);
1073
1074 // Check if we get faulty output from the exception alone,
1075 // keep pushInput ok
1076 pollRegister = 4;
1077 pollRegister.write();
1078 pushInput = 50;
1079 device2DummyBackend->throwExceptionRead = true;
1080
1081 pushInput.write();
1082 result.read();
1083 BOOST_CHECK(result.readLatest() == false);
1084 // The new data from the pushInput, the device exception should yield DataValidity::faulty at the outout,
1085 BOOST_CHECK_EQUAL(result, 53);
1086 BOOST_CHECK(result.dataValidity() == ctk::DataValidity::faulty);
1087
1088 // Device status should report fault. We need to wait for it here to make sure the DeviceModule has seen the fault.
1089 CHECK_EQUAL_TIMEOUT((deviceStatus.readLatest(), deviceStatus), 1, 10000);
1090
1091 // Also set pushInputValidity to faulty
1092 pushInput = 60;
1093 pushInput.setDataValidity(ctk::DataValidity::faulty);
1094 pushInput.write();
1095 result.read();
1096 BOOST_CHECK_EQUAL(result, 63);
1097 BOOST_CHECK(result.dataValidity() == ctk::DataValidity::faulty);
1098
1099 // let the device recover
1100 device2DummyBackend->throwExceptionRead = false;
1101 CHECK_EQUAL_TIMEOUT((deviceStatus.readLatest(), deviceStatus), 0, 10000);
1102
1103 // The new pollRegister value should now be reflected in the result,
1104 // but it's still faulty from the pushInput
1105 pushInput = 70;
1106 pollRegister = 5;
1107 pollRegister.write();
1108 pushInput.write();
1109 result.read();
1110 BOOST_CHECK_EQUAL(result, 75);
1111 BOOST_CHECK(result.dataValidity() == ctk::DataValidity::faulty);
1112
1113 // MAke pushInput ok, everything should be back to normal
1114 pushInput = 80;
1115 pushInput.setDataValidity(ctk::DataValidity::ok);
1116 pollRegister = 6;
1117 pollRegister.write();
1118 pushInput.write();
1119 result.read();
1120 BOOST_CHECK_EQUAL(result, 86);
1121 BOOST_CHECK(result.dataValidity() == ctk::DataValidity::ok);
1122 // nothing more in the queue
1123 BOOST_CHECK(result.readLatest() == false);
1124 }
1125
1126 /********************************************************************************************************************/
1127
1129 using ctk::ApplicationModule::ApplicationModule;
1130 ctk::ScalarOutput<int> o1{this, "o1", "", ""};
1131 ctk::ArrayOutput<int> o2{this, "o2", "", 2, ""};
1132 void mainLoop() override {}
1133 };
1134
1135 /********************************************************************************************************************/
1136
1138 TestApplication5() : Application("testSuite") {}
1139 ~TestApplication5() override { shutdown(); }
1140
1141 TestModule3 a{this, "A", ""};
1142 };
1143
1144 /********************************************************************************************************************/
1145
1146 BOOST_AUTO_TEST_CASE(testWriteIfDifferent) {
1147 std::cout << "testWriteIfDifferent" << std::endl;
1148
1149 TestApplication5 app;
1150 ctk::TestFacility test{app, false};
1151
1152 auto o1 = test.getScalar<int>("/A/o1");
1153 auto o2 = test.getArray<int>("/A/o2");
1154
1155 test.runApplication();
1156
1157 // initialise in defined conditions
1158 app.a.o1 = 42;
1159 app.a.o1.write();
1160 BOOST_TEST(o1.readLatest());
1161 BOOST_TEST(o1.dataValidity() == ctk::DataValidity::ok);
1162 BOOST_TEST(int(o1) == 42);
1163
1164 app.a.o2 = {48, 59};
1165 app.a.o2.write();
1166 BOOST_TEST(o2.readLatest());
1167 BOOST_TEST(o2.dataValidity() == ctk::DataValidity::ok);
1168 BOOST_TEST(std::vector<int>(o2) == std::vector<int>({48, 59}));
1169
1170 // set module to faulty and write same value with writeIfDifferent again: faulty flag should be propagated
1172 app.a.o1.writeIfDifferent(42);
1173 BOOST_TEST(o1.readNonBlocking());
1174 BOOST_TEST(o1.dataValidity() == ctk::DataValidity::faulty);
1175 BOOST_TEST(int(o1) == 42);
1176
1177 // repeat with array
1178 app.a.o2.writeIfDifferent({48, 59});
1179 BOOST_TEST(o2.readNonBlocking());
1180 BOOST_TEST(o2.dataValidity() == ctk::DataValidity::faulty);
1181 BOOST_TEST(std::vector<int>(o2) == std::vector<int>({48, 59}));
1182
1183 // repeat with ok validity
1185 app.a.o1.writeIfDifferent(42);
1186 BOOST_TEST(o1.readNonBlocking());
1187 BOOST_TEST(o1.dataValidity() == ctk::DataValidity::ok);
1188 BOOST_TEST(int(o1) == 42);
1189
1190 app.a.o2.writeIfDifferent({48, 59});
1191 BOOST_TEST(o2.readNonBlocking());
1192 BOOST_TEST(o2.dataValidity() == ctk::DataValidity::ok);
1193 BOOST_TEST(std::vector<int>(o2) == std::vector<int>({48, 59}));
1194 }
1195
1196 /********************************************************************************************************************/
1197
1198} // namespace Tests::testPropagateDataFaultFlag
#define CHECK_EQUAL_TIMEOUT(left, right, maxMilliseconds)
void shutdown() override
This will remove the global pointer to the instance and allows creating another instance afterwards.
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.
void writeIfDifferent(const std::vector< UserType > &newValue, VersionNumber versionNumber, DataValidity validity)=delete
bool write(ChimeraTK::VersionNumber versionNumber)=delete
Convenience class for output array accessors (always UpdateMode::push)
Convenience class for input array accessors with UpdateMode::push.
friend class Application
Definition ModuleGroup.h:47
void readAll(bool includeReturnChannels=false)
Read all readable variables in the group.
Definition Module.cc:72
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 writeIfDifferent(UserType newValue, VersionNumber versionNumber, DataValidity validity)=delete
Convenience class for output scalar accessors (always UpdateMode::push)
Convenience class for input scalar accessors with UpdateMode::poll.
Convenience class for input scalar accessors with UpdateMode::push.
Convenience class for input scalar accessors with return channel ("write back") and UpdateMode::push.
Special ScalarOutput which represents a status which can be aggregated by the StatusAggregator.
Helper class to facilitate tests of applications based on ApplicationCore.
ChimeraTK::OneDRegisterAccessor< T > getArray(const ChimeraTK::RegisterPath &name) const
Obtain an array-type process variable from the application, which is published to the control system.
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.
std::string escapeName(const std::string &name, bool allowDotsAndSlashes)
Convert all characters which are not allowed in variable or module names into underscores followed by...
Definition Utilities.cc:45
InvalidityTracer application module.
BOOST_FIXTURE_TEST_CASE(testDeviceReadFailure, FixtureNoTestableMode)
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...
#define CHECK_TIMEOUT(condition, maxMilliseconds)