ChimeraTK-ApplicationCore 04.06.00
Loading...
Searching...
No Matches
testStatusAggregator.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
4#include "Application.h"
5#include "ModuleGroup.h"
6#include "StatusAggregator.h"
7#include "StatusWithMessage.h"
8#include "TestFacility.h"
9
10#define BOOST_NO_EXCEPTIONS
11#define BOOST_TEST_MODULE testStatusAggregator
12#include <boost/test/included/unit_test.hpp>
13#undef BOOST_NO_EXCEPTIONS
14
16
17 using namespace boost::unit_test_framework;
18
19 namespace ctk = ChimeraTK;
20
21 /********************************************************************************************************************/
22
24 using ctk::ApplicationModule::ApplicationModule;
25
26 StatusGenerator(ctk::ModuleGroup* owner, const std::string& name, const std::string& description,
27 const std::unordered_set<std::string>& tags = {},
28 ctk::StatusOutput::Status initialStatus = ctk::StatusOutput::Status::OFF)
29 : ApplicationModule(owner, name, description, tags), initialValue(initialStatus) {}
30
32
33 ctk::StatusOutput::Status initialValue;
34
35 void prepare() override {
37 status.write();
38 }
39 void mainLoop() override {}
40 };
41
42 /********************************************************************************************************************/
43
45 using ctk::ApplicationModule::ApplicationModule;
46
47 StatusWithMessageGenerator(ctk::ModuleGroup* owner, const std::string& name, const std::string& description,
48 const std::unordered_set<std::string>& tags = {},
49 ctk::StatusOutput::Status initialStatus = ctk::StatusOutput::Status::OFF)
50 : ApplicationModule(owner, name, description, tags), initialValue(initialStatus) {}
51
53
54 ctk::StatusOutput::Status initialValue;
55
56 void prepare() override {
57 if(initialValue == ctk::StatusOutput::Status::OK) {
59 }
60 else {
62 }
63 }
64 void mainLoop() override {}
65 };
66
67 /********************************************************************************************************************/
68
70 TestApplication() : Application("testApp") {}
71 ~TestApplication() override { shutdown(); }
72
73 StatusGenerator s{this, "s", "Status"};
74
76 using ctk::ModuleGroup::ModuleGroup;
77
78 StatusGenerator s1{this, "s1", "Status 1"};
79 StatusGenerator s2{this, "s2", "Status 2"};
80
82 using ctk::ModuleGroup::ModuleGroup;
83 StatusGenerator s{this, "s", "Status"};
84 StatusGenerator deep{this, "deep", "Status"};
85 };
86 InnerGroup innerGroup1{this, "InnerGroup1", ""};
87 InnerGroup innerGroup2{this, "InnerGroup2", ""};
88
89 } outerGroup{this, "OuterGroup", ""};
90
92 this, "Aggregated/status", "aggregated status description", ctk::StatusAggregator::PriorityMode::fwko};
93 };
94
95 /********************************************************************************************************************/
96
97 BOOST_AUTO_TEST_CASE(testSingleNoTags) {
98 std::cout << "testSingleNoTags" << std::endl;
99
100 TestApplication app;
101 ctk::TestFacility test(app);
102
103 auto status = test.getScalar<int>("/Aggregated/status");
104
105 test.runApplication();
106
107 // check that statuses on different levels are correctly aggregated
108 auto check = [&](auto& var) {
109 var = ctk::StatusOutput::Status::OK;
110 var.write();
111 test.stepApplication();
112 BOOST_CHECK(status.readNonBlocking() == true);
113 BOOST_CHECK_EQUAL(int(status), int(ctk::StatusOutput::Status::OK));
114 var = ctk::StatusOutput::Status::OFF;
115 var.write();
116 test.stepApplication();
117 BOOST_CHECK(status.readNonBlocking() == true);
118 BOOST_CHECK_EQUAL(int(status), int(ctk::StatusOutput::Status::OFF));
119 };
120 check(app.s.status);
121 check(app.outerGroup.s1.status);
122 check(app.outerGroup.s2.status);
123 check(app.outerGroup.innerGroup1.s.status);
124 check(app.outerGroup.innerGroup1.deep.status);
125 check(app.outerGroup.innerGroup2.s.status);
126 check(app.outerGroup.innerGroup2.deep.status);
127 }
128
129 /********************************************************************************************************************/
130
131 BOOST_AUTO_TEST_CASE(testDataValidity) {
132 std::cout << "testDataValidity" << std::endl;
133
134 TestApplication app;
135 ctk::TestFacility test(app);
136
137 auto status = test.getScalar<int>("/Aggregated/status");
138
139 test.runApplication();
140
142 app.s.status = ctk::StatusOutput::Status::OK;
143 app.s.status.write();
144 test.stepApplication();
145 BOOST_CHECK(status.readNonBlocking() == true);
146 BOOST_CHECK(status.dataValidity() == ctk::DataValidity::ok);
147 BOOST_CHECK_EQUAL(int(status), int(ctk::StatusOutput::Status::OK));
148 app.s.status = ctk::StatusOutput::Status::OFF;
149 app.s.status.write();
150 test.stepApplication();
151 BOOST_CHECK(status.readNonBlocking() == true);
152 BOOST_CHECK(status.dataValidity() == ctk::DataValidity::ok);
153 BOOST_CHECK_EQUAL(int(status), int(ctk::StatusOutput::Status::OFF));
154 }
155
156 /********************************************************************************************************************/
157
159 explicit TestPrioApplication(ctk::StatusOutput::Status theInitialValue)
160 : Application("testApp"), initialValue(theInitialValue) {}
162
163 ctk::StatusOutput::Status initialValue;
164
165 StatusGenerator s1{this, "sg1/internal", "Status 1", {}, initialValue};
166 StatusGenerator s2{this, "sg2/external", "Status 2", {}, initialValue};
167
169 };
170
171 /********************************************************************************************************************/
172
173 BOOST_AUTO_TEST_CASE(testPriorities) {
174 std::cout << "testPriorities" << std::endl;
175
176 // Define repeated check for a given priority mode
177 auto check = [&](ctk::StatusAggregator::PriorityMode mode, auto prio0, auto prio1, auto prio2, auto prio3,
178 bool warnMixed01 = false) {
179 // create app with initial values set to lowest prio value
180 TestPrioApplication app(prio0);
181
182 app.aggregator = ctk::StatusAggregator{&app, "Aggregated/status", "aggregated status description", mode};
183
184 ctk::TestFacility test(app);
185
186 auto status = test.getScalar<int>("/Aggregated/status");
187
188 test.runApplication();
189
190 // check initial value
191 status.readNonBlocking(); // do not check return value, as it will only be written when changed
192 BOOST_CHECK_EQUAL(int(status), int(prio0));
193
194 // Define repeated check to test all combinations of two given values with different priority.
195 // This kind of a whitebox test. We know that
196 // - the first aggregates variable has a different code path
197 // - the code path depends on the VersionNumber, so the write order matters and we have to set the version number
198 auto subcheck = [&](auto lower, auto higher, bool writeS2First, bool warnMixed = false) {
199 StatusGenerator* first;
200 StatusGenerator* second;
201 if(writeS2First) {
202 first = &app.s2;
203 second = &app.s1;
204 }
205 else {
206 first = &app.s1;
207 second = &app.s2;
208 }
209 std::cout << int(lower) << " vs. " << int(higher) << std::endl;
210 first->status = lower;
211 first->setCurrentVersionNumber({});
212 first->status.write();
213 second->status = lower;
214 second->setCurrentVersionNumber({});
215 second->status.write();
216 test.stepApplication();
217 status.readLatest();
218 BOOST_CHECK_EQUAL(int(status), int(lower));
219 first->status = lower;
220 first->setCurrentVersionNumber({});
221 first->status.write();
222 second->status = higher;
223 second->setCurrentVersionNumber({});
224 second->status.write();
225 test.stepApplication();
226 status.readLatest();
227 if(!warnMixed) {
228 BOOST_CHECK_EQUAL(int(status), int(higher));
229 }
230 else {
231 BOOST_CHECK_EQUAL(int(status), int(ctk::StatusOutput::Status::WARNING));
232 }
233 first->status = higher;
234 first->setCurrentVersionNumber({});
235 first->status.write();
236 second->status = lower;
237 second->setCurrentVersionNumber({});
238 second->status.write();
239 test.stepApplication();
240 status.readLatest();
241 if(!warnMixed) {
242 BOOST_CHECK_EQUAL(int(status), int(higher));
243 }
244 else {
245 BOOST_CHECK_EQUAL(int(status), int(ctk::StatusOutput::Status::WARNING));
246 }
247 first->status = higher;
248 first->setCurrentVersionNumber({});
249 first->status.write();
250 second->status = higher;
251 second->setCurrentVersionNumber({});
252 second->status.write();
253 test.stepApplication();
254 status.readLatest();
255 BOOST_CHECK_EQUAL(int(status), int(higher));
256 };
257
258 // all prios against each other
259 auto runAllSubchecks = [&](bool writeS2First) {
260 subcheck(prio0, prio1, writeS2First, warnMixed01);
261 subcheck(prio0, prio2, writeS2First);
262 subcheck(prio0, prio3, writeS2First);
263 subcheck(prio1, prio2, writeS2First);
264 subcheck(prio1, prio3, writeS2First);
265 subcheck(prio2, prio3, writeS2First);
266 };
267 runAllSubchecks(false);
268 runAllSubchecks(true);
269 };
270
271 // check all priority modes
272 std::cout << "PriorityMode::fwko" << std::endl;
273 check(ctk::StatusAggregator::PriorityMode::fwko, ctk::StatusOutput::Status::OFF, ctk::StatusOutput::Status::OK,
274 ctk::StatusOutput::Status::WARNING, ctk::StatusOutput::Status::FAULT);
275 std::cout << "PriorityMode::fwok" << std::endl;
276 check(ctk::StatusAggregator::PriorityMode::fwok, ctk::StatusOutput::Status::OK, ctk::StatusOutput::Status::OFF,
277 ctk::StatusOutput::Status::WARNING, ctk::StatusOutput::Status::FAULT);
278 std::cout << "PriorityMode::ofwk" << std::endl;
279 check(ctk::StatusAggregator::PriorityMode::ofwk, ctk::StatusOutput::Status::OK, ctk::StatusOutput::Status::WARNING,
280 ctk::StatusOutput::Status::FAULT, ctk::StatusOutput::Status::OFF);
281 std::cout << "PriorityMode::fw_warn_mixed" << std::endl;
282 check(ctk::StatusAggregator::PriorityMode::fw_warn_mixed, ctk::StatusOutput::Status::OFF,
283 ctk::StatusOutput::Status::OK, ctk::StatusOutput::Status::WARNING, ctk::StatusOutput::Status::FAULT, true);
284 }
285
286 /********************************************************************************************************************/
287
288 BOOST_AUTO_TEST_CASE(testCustomMixedWarnMessage) {
289 std::cout << "testCustomMixedWarnMessage" << std::endl;
290
291 const std::string customMessage1{"My custom warn mixed message"};
292
293 TestPrioApplication app(ctk::StatusOutput::Status::OK);
294 app.aggregator = ctk::StatusAggregator{&app, "Aggregated/status", "aggregated status description",
295 ctk::StatusAggregator::PriorityMode::fw_warn_mixed, {}, {}, customMessage1};
296
297 ctk::TestFacility test(app);
298 test.runApplication();
299
300 auto statusMessage = test.getScalar<std::string>("/Aggregated/status_message");
301
302 // check test pre-condition: No message when OK.
303 BOOST_TEST(std::string(statusMessage) == "");
304
306 app.s1.status.setAndWrite(int(ctk::StatusOutput::Status::OFF));
307 test.stepApplication();
308 BOOST_TEST(statusMessage.readAndGet() == customMessage1);
309
310 // Change custom message at run time
311 const std::string customMessage2{"Another warn mixed message"};
312 app.aggregator.setWarnMixedMessage(customMessage2);
313
315 app.s1.status.setAndWrite(int(ctk::StatusOutput::Status::OFF));
316 test.stepApplication();
317 BOOST_TEST(statusMessage.readAndGet() == customMessage2);
318 }
319
320 /********************************************************************************************************************/
321
325
326 StatusGenerator s{this, "s", "Status"};
327
329 using ctk::ModuleGroup::ModuleGroup;
330
331 // Set one of the inputs for the extraAggregator to fault, which has no effect, since one other is OFF which is
332 // prioritised. If the top-level aggregator would wrongly aggregate this input directly, it would go to FAULT.
333 StatusGenerator s1{this, "s1", "Status 1", {}, ctk::StatusOutput::Status::FAULT};
334
335 StatusGenerator s2{this, "s2", "Status 2"};
336
338 this, "/Aggregated/extraStatus", "aggregated status description", ctk::StatusAggregator::PriorityMode::ofwk};
339
340 } outerGroup{this, "OuterGroup", ""};
341
343 this, "Aggregated/status", "aggregated status description", ctk::StatusAggregator::PriorityMode::fwko};
344 };
345
347
348 BOOST_AUTO_TEST_CASE(testTwoLevels) {
349 std::cout << "testTwoLevels" << std::endl << std::endl << std::endl;
351
352 ctk::TestFacility test(app);
353
354 auto status = test.getScalar<int>("/Aggregated/status");
355 auto extraStatus = test.getScalar<int>("/Aggregated/extraStatus");
356
357 test.runApplication();
358
359 // check the initial values
360 extraStatus.readLatest();
361 BOOST_CHECK_EQUAL(int(extraStatus), int(ctk::StatusOutput::Status::OFF));
362 status.readLatest();
363 BOOST_CHECK_EQUAL(int(status), int(ctk::StatusOutput::Status::OFF));
364
365 // change status which goes directly into the upper aggregator
366 app.s.status = ctk::StatusOutput::Status::OK;
367 app.s.status.write();
368 test.stepApplication();
369 status.readLatest();
370 BOOST_CHECK_EQUAL(int(status), int(ctk::StatusOutput::Status::OK));
371 app.s.status = ctk::StatusOutput::Status::OFF;
372 app.s.status.write();
373 test.stepApplication();
374 status.readLatest();
375 BOOST_CHECK_EQUAL(int(status), int(ctk::StatusOutput::Status::OFF));
376
377 // change status which goes into the lower aggregator (then the FAULT of s1 will win)
378 app.outerGroup.s2.status = ctk::StatusOutput::Status::OK;
379 app.outerGroup.s2.status.write();
380 test.stepApplication();
381 status.readLatest();
382 BOOST_CHECK_EQUAL(int(status), int(ctk::StatusOutput::Status::FAULT));
383 app.outerGroup.s2.status = ctk::StatusOutput::Status::OFF;
384 app.outerGroup.s2.status.write();
385 test.stepApplication();
386 status.readLatest();
387 BOOST_CHECK_EQUAL(int(status), int(ctk::StatusOutput::Status::OFF));
388 }
389
390 /********************************************************************************************************************/
391
395
397 using ctk::ModuleGroup::ModuleGroup;
398
399 StatusGenerator sA{this, "sA", "Status 1", ctk::TAGS{"A"}, ctk::StatusOutput::Status::WARNING};
400 StatusGenerator sAB{this, "sAB", "Status 2", {"A", "B"}, ctk::StatusOutput::Status::OFF};
401
402 // First level of aggregation: Input and output tags are identical
404 this, "aggregateA", "aggregated status description", ctk::StatusAggregator::PriorityMode::fwko, {"A"}, {"A"}};
406 this, "aggregateB", "aggregated status description", ctk::StatusAggregator::PriorityMode::fwko, {"B"}, {"B"}};
407
408 } group{this, "Group", ""};
409
410 // Use other priority mode here to make sure only the aggregators are aggregated, not the generators
412 this, "aggregateA", "aggregated status description", ctk::StatusAggregator::PriorityMode::ofwk, {"A"}};
414 this, "aggregateB", "aggregated status description", ctk::StatusAggregator::PriorityMode::ofwk, {"B"}};
416 this, "aggregateAll", "aggregated status description", ctk::StatusAggregator::PriorityMode::fw_warn_mixed};
417 };
418
419 /********************************************************************************************************************/
420
422 std::cout << "testTags" << std::endl;
423
425 ctk::TestFacility test(app);
426
427 auto aggregateA = test.getScalar<int>("/aggregateA");
428 auto aggregateB = test.getScalar<int>("/aggregateB");
429 auto aggregateAll = test.getScalar<int>("/aggregateAll");
430 auto Group_aggregateA = test.getScalar<int>("/Group/aggregateA");
431 auto Group_aggregateB = test.getScalar<int>("/Group/aggregateB");
432
433 test.runApplication();
434
435 // check initial values
436 aggregateA.readLatest();
437 aggregateB.readLatest();
438 aggregateAll.readLatest();
439 Group_aggregateA.readLatest();
440 Group_aggregateB.readLatest();
441 BOOST_CHECK_EQUAL(int(aggregateA), int(ctk::StatusOutput::Status::WARNING));
442 BOOST_CHECK_EQUAL(int(aggregateB), int(ctk::StatusOutput::Status::OFF));
443 BOOST_CHECK_EQUAL(int(aggregateAll), int(ctk::StatusOutput::Status::WARNING));
444 BOOST_CHECK_EQUAL(int(Group_aggregateA), int(ctk::StatusOutput::Status::WARNING));
445 BOOST_CHECK_EQUAL(int(Group_aggregateB), int(ctk::StatusOutput::Status::OFF));
446
447 app.group.sAB.status = ctk::StatusOutput::Status::FAULT;
448 app.group.sAB.status.write();
449
450 test.stepApplication();
451
452 // change value tagged with 'A' and 'B', affecting all aggregators to highest priority value, so it is visible
453 // everywhere
454 aggregateA.readLatest();
455 aggregateB.readLatest();
456 aggregateAll.readLatest();
457 Group_aggregateA.readLatest();
458 Group_aggregateB.readLatest();
459 BOOST_CHECK_EQUAL(int(aggregateA), int(ctk::StatusOutput::Status::FAULT));
460 BOOST_CHECK_EQUAL(int(aggregateB), int(ctk::StatusOutput::Status::FAULT));
461 BOOST_CHECK_EQUAL(int(aggregateAll), int(ctk::StatusOutput::Status::FAULT));
462 BOOST_CHECK_EQUAL(int(Group_aggregateA), int(ctk::StatusOutput::Status::FAULT));
463 BOOST_CHECK_EQUAL(int(Group_aggregateB), int(ctk::StatusOutput::Status::FAULT));
464 }
465
466 /********************************************************************************************************************/
467
471
473 using ctk::ModuleGroup::ModuleGroup;
474
475 StatusGenerator sA{this, "sA", "Status A", ctk::TAGS{"A"}};
476 StatusGenerator sB1{this, "sB1", "Status B1", ctk::TAGS{"B"}};
477 StatusGenerator sB2{this, "sB2", "Status B2", ctk::TAGS{"B"}};
478 StatusGenerator sC{this, "sC", "Status C", ctk::TAGS{"C"}};
479
481 this, "aggregatedA", "", ctk::StatusAggregator::PriorityMode::fwko, {"A"}, {"AGG_A"}};
482 // Same input and output tag. When aggregating tag "B" the status outputs themselves shoudl not be taken again.
484 this, "aggregatedB", "", ctk::StatusAggregator::PriorityMode::fwko, {"B"}, {"B"}};
485
486 // Missing: Test of multiple aggregation tags. Currently only one tag is allowed due to the missing
487 // design decision whether multiple tags should be a logical AND or OR (#13256).
488 } group{this, "Group", ""};
489
490 // Does not aggregate an aggregator
491 ctk::StatusAggregator aggregateA{this, "aggA", "", ctk::StatusAggregator::PriorityMode::fwko, {"A"}};
492 // Aggregates the A aggregator
493 ctk::StatusAggregator aggAggA{this, "aggAggA", "", ctk::StatusAggregator::PriorityMode::fwko, {"AGG_A"}};
494 // Aggregates the B aggregator
495 ctk::StatusAggregator aggAggB{this, "aggAggB", "", ctk::StatusAggregator::PriorityMode::fwko, {"B"}};
496 };
497
498 /********************************************************************************************************************/
499
500 BOOST_AUTO_TEST_CASE(testAggregatorTags) {
501 std::cout << "testAggregatorTags" << std::endl;
502
504 ctk::TestFacility test(app);
505
506 auto checkForName = [](auto& accessors, std::string name) {
507 return std::any_of(accessors.begin(), accessors.end(), [&name](auto acc) { return acc.getName() == name; });
508 };
509
510 auto accessorsAggA = app.aggregateA.getAccessorListRecursive();
511 // One aggregated input ("sA") + plus 3 inputs/outputs from the aggregator itself
512 // "aggregatedA" is not used because the tag "A" is its input. The output tag is "AGG_A"
513 BOOST_TEST(accessorsAggA.size() == 4);
514 BOOST_CHECK(checkForName(accessorsAggA, "sA"));
515 BOOST_CHECK(!checkForName(accessorsAggA, "aggregatedA"));
516
517 auto accessorsAggAggA = app.aggAggA.getAccessorListRecursive();
518 // One aggregated input ("aggregatedA") + according status message + plus 3 inputs/outputs from the aggregator
519 // itself "sA" is not used because the tag "A" is its input. The output tag is "AGG_A"
520 BOOST_TEST(accessorsAggAggA.size() == 5);
521 BOOST_CHECK(checkForName(accessorsAggAggA, "aggregatedA"));
522 BOOST_CHECK(!checkForName(accessorsAggAggA, "sA"));
523
524 auto accessorsAggAggB = app.aggAggB.getAccessorListRecursive();
525 // One aggregated input ("aggregatedB") + according status message + plus 3 inputs/outputs from the aggregator
526 // itself "sB1" and "sB2" are not used because their input is already aggregated.
527 BOOST_TEST(accessorsAggAggB.size() == 5);
528 BOOST_CHECK(checkForName(accessorsAggAggB, "aggregatedB"));
529 BOOST_CHECK(!checkForName(accessorsAggAggB, "sB1"));
530 BOOST_CHECK(!checkForName(accessorsAggAggB, "sB2"));
531 }
532
533 /********************************************************************************************************************/
534
538
539 StatusGenerator s{this, "s", "Status", {}, ctk::StatusOutput::Status::OK};
540
542 using ctk::ModuleGroup::ModuleGroup;
543
544 StatusGenerator s1{this, "s1", "Status 1", {}, ctk::StatusOutput::Status::OK};
545
546 StatusWithMessageGenerator s2{this, "s2", "Status 2", {}, ctk::StatusOutput::Status::OK};
547
549 this, "/Aggregated/extraStatus", "aggregated status description", ctk::StatusAggregator::PriorityMode::ofwk};
550
551 } outerGroup{this, "OuterGroup", ""};
552
554 this, "Aggregated/status", "aggregated status description", ctk::StatusAggregator::PriorityMode::fwko};
555 };
556
557 /********************************************************************************************************************/
558
559 // test behavior for status+string:
560 // test that status aggregator always has a message output and hands it over to next status aggregator
561 BOOST_AUTO_TEST_CASE(testStatusMessage) {
562 std::cout << "testStatusMessage" << std::endl;
564
565 ctk::TestFacility test(app);
566
567 auto status = test.getScalar<int>("/Aggregated/status");
568 auto statusMessage = test.getScalar<std::string>("/Aggregated/status_message");
569 auto innerStatus = test.getScalar<int>("/Aggregated/extraStatus");
570 auto innerStatusMessage = test.getScalar<std::string>("/Aggregated/extraStatus_message");
571
572 test.runApplication();
573
574 // check the initial values
575 innerStatus.readLatest();
576 BOOST_CHECK_EQUAL(int(innerStatus), int(ctk::StatusOutput::Status::OK));
577 innerStatusMessage.readLatest();
578 BOOST_CHECK_EQUAL(std::string(innerStatusMessage), "");
579 status.readLatest();
580 BOOST_CHECK_EQUAL(int(status), int(ctk::StatusOutput::Status::OK));
581 statusMessage.readLatest();
582 BOOST_CHECK_EQUAL(std::string(statusMessage), "");
583
584 // check normal status (without message) going to fault
585 app.outerGroup.s1.status = ctk::StatusOutput::Status::FAULT;
586 app.outerGroup.s1.status.write();
587 test.stepApplication();
588 status.readLatest();
589 statusMessage.readLatest();
590 innerStatus.readLatest();
591 innerStatusMessage.readLatest();
592 BOOST_CHECK_EQUAL(int(status), int(ctk::StatusOutput::Status::FAULT));
593 std::string faultString1 = "/OuterGroup/s1/s1 switched to FAULT";
594 BOOST_CHECK_EQUAL(std::string(statusMessage), faultString1);
595 BOOST_CHECK_EQUAL(int(innerStatus), int(ctk::StatusOutput::Status::FAULT));
596 BOOST_CHECK_EQUAL(std::string(innerStatusMessage), faultString1);
597
598 // go back to OK
599 app.outerGroup.s1.status = ctk::StatusOutput::Status::OK;
600 app.outerGroup.s1.status.write();
601 test.stepApplication();
602 status.readLatest();
603 statusMessage.readLatest();
604 innerStatus.readLatest();
605 innerStatusMessage.readLatest();
606 BOOST_CHECK_EQUAL(int(status), int(ctk::StatusOutput::Status::OK));
607 BOOST_CHECK_EQUAL(std::string(statusMessage), "");
608 BOOST_CHECK_EQUAL(int(innerStatus), int(ctk::StatusOutput::Status::OK));
609 BOOST_CHECK_EQUAL(std::string(innerStatusMessage), "");
610
611 // check StatusWithMessage going to fault
612 std::string faultString2 = "Status 2 at fault";
613 app.outerGroup.s2.setCurrentVersionNumber({});
614 app.outerGroup.s2.status.write(ctk::StatusOutput::Status::FAULT, faultString2);
615 test.stepApplication();
616 status.readLatest();
617 statusMessage.readLatest();
618 innerStatus.readLatest();
619 innerStatusMessage.readLatest();
620 BOOST_CHECK_EQUAL(int(status), int(ctk::StatusOutput::Status::FAULT));
621 BOOST_CHECK_EQUAL(std::string(statusMessage), faultString2);
622 BOOST_CHECK_EQUAL(int(innerStatus), int(ctk::StatusOutput::Status::FAULT));
623 BOOST_CHECK_EQUAL(std::string(innerStatusMessage), faultString2);
624
625 // set normal status to fault, too, to see the right message "wins" (first message should stay)
626 app.outerGroup.s1.setCurrentVersionNumber({});
627 app.outerGroup.s1.status = ctk::StatusOutput::Status::FAULT;
628 app.outerGroup.s1.status.write();
629 test.stepApplication();
630 status.readLatest();
631 statusMessage.readLatest();
632 innerStatus.readLatest();
633 innerStatusMessage.readLatest();
634 BOOST_CHECK_EQUAL(int(status), int(ctk::StatusOutput::Status::FAULT));
635 BOOST_CHECK_EQUAL(std::string(statusMessage), faultString2);
636 BOOST_CHECK_EQUAL(int(innerStatus), int(ctk::StatusOutput::Status::FAULT));
637 BOOST_CHECK_EQUAL(std::string(innerStatusMessage), faultString2);
638
639 // go back to OK
640 app.outerGroup.s1.setCurrentVersionNumber({});
641 app.outerGroup.s1.status = ctk::StatusOutput::Status::OK;
642 app.outerGroup.s1.status.write();
643 app.outerGroup.s2.setCurrentVersionNumber({});
644 app.outerGroup.s2.status.writeOk();
645 test.stepApplication();
646 status.readLatest();
647 statusMessage.readLatest();
648 innerStatus.readLatest();
649 innerStatusMessage.readLatest();
650 BOOST_CHECK_EQUAL(int(status), int(ctk::StatusOutput::Status::OK));
651 BOOST_CHECK_EQUAL(std::string(statusMessage), "");
652 BOOST_CHECK_EQUAL(int(innerStatus), int(ctk::StatusOutput::Status::OK));
653 BOOST_CHECK_EQUAL(std::string(innerStatusMessage), "");
654
655 // set both status to fault in alternate order (compared to before), again the first message should "win"
656 app.outerGroup.s1.setCurrentVersionNumber({});
657 app.outerGroup.s1.status = ctk::StatusOutput::Status::FAULT;
658 app.outerGroup.s1.status.write();
659 app.outerGroup.s2.setCurrentVersionNumber({});
660 app.outerGroup.s2.status.write(ctk::StatusOutput::Status::FAULT, faultString2);
661 test.stepApplication();
662 status.readLatest();
663 statusMessage.readLatest();
664 innerStatus.readLatest();
665 innerStatusMessage.readLatest();
666 BOOST_CHECK_EQUAL(int(status), int(ctk::StatusOutput::Status::FAULT));
667 BOOST_CHECK_EQUAL(std::string(statusMessage), faultString1);
668 BOOST_CHECK_EQUAL(int(innerStatus), int(ctk::StatusOutput::Status::FAULT));
669 BOOST_CHECK_EQUAL(std::string(innerStatusMessage), faultString1);
670 }
671
672 /********************************************************************************************************************/
673
674} // namespace Tests::testStatusAggregator
void shutdown() override
This will remove the global pointer to the instance and allows creating another instance afterwards.
void setCurrentVersionNumber(VersionNumber) override
Set the current version number.
ApplicationModule()=default
Default constructor: Allows late initialisation of modules (e.g.
void setCurrentVersionNumber(VersionNumber versionNumber) override
Set the current version number.
void incrementDataFaultCounter() override
Set the data validity flag to fault and increment the fault counter.
const std::string & getName() const
Get the name of the module instance.
Definition EntityOwner.h:59
const std::string & getDescription() const
Get the description of the module instance.
Definition EntityOwner.h:69
std::list< VariableNetworkNode > getAccessorListRecursive() const
Obtain the list of accessors/variables associated with this instance and any submodules.
friend class Application
Definition ModuleGroup.h:47
bool write(ChimeraTK::VersionNumber versionNumber)=delete
void setAndWrite(UserType newValue, VersionNumber versionNumber)=delete
Helper class to facilitate tests of applications based on ApplicationCore.
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.
InvalidityTracer application module.
const std::unordered_set< std::string > TAGS
Convenience type definition which can optionally be used as a shortcut for the type which defines a l...
Definition EntityOwner.h:27
The StatusAggregator collects results of multiple StatusMonitor instances and aggregates them into a ...
void setWarnMixedMessage(std::string message)
Set a custom message for the warn mixed state.
Special ScalarOutput which represents a status which can be aggregated by the StatusAggregator.
A VariableGroup for error status and message reporting.
void writeOk()
Set status to OK, clear the message and write the outputs.
void write(StatusOutput::Status status, std::string message)
Set the status and the message and write the outputs.
void prepare() override
Prepare the execution of the module.
StatusGenerator(ctk::ModuleGroup *owner, const std::string &name, const std::string &description, const std::unordered_set< std::string > &tags={}, ctk::StatusOutput::Status initialStatus=ctk::StatusOutput::Status::OFF)
void mainLoop() override
To be implemented by the user: function called in a separate thread executing the main loop of the mo...
StatusWithMessageGenerator(ctk::ModuleGroup *owner, const std::string &name, const std::string &description, const std::unordered_set< std::string > &tags={}, ctk::StatusOutput::Status initialStatus=ctk::StatusOutput::Status::OFF)
void prepare() override
Prepare the execution of the module.
void mainLoop() override
To be implemented by the user: function called in a separate thread executing the main loop of the mo...
TestPrioApplication(ctk::StatusOutput::Status theInitialValue)