Skip to content
Snippets Groups Projects
Unverified Commit 759c6660 authored by Martin Christoph Hierholzer's avatar Martin Christoph Hierholzer Committed by GitHub
Browse files

Merge pull request #211 from ChimeraTK/wip-mhier-noticket-status-monitor-interface

modernise StatusMonitor interface
parents 1cfdcd67 9b4ffe0e
No related branches found
No related tags found
No related merge requests found
...@@ -42,6 +42,8 @@ namespace ChimeraTK { ...@@ -42,6 +42,8 @@ namespace ChimeraTK {
* type of the variable to be monitored. A non-template base class * type of the variable to be monitored. A non-template base class
* facilitates checking for the type in the StatusAggregator, which * facilitates checking for the type in the StatusAggregator, which
* needs to identify any StatusMonitor. * needs to identify any StatusMonitor.
*
* FIXME This distinction between StatusMonitorImpl and StatusMonitor is no longer required. Merge the classes!
*/ */
struct StatusMonitor : ApplicationModule { struct StatusMonitor : ApplicationModule {
StatusMonitor(EntityOwner* owner, const std::string& name, const std::string& description, const std::string& input, StatusMonitor(EntityOwner* owner, const std::string& name, const std::string& description, const std::string& input,
...@@ -217,30 +219,91 @@ namespace ChimeraTK { ...@@ -217,30 +219,91 @@ namespace ChimeraTK {
} }
}; };
/** Module for status monitoring of an exact value. /**
* If value monitored is not exactly the same. an fault state will be * Module for status monitoring of an exact value.
* reported.*/ *
* If monitored input value is not exactly the same as the requiredValue, a fault state will be reported. If the
* parameter variable "disable" is set to a non-zero value, the monitoring is disabled and the output status is
* always OFF.
*
* Note: It is strongly recommended to use this monitor only for integer data types or strings, as floating point
* data types should never be compared with exact equality.
*/
template<typename T> template<typename T>
struct ExactMonitor : public StatusMonitorImpl<T> { struct ExactMonitor : ApplicationModule {
using StatusMonitorImpl<T>::StatusMonitorImpl; /**
/**FAULT state if value is not equal to requiredValue*/ * Constructor for exact monitoring module.
ScalarPushInput<T> requiredValue{this, "requiredValue", "", "", StatusMonitor::_parameterTags}; *
* inputPath: qualified path of the variable to monitor
* outputPath: qualified path of the status output variable
* parameterPath: qualified path of the VariableGroup holding the parameter variables requiredValue and disable
*
* All qualified paths can be either relative or absolute to the given owner. See HierarchyModifyingGroup for
* more details.
*/
ExactMonitor(EntityOwner* owner, const std::string& inputPath, const std::string& outputPath,
const std::string& parameterPath, const std::string& description,
const std::unordered_set<std::string>& outputTags = {},
const std::unordered_set<std::string>& parameterTags = {})
: ExactMonitor(owner, inputPath, outputPath, parameterPath + "/requiredValue", parameterPath + "/disable",
description, outputTags, parameterTags) {}
/**This is where state evaluation is done*/ /**
* Constructor for exact monitoring module.
*
* inputPath: qualified path of the variable to monitor
* outputPath: qualified path of the status output variable
* requiredValuePath: qualified path of the parameter variable requiredValue
* disablePath: qualified path of the parameter variable disable
*
* All qualified paths can be either relative or absolute to the given owner. See HierarchyModifyingGroup for
* more details.
*/
ExactMonitor(EntityOwner* owner, const std::string& inputPath, const std::string& outputPath,
const std::string& requiredValuePath, const std::string& disablePath, const std::string& description,
const std::unordered_set<std::string>& outputTags = {},
const std::unordered_set<std::string>& parameterTags = {})
: ApplicationModule(owner, "hidden", description, HierarchyModifier::hideThis),
watch(this, inputPath, "", "Value to monitor"),
requiredValue(this, requiredValuePath, "", "Value to compare with", parameterTags),
disable(this, disablePath, "", "Disable the status monitor", parameterTags),
status(this, outputPath, "Resulting status", outputTags) {}
ExactMonitor() = default;
/** Variable to monitor */
ModifyHierarchy<ScalarPushInput<T>> watch;
/** The required value to compare with */
ModifyHierarchy<ScalarPushInput<T>> requiredValue;
/** Disable/enable the entire status monitor */
ModifyHierarchy<ScalarPushInput<int>> disable;
/** Result of the monitor */
ModifyHierarchy<StatusOutput> status;
/** This is where state evaluation is done */
void mainLoop() { void mainLoop() {
/** If there is a change either in value monitored or in requiredValue, the status is re-evaluated*/ // If there is a change either in value monitored or in requiredValue, the status is re-evaluated
ReadAnyGroup group{StatusMonitorImpl<T>::oneUp.watch, StatusMonitorImpl<T>::disable, requiredValue}; ReadAnyGroup group{watch.value, disable.value, requiredValue.value};
while(true) { while(true) {
if(StatusMonitorImpl<T>::disable != 0) { StatusOutput::Status newStatus;
StatusMonitorImpl<T>::status = StatusOutput::Status::OFF; if(disable.value != 0) {
newStatus = StatusOutput::Status::OFF;
} }
else if(StatusMonitorImpl<T>::oneUp.watch != requiredValue) { else if(watch.value != requiredValue.value) {
StatusMonitorImpl<T>::status = StatusOutput::Status::FAULT; newStatus = StatusOutput::Status::FAULT;
} }
else { else {
StatusMonitorImpl<T>::status = StatusOutput::Status::OK; newStatus = StatusOutput::Status::OK;
}
// update only if status has changed, but always in case of initial value
if(status.value != newStatus || status.value.getVersionNumber() == VersionNumber{nullptr}) {
status.value = newStatus;
status.value.write();
} }
StatusMonitorImpl<T>::status.write();
group.readAny(); group.readAny();
} }
} }
......
...@@ -20,10 +20,15 @@ namespace ChimeraTK { ...@@ -20,10 +20,15 @@ namespace ChimeraTK {
class Module; class Module;
class VirtualModule; class VirtualModule;
/** Base class for owners of other EntityOwners (e.g. Modules) and Accessors. /**
* @todo Rename this class to "Owner" and make it more generic. It should * Convenience type definition which can optionally be used as a shortcut for the type which defines a list of
* basically just implement the "Composite Pattern". The classes AccessorBase, * tags.
* Module and Owner should have a common base class called "Component". */
using TAGS = const std::unordered_set<std::string>;
/**
* Base class for owners of other EntityOwners (e.g. Modules) and Accessors.
* FIXME: Unify with Module class (not straight forward!).
*/ */
class EntityOwner { class EntityOwner {
public: public:
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
namespace ChimeraTK { namespace ChimeraTK {
/********************************************************************************************************************/
/** /**
* A HierarchyModifyingGroup is a VariableGroup which can easily appear at a different place in the hierarchy by * A HierarchyModifyingGroup is a VariableGroup which can easily appear at a different place in the hierarchy by
* specifying a qualified name. The qualifiedName can contain multiple hierarchy levels separated by slashes "/". * specifying a qualified name. The qualifiedName can contain multiple hierarchy levels separated by slashes "/".
...@@ -50,6 +52,27 @@ namespace ChimeraTK { ...@@ -50,6 +52,27 @@ namespace ChimeraTK {
std::vector<std::string> _splittedPath; std::vector<std::string> _splittedPath;
}; };
/********************************************************************************************************************/
/**
* Convenience version of the HierarchyModifyingGroup with exactly one variable inside. The constructor takes the
* qualified name of the variable and splits it internally into the path name (for the HierarchyModifyingGroup) and
* the unqialified variable name.
*
* The template argument must be one of the Scalar*Input, ScalarOutput classes resp. their Array counterparts.
*/
template<typename ACCESSOR>
struct ModifyHierarchy : HierarchyModifyingGroup {
template<typename... Types>
ModifyHierarchy(Module* owner, const std::string& qualifiedName, Types... args)
: HierarchyModifyingGroup(owner, HierarchyModifyingGroup::getPathName(qualifiedName), ""),
value(this, HierarchyModifyingGroup::getUnqualifiedName(qualifiedName), args...) {}
ACCESSOR value;
};
/********************************************************************************************************************/
} // namespace ChimeraTK } // namespace ChimeraTK
#endif // HIERARCHYMODIFYINGGROUP_H #endif // HIERARCHYMODIFYINGGROUP_H
...@@ -9,8 +9,8 @@ ...@@ -9,8 +9,8 @@
#include "ApplicationModule.h" #include "ApplicationModule.h"
#include "ControlSystemModule.h" #include "ControlSystemModule.h"
#include "ScalarAccessor.h" #include "ScalarAccessor.h"
#include "TestFacility.h"
#include "StatusMonitor.h" #include "StatusMonitor.h"
#include "TestFacility.h"
using namespace boost::unit_test_framework; using namespace boost::unit_test_framework;
namespace ctk = ChimeraTK; namespace ctk = ChimeraTK;
...@@ -19,9 +19,9 @@ namespace ctk = ChimeraTK; ...@@ -19,9 +19,9 @@ namespace ctk = ChimeraTK;
template<typename T> template<typename T>
struct TestApplication : public ctk::Application { struct TestApplication : public ctk::Application {
TestApplication() : Application("testSuite") {} TestApplication() : Application("testSuite") {}
~TestApplication() { shutdown(); } ~TestApplication() override { shutdown(); }
void defineConnections() { void defineConnections() override {
findTag(".*").connectTo(cs); // publish everything to CS findTag(".*").connectTo(cs); // publish everything to CS
findTag("MY_MONITOR").connectTo(cs["MyNiceMonitorCopy"]); // cable again, checking that the tag is applied correctly findTag("MY_MONITOR").connectTo(cs["MyNiceMonitorCopy"]); // cable again, checking that the tag is applied correctly
findTag("MON_PARAMS") findTag("MON_PARAMS")
...@@ -34,6 +34,24 @@ struct TestApplication : public ctk::Application { ...@@ -34,6 +34,24 @@ struct TestApplication : public ctk::Application {
{"MON_OUTPUT"}, {"MON_PARAMS"}, {"MY_MONITOR"}}; {"MON_OUTPUT"}, {"MON_PARAMS"}, {"MY_MONITOR"}};
}; };
/* dummy application - for new StatusMonitor interface */
template<typename T>
struct TestApplicationNewInterface : public ctk::Application {
TestApplicationNewInterface() : Application("testSuite") {}
~TestApplicationNewInterface() override { shutdown(); }
void defineConnections() override {
findTag(".*").connectTo(cs); // publish everything to CS
findTag("MON_PARAMS")
.connectTo(cs["MonitorParameters"]); // cable the parameters in addition (checking that tags are set correctly)
findTag("MON_OUTPUT")
.connectTo(cs["MonitorOutput"]); // cable the parameters in addition (checking that tags are set correctly)
}
ctk::ControlSystemModule cs;
T monitor{this, "/input/path", "/output/path", "/parameters", "Now this is a nice monitor...",
ctk::TAGS{"MON_OUTPUT"}, ctk::TAGS{"MON_PARAMS"}};
};
/*********************************************************************************************************************/ /*********************************************************************************************************************/
BOOST_AUTO_TEST_CASE(testMaxMonitor) { BOOST_AUTO_TEST_CASE(testMaxMonitor) {
...@@ -552,34 +570,34 @@ BOOST_AUTO_TEST_CASE(testRangeMonitor) { ...@@ -552,34 +570,34 @@ BOOST_AUTO_TEST_CASE(testRangeMonitor) {
BOOST_AUTO_TEST_CASE(testExactMonitor) { BOOST_AUTO_TEST_CASE(testExactMonitor) {
std::cout << "testExactMonitor" << std::endl; std::cout << "testExactMonitor" << std::endl;
TestApplication<ctk::ExactMonitor<float>> app; TestApplicationNewInterface<ctk::ExactMonitor<int64_t>> app;
// check that the reserved StatusOutput tag is present at the output, required for StatusAggregator integration // check that the reserved StatusOutput tag is present at the output, required for StatusAggregator integration
auto tags = ctk::VariableNetworkNode(app.monitor.status).getTags(); auto tags = ctk::VariableNetworkNode(app.monitor.status.value).getTags();
BOOST_CHECK(tags.find(ctk::StatusOutput::tagStatusOutput) != tags.end()); BOOST_CHECK(tags.find(ctk::StatusOutput::tagStatusOutput) != tags.end());
ctk::TestFacility test; ctk::TestFacility test;
test.runApplication(); test.runApplication();
//app.dumpConnections(); //app.dumpConnections();
auto requiredValue = test.getScalar<float>(std::string("/Monitor/requiredValue")); auto requiredValue = test.getScalar<int64_t>(std::string("/parameters/requiredValue"));
requiredValue = 40.9; requiredValue = 409;
requiredValue.write(); requiredValue.write();
test.stepApplication(); test.stepApplication();
auto watch = test.getScalar<float>(std::string("/watch")); auto watch = test.getScalar<int64_t>(std::string("/input/path"));
watch = 40.9; watch = 409;
watch.write(); watch.write();
test.stepApplication(); test.stepApplication();
auto status = test.getScalar<int32_t>(std::string("/Monitor/status")); auto status = test.getScalar<int32_t>(std::string("/output/path"));
status.readLatest(); status.readLatest();
//should be in OK state. //should be in OK state.
BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK)); BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
// drop in a disable test. // drop in a disable test.
auto disable = test.getScalar<int>("/Monitor/disable"); auto disable = test.getScalar<int>("/parameters/disable");
disable = 1; disable = 1;
disable.write(); disable.write();
test.stepApplication(); test.stepApplication();
...@@ -593,7 +611,7 @@ BOOST_AUTO_TEST_CASE(testExactMonitor) { ...@@ -593,7 +611,7 @@ BOOST_AUTO_TEST_CASE(testExactMonitor) {
BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK)); BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
//set watch value different than required value //set watch value different than required value
watch = 41.4; watch = 414;
watch.write(); watch.write();
test.stepApplication(); test.stepApplication();
status.readLatest(); status.readLatest();
...@@ -613,7 +631,7 @@ BOOST_AUTO_TEST_CASE(testExactMonitor) { ...@@ -613,7 +631,7 @@ BOOST_AUTO_TEST_CASE(testExactMonitor) {
status.readLatest(); status.readLatest();
BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT)); BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
watch = 40.9; watch = 409;
watch.write(); watch.write();
test.stepApplication(); test.stepApplication();
status.readLatest(); status.readLatest();
...@@ -621,7 +639,7 @@ BOOST_AUTO_TEST_CASE(testExactMonitor) { ...@@ -621,7 +639,7 @@ BOOST_AUTO_TEST_CASE(testExactMonitor) {
BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK)); BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
//set requiredValue value different than watch value //set requiredValue value different than watch value
requiredValue = 41.3; requiredValue = 413;
requiredValue.write(); requiredValue.write();
test.stepApplication(); test.stepApplication();
status.readLatest(); status.readLatest();
...@@ -629,7 +647,7 @@ BOOST_AUTO_TEST_CASE(testExactMonitor) { ...@@ -629,7 +647,7 @@ BOOST_AUTO_TEST_CASE(testExactMonitor) {
BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT)); BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::FAULT));
//set requiredValue value equals to watch value //set requiredValue value equals to watch value
requiredValue = 40.9; requiredValue = 409;
requiredValue.write(); requiredValue.write();
test.stepApplication(); test.stepApplication();
status.readLatest(); status.readLatest();
...@@ -637,10 +655,12 @@ BOOST_AUTO_TEST_CASE(testExactMonitor) { ...@@ -637,10 +655,12 @@ BOOST_AUTO_TEST_CASE(testExactMonitor) {
BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK)); BOOST_CHECK_EQUAL(status, static_cast<int>(ChimeraTK::StatusOutput::Status::OK));
// check that the tags are applied correctly // check that the tags are applied correctly
BOOST_CHECK_EQUAL(status, test.readScalar<int32_t>("/MyNiceMonitorCopy/Monitor/status")); BOOST_CHECK_EQUAL(status, test.readScalar<int32_t>("/MonitorOutput/output/path"));
BOOST_CHECK_EQUAL(status, test.readScalar<int32_t>("/MonitorOutput/Monitor/status")); BOOST_CHECK_EQUAL(requiredValue, test.readScalar<int64_t>("/MonitorParameters/parameters/requiredValue"));
BOOST_CHECK_EQUAL(watch, test.readScalar<float>("/MyNiceMonitorCopy/watch")); disable = 1;
BOOST_CHECK_EQUAL(requiredValue, test.readScalar<float>("/MonitorParameters/Monitor/requiredValue")); disable.write();
test.stepApplication();
BOOST_CHECK_EQUAL(disable, test.readScalar<int>("/MonitorParameters/parameters/disable"));
} }
/*********************************************************************************************************************/ /*********************************************************************************************************************/
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment