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