Skip to content
Snippets Groups Projects
Commit 815c4a55 authored by Martin Christoph Hierholzer's avatar Martin Christoph Hierholzer
Browse files

Merge branch 'master' of github.com:ChimeraTK/ApplicationCore

parents 019c70f2 2872e808
No related branches found
No related tags found
No related merge requests found
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
#include <vector> #include <vector>
#include <string> #include <string>
namespace ChimeraTK{ namespace ChimeraTK {
/** /**
* The StatusAggregator collects results of multiple StatusMonitor instances * The StatusAggregator collects results of multiple StatusMonitor instances
...@@ -21,30 +21,31 @@ namespace ChimeraTK{ ...@@ -21,30 +21,31 @@ namespace ChimeraTK{
* included in the scope (ModuleGroup, Application, ...) of interest. * included in the scope (ModuleGroup, Application, ...) of interest.
*/ */
class StatusAggregator : public ApplicationModule { class StatusAggregator : public ApplicationModule {
public: public:
StatusAggregator(EntityOwner* owner, const std::string& name, const std::string& description, StatusAggregator(EntityOwner* owner, const std::string& name, const std::string& description,
const std::string& output, const std::string& output, HierarchyModifier modifier, const std::unordered_set<std::string>& tags = {})
HierarchyModifier modifier, const std::unordered_set<std::string>& tags = {}) : ApplicationModule(owner, name, description, modifier, tags), status(this, output, "", "", {}) {
: ApplicationModule(owner, name, description, modifier, tags), status(this, output, "", "", {}) {
populateStatusInput(); populateStatusInput();
} }
~StatusAggregator() override {} StatusAggregator() = default;
StatusAggregator(StatusAggregator&&) = default;
protected: ~StatusAggregator() override {}
protected:
void mainLoop() override { void mainLoop() override {
std::cout << "Entered StatusAggregator::mainLoop()" << std::endl; std::cout << "Entered StatusAggregator::mainLoop()" << std::endl;
// while(true){ // while(true){
// // Status collection goes here // // Status collection goes here
// } // }
} }
/// Recursivly search for StatusMonitors and other StatusAggregators /// Recursivly search for StatusMonitors and other StatusAggregators
void populateStatusInput(); void populateStatusInput();
/// Helper for populateStatusInput
void scanAndPopulateFromHierarchyLevel(std::list<VariableNetworkNode> nodes);
/**One of four possible states to be reported*/ /**One of four possible states to be reported*/
ScalarOutput<uint16_t> status; ScalarOutput<uint16_t> status;
...@@ -53,11 +54,10 @@ namespace ChimeraTK{ ...@@ -53,11 +54,10 @@ namespace ChimeraTK{
std::vector<ScalarPushInput<uint16_t>> statusInput; std::vector<ScalarPushInput<uint16_t>> statusInput;
//TODO Also provide this for the aggregator? //TODO Also provide this for the aggregator?
// /** Disable the monitor. The status will always be OFF. You don't have to connect this input. // /** Disable the monitor. The status will always be OFF. You don't have to connect this input.
// * When there is no feeder, ApplicationCore will connect it to a constant feeder with value 0, hence the monitor is always enabled. // * When there is no feeder, ApplicationCore will connect it to a constant feeder with value 0, hence the monitor is always enabled.
// */ // */
// ScalarPushInput<int> disable{this, "disable", "", "Disable the status monitor"}; // ScalarPushInput<int> disable{this, "disable", "", "Disable the status monitor"};
}; };
} // namespace ChimeraTK } // namespace ChimeraTK
......
#include "StatusAggregator.h" #include "StatusAggregator.h"
#include <list> #include <list>
#include <regex>
namespace ChimeraTK{ namespace ChimeraTK {
void StatusAggregator::populateStatusInput(){ void StatusAggregator::populateStatusInput() {
std::cout << "Populating aggregator " << getName() << ", fully qualified name is: " << getQualifiedName()
<< std::endl;
// Another try, virtualise the entire Application
auto virtualisedApplication = Application::getInstance().findTag(".*");
auto virtualPathToThis = getVirtualQualifiedName();
// This does not work, we need to operate on the virtual hierarchy // Remove application name and name of leaf node from the path
//std::list<Module*> subModuleList{getOwner()->getSubmoduleList()}; auto pathBegin = virtualPathToThis.find_first_not_of("/" + Application::getInstance().getName() + "/");
auto pathEnd = virtualPathToThis.find_last_of("/") - 1;
// This approach crashes auto pathWithoutAppAndLeafNode{virtualPathToThis.substr(pathBegin, pathEnd - pathBegin + 1)};
// auto subModuleList = getOwner()->findTag(".*").getSubmoduleList();
// for(auto module : subModuleList){ std::list<VariableNetworkNode> allAccessors;
std::list<Module*> allSubmodules;
if(pathWithoutAppAndLeafNode == getName()) {
// Path to this module, the parent is the Application
allAccessors = virtualisedApplication.getAccessorList();
allSubmodules = virtualisedApplication.getSubmoduleList();
}
else {
Module& virtualParent =
virtualisedApplication.submodule(virtualPathToThis.substr(pathBegin, pathEnd - pathBegin + 1));
// std::cout << "Testing Module " << module->getName() << std::endl; virtualParent.dump();
// auto accList{module->getAccessorList()};
// }
allAccessors = virtualParent.getAccessorList();
allSubmodules = virtualParent.getSubmoduleList();
}
// Works, as long as we do not use HIerarchyModifiers other than "none" in the test app std::cout << " Size of allAccessors: " << allAccessors.size() << std::endl;
std::cout << "Populating aggregator " << getName() << std::endl; std::cout << " Size of allSubmodules: " << allSubmodules.size() << std::endl;
auto allAccessors{getOwner()->findTag(".*").getAccessorListRecursive()}; // Two approaches to get the modules of interest to feed to scanAndPopulateFromHierarchyLeve:
// 1. directly call getSubmoduleList on each level: gives VirtualModules and the dynamic_casts below fail
// 2.use getAccessorList and call getOwningModule() on each Accessor (as done below): Does not find the status outputs right now
// Also, this means more effort to detect and recurse into ModuleGroups
//for(auto acc : allAccessors) {
//if(acc.getDirection().dir == VariableDirection::feeding) {
//std::cout << " -- Accessor: " << acc.getName() << " of module: " << acc.getOwningModule()->getName()
//<< std::endl;
//}
std::cout << " Size of allAccessors: " << allAccessors.size() << std::endl; scanAndPopulateFromHierarchyLevel(allAccessors);
for(auto acc : allAccessors){ std::cout << std::endl << std::endl;
} // poplateStatusInput()*/
if(acc.getDirection().dir == VariableDirection::feeding){
std::cout << " -- Accessor: " << acc.getName()
<< " of module: " << acc.getOwningModule()->getName()
<< std::endl;
}
void StatusAggregator::scanAndPopulateFromHierarchyLevel(std::list<VariableNetworkNode> nodes) {
if(nodes.empty()) {
return;
} }
bool statusAggregatorFound{false};
std::list<Module*> instancesToBeAggregated;
// This does loops per level:
// 1. Find all StatusMonitors and StatusAggregators on this level, if there
// is an aggregator, we can discard the Monitors on this level
// 2. Iterate over instancesToBeAggregated and add to statusInput
for(auto node : nodes) {
// Only check feeding nodes to test each Module only once on the status output
if(node.getDirection().dir != VariableDirection::feeding){
continue;
}
auto module{node.getOwningModule()};
std::cout << "Scanning Module " << module->getName() << std::endl;
if(dynamic_cast<StatusMonitor*>(module)) {
std::cout << " Found Monitor " << module->getName() << std::endl;
}
else if(dynamic_cast<StatusAggregator*>(module)) {
std::string moduleName = module->getName();
if(statusAggregatorFound){
throw ChimeraTK::logic_error("StatusAggregator: A second instance was found on the same hierarchy level.");
}
statusAggregatorFound = true;
// if(dynamic_cast<StatusMonitor*>(module)){ std::cout << "Found Aggregator " << moduleName << std::endl;
// std::cout << " Found Monitor " << module->getName() << std::endl;
// }
// else if(dynamic_cast<StatusAggregator*>(module)){
// std::string moduleName = module->getName();
// std::cout << "Found Aggregator " << moduleName << std::endl; statusInput.emplace_back(this, moduleName, "", "");
}
else if(dynamic_cast<ModuleGroup*>(module)) {
std::cout << "Found ModuleGroup " << module->getName() << std::endl;
// Process level below
scanAndPopulateFromHierarchyLevel(module->getAccessorList());
}
else {
}
}
// statusInput.emplace_back(this, moduleName, "", ""); // 2. TODO Add status inputs
// } }
// else if(dynamic_cast<ModuleGroup*>(module)){ } // namespace ChimeraTK
// std::cout << "Found ModuleGroup " << module->getName() << std::endl;
// }
// else{}
// }
std::cout << std::endl << std::endl;
} // poplateStatusInput()*/
}
...@@ -44,27 +44,35 @@ options exist (highest priority first): ...@@ -44,27 +44,35 @@ options exist (highest priority first):
3. error - warning - ok or off - mixed state of ok/off results in a warning 3. error - warning - ok or off - mixed state of ok/off results in a warning
4. off - error - warning - ok 4. off - error - warning - ok
### Constraints and issues ###
* As detection of underlying instances can only be performed in the
constructor, the StatusAggregator has to be declared in user code after all
instances of `ChimeraTK::StatusMonitor` in a Module.
* Usage of `HierarchyModifier::moveToRoot` on a StatusAggregator is
controversial:
* Either: Detection of aggregated instances may take place from the virtual
hierarchy level of the original (C++) parent module downwards. The
Aggregator and its status output would then appear on the root level.
* Or: `HierarchyModifier::moveToRoot` is not allowd on StatusAggregators
## Implementation ## ## Implementation ##
### Requirements ### ### Requirements ###
* **R2.1**: The instances need to be dectected on the `ChimeraTK::VirtualModule` on the aggregator's level * **R2.1**: The detection of instances needs to be performed recursively from the StatusAggregator's virtual parent module on
* **R2.2**: The decteion has to work on instances the are modified by any of * **R2.2**: The detection has to work on instances the are modified by any of
the `ChimeraTK::HierarchyModifier`s the `ChimeraTK::HierarchyModifier`s
### Constraints and issues ### ### Constraints and issues ###
* The detection and connection of the instances to be aggregated has to be * The detection and connection of the instances to be aggregated has to be
performed in the constructor, later the variable household is fixed and the performed in the constructor, later the variable household is fixed and the
status inputs can not be added anymore status inputs can not be added anymore.
* `ChimeraTK::StatusMonitor`s on the same level and the StatusAggregator itself
must be ignored in the detection.
## Questions & issues ## * As a consequence of the implementation requirements, in order to get the
virtual parent module, the entire Application needs to be virtualised and the
search algorithm must then navigate to the parent module.
* Which Modifiers exactly cause problems? Probably these are are bugs and we * Related to R1.1.3: If a StatusAggregator is found on a level below, the
need separate treatment. But we should clarify if this blocks required use instances of `ChimeraTK::StatusMonitor`s on that level must be ignored.
cases of the aggregator.
...@@ -36,11 +36,18 @@ namespace ChimeraTK { ...@@ -36,11 +36,18 @@ namespace ChimeraTK {
void doPostRead(TransferType type, bool hasNewData) override; void doPostRead(TransferType type, bool hasNewData) override;
void doPreWrite(TransferType type, VersionNumber versionNumber) override; void doPreWrite(TransferType type, VersionNumber versionNumber) override;
void setDataValidity(ChimeraTK::DataValidity validity = ChimeraTK::DataValidity::ok) override {
localValidity = validity;
NDRegisterAccessorDecorator<T, T>::setDataValidity(validity);
}
protected: protected:
EntityOwner* _owner; EntityOwner* _owner;
/** value of validity flag from last read operation */ /** value of validity flag from last read operation */
DataValidity lastValidity{DataValidity::ok}; DataValidity lastValidity{DataValidity::ok};
/** value of validity flag from user */
DataValidity localValidity{DataValidity::ok};
bool isNonblockingRead{false}; bool isNonblockingRead{false};
}; };
......
...@@ -127,6 +127,8 @@ namespace ChimeraTK { ...@@ -127,6 +127,8 @@ namespace ChimeraTK {
return ((_owner != nullptr) ? _owner->getQualifiedName() : "") + "/" + _name; return ((_owner != nullptr) ? _owner->getQualifiedName() : "") + "/" + _name;
} }
virtual std::string getVirtualQualifiedName() const;
std::string getFullDescription() const override { std::string getFullDescription() const override {
if(_owner == nullptr) return _description; if(_owner == nullptr) return _description;
auto ownerDescription = _owner->getFullDescription(); auto ownerDescription = _owner->getFullDescription();
...@@ -155,7 +157,9 @@ namespace ChimeraTK { ...@@ -155,7 +157,9 @@ namespace ChimeraTK {
DataValidity getDataValidity() const override { return _owner->getDataValidity(); } DataValidity getDataValidity() const override { return _owner->getDataValidity(); }
void incrementDataFaultCounter() override { _owner->incrementDataFaultCounter(); } void incrementDataFaultCounter() override { _owner->incrementDataFaultCounter(); }
void decrementDataFaultCounter() override { _owner->decrementDataFaultCounter(); } void decrementDataFaultCounter() override { _owner->decrementDataFaultCounter(); }
void incrementExceptionCounter(bool writeAllOutputs) override { _owner->incrementExceptionCounter(writeAllOutputs); } void incrementExceptionCounter(bool writeAllOutputs) override {
_owner->incrementExceptionCounter(writeAllOutputs);
}
void decrementExceptionCounter() override { _owner->decrementExceptionCounter(); } void decrementExceptionCounter() override { _owner->decrementExceptionCounter(); }
protected: protected:
......
...@@ -25,7 +25,13 @@ namespace ChimeraTK { ...@@ -25,7 +25,13 @@ namespace ChimeraTK {
template<typename T> template<typename T>
void MetaDataPropagatingRegisterDecorator<T>::doPreWrite(TransferType type, VersionNumber versionNumber) { void MetaDataPropagatingRegisterDecorator<T>::doPreWrite(TransferType type, VersionNumber versionNumber) {
ChimeraTK::NDRegisterAccessorDecorator<T>::setDataValidity(_owner->getDataValidity()); if(localValidity == DataValidity::faulty) {
ChimeraTK::NDRegisterAccessorDecorator<T>::setDataValidity(DataValidity::faulty);
}
else {
ChimeraTK::NDRegisterAccessorDecorator<T>::setDataValidity(_owner->getDataValidity());
}
NDRegisterAccessorDecorator<T, T>::doPreWrite(type, versionNumber); NDRegisterAccessorDecorator<T, T>::doPreWrite(type, versionNumber);
} }
......
...@@ -163,4 +163,48 @@ namespace ChimeraTK { ...@@ -163,4 +163,48 @@ namespace ChimeraTK {
return (*this)[upperModuleName].submodule(remainingModuleNames); return (*this)[upperModuleName].submodule(remainingModuleNames);
} }
std::string Module::getVirtualQualifiedName() const {
std::string virtualQualifiedName{""};
const EntityOwner* currentLevelModule{this};
bool rootReached{false};
do {
if(currentLevelModule == &Application::getInstance()) {
rootReached = true;
}
auto currentLevelModifier = currentLevelModule->getHierarchyModifier();
switch(currentLevelModifier) {
case HierarchyModifier::none:
virtualQualifiedName = "/" + currentLevelModule->getName() + virtualQualifiedName;
break;
case HierarchyModifier::hideThis:
// Omit name of current level
break;
case HierarchyModifier::oneLevelUp:
virtualQualifiedName = "/" + currentLevelModule->getName() + virtualQualifiedName;
// Need to leave out to next level
// TODO This needs to catch the case that the mdifier is illegally used on the first level
currentLevelModule = dynamic_cast<const Module*>(currentLevelModule)->getOwner();
break;
case HierarchyModifier::oneUpAndHide:
// Need to leave out to next level
currentLevelModule = dynamic_cast<const Module*>(currentLevelModule)->getOwner();
break;
case HierarchyModifier::moveToRoot:
virtualQualifiedName =
"/" + Application::getInstance().getName() + "/" + currentLevelModule->getName() + virtualQualifiedName;
rootReached = true;
}
if(!rootReached) {
currentLevelModule = dynamic_cast<const Module*>(currentLevelModule)->getOwner();
}
} while(!rootReached);
return virtualQualifiedName;
}
} /* namespace ChimeraTK */ } /* namespace ChimeraTK */
...@@ -8,63 +8,59 @@ ...@@ -8,63 +8,59 @@
#include "ModuleGroup.h" #include "ModuleGroup.h"
#include "TestFacility.h" #include "TestFacility.h"
using namespace boost::unit_test_framework; using namespace boost::unit_test_framework;
namespace ctk = ChimeraTK; namespace ctk = ChimeraTK;
struct OuterGroup : public ctk::ModuleGroup { struct OuterGroup : public ctk::ModuleGroup {
using ctk::ModuleGroup::ModuleGroup; using ctk::ModuleGroup::ModuleGroup;
virtual ~OuterGroup(){} virtual ~OuterGroup() {}
ctk::MinMonitor<double_t> outerMinMonitor{this, "outerMinMonitor", "", "watch", "status", ctk::HierarchyModifier::none,
{"OUTER_MON_OUTPUT"}, {"OUTER_MON_PARAMS"},{"OUTER_MON_INPUT"}};
//ctk::StatusAggregator outerStatusAggregator{this, "outerStatusAggregator", "", ctk::HierarchyModifier::none, {"STATUS"}}; ctk::StateMonitor<uint8_t> outerStateMonitor{this, "outerStateMonitor", "", "watch", "status",
ctk::HierarchyModifier::none, {"OUTER_MON_OUTPUT"}, {"OUTER_MON_PARAMS"}, {"OUTER_MON_INPUT"}};
struct InnerGroup : public ctk::ModuleGroup { struct InnerGroup : public ctk::ModuleGroup {
using ctk::ModuleGroup::ModuleGroup; using ctk::ModuleGroup::ModuleGroup;
ctk::MinMonitor<double_t> innerMinMonitor{this, "innerMinMonitor", "", "minWatch", "minStatus", ctk::HierarchyModifier::none, ctk::StateMonitor<uint8_t> innerStateMonitorNone{this, "innerMinMonitorNone", "", "watch", "status",
{"INNER_MON_OUTPUT"}, {"INNER_MON_PARAMS"},{"INNER_MON_INPUT"}}; ctk::HierarchyModifier::none, {"INNER_MON_OUTPUT"}, {"INNER_MON_PARAMS"}, {"INNER_MON_INPUT"}};
ctk::StateMonitor<uint8_t> innerStateMonitor{this, "innerStateMonitor", "", "stateWatch", "stateStatus", ctk::HierarchyModifier::none,
{"INNER_MON_OUTPUT"}, {"INNER_MON_PARAMS"},{"INNER_MON_INPUT"}};
} innerGroup{this, "innerModuleGroup", ""}; ctk::StateMonitor<uint8_t> innerStateMonitorHideThis{this, "innerStateMonitorHideThis", "", "watch", "status",
ctk::HierarchyModifier::hideThis, {"INNER_MON_OUTPUT"}, {"INNER_MON_PARAMS"}, {"INNER_MON_INPUT"}};
}; ctk::StateMonitor<uint8_t> innerStateMonitorOneUp{this, "innerStateMonitorOneUp", "", "watch", "status",
ctk::HierarchyModifier::oneLevelUp, {"INNER_MON_OUTPUT"}, {"INNER_MON_PARAMS"}, {"INNER_MON_INPUT"}};
} innerGroup{this, "innerModuleGroup", "", ctk::HierarchyModifier::none};
ctk::StatusAggregator outerStatusAggregator{this, "outerStatusAggregator", "StatusAggregator of OuterGroup",
"groupStatus", ctk::HierarchyModifier::none, {"STATUS"}};
};
struct TestApplication : public ctk::Application { struct TestApplication : public ctk::Application {
TestApplication() : Application("testApp"){} TestApplication() : Application("testApp") {}
~TestApplication(){ shutdown(); } ~TestApplication() { shutdown(); }
OuterGroup outerModuleGroup1{this, "outerModuleGroup1", ""}; OuterGroup outerModuleGroup1{this, "outerModuleGroup1", "", ctk::HierarchyModifier::none};
OuterGroup outerModuleGroup2{this, "outerModuleGroup2", ""}; //OuterGroup outerModuleGroup2{this, "outerModuleGroup2", "", ctk::HierarchyModifier::hideThis};
ctk::StateMonitor<uint8_t> globalStateMonitor{this, "globalStateMonitor", "", "stateWatch", "stateStatus", ctk::HierarchyModifier::none, ctk::StateMonitor<uint8_t> globalStateMonitor{this, "globalStateMonitor", "", "stateWatch", "stateStatus",
{"GLOBAL_MON_OUTPUT"}, {"GLOBAL_MON_PARAMS"},{"GLOBAL_MON_INPUT"}}; ctk::HierarchyModifier::none, {"GLOBAL_MON_OUTPUT"}, {"GLOBAL_MON_PARAMS"}, {"GLOBAL_MON_INPUT"}};
ctk::ControlSystemModule cs; ctk::ControlSystemModule cs;
ctk::StatusAggregator globalStatusAggregator{this, "globalStatusAggregator", "Global StatusAggregator of testApp", ctk::StatusAggregator globalStatusAggregator{this, "globalStatusAggregator", "Global StatusAggregator of testApp",
"globalStatus", ctk::HierarchyModifier::none, {"STATUS"}}; "globalStatus", ctk::HierarchyModifier::none, {"STATUS"}};
void defineConnections(){ void defineConnections() { findTag(".*").connectTo(cs); }
findTag(".*").connectTo(cs);
}
}; };
BOOST_AUTO_TEST_CASE(testStatusAggregator) {
BOOST_AUTO_TEST_CASE(testStatusAggregator){
std::cout << "testStatusAggregator" << std::endl; std::cout << "testStatusAggregator" << std::endl;
TestApplication app; TestApplication app;
ctk::TestFacility test; ctk::TestFacility test;
test.runApplication(); test.runApplication();
//app.dump(); //app.cs.dump();
} }
#define BOOST_TEST_MODULE testVirtualHierarchy
#include <boost/test/included/unit_test.hpp>
#include "Application.h"
#include "ApplicationModule.h"
#include "ModuleGroup.h"
#include "TestFacility.h"
using namespace boost::unit_test_framework;
namespace ctk = ChimeraTK;
struct TestModule : public ctk::ApplicationModule {
using ctk::ApplicationModule::ApplicationModule;
ctk::ScalarPushInput<int> input{this, "input", "", {"CS"}};
ctk::ScalarOutput<int> output{this, "output", "", {"CS"}};
void mainLoop() override {}
};
struct TestModule2 : public ctk::ApplicationModule {
using ctk::ApplicationModule::ApplicationModule;
ctk::ScalarPushInput<int> input2{this, "input2", "", {"CS"}};
ctk::ScalarOutput<int> output2{this, "output2", "", {"CS"}};
void mainLoop() override {}
};
struct TestModule3 : public ctk::ApplicationModule {
using ctk::ApplicationModule::ApplicationModule;
ctk::ScalarPushInput<int> input3{this, "input3", "", {"CS"}};
ctk::ScalarOutput<int> output3{this, "output3", "", {"CS"}};
void mainLoop() override {}
};
struct InnerGroup : public ctk::ModuleGroup {
using ctk::ModuleGroup::ModuleGroup;
TestModule innerModule{this, "innerModule", "", ctk::HierarchyModifier::none};
TestModule2 innerModuleOneUpAndHide{this, "innerModuleOneUpAndHide", "", ctk::HierarchyModifier::oneUpAndHide};
TestModule3 innerModuleMoveToRoot{this, "innerModuleMoveToRoot", "", ctk::HierarchyModifier::moveToRoot};
TestModule3 innerModuleSameNameAsGroup{this, "innerModuleGroup", "", ctk::HierarchyModifier::oneLevelUp};
};
struct OuterGroup : public ctk::ModuleGroup {
OuterGroup(EntityOwner* owner, const std::string& name, const std::string& description,
ctk::HierarchyModifier modifier, ctk::HierarchyModifier innerGroupModifier = ctk::HierarchyModifier::none)
: ModuleGroup{owner, name, description, modifier}, innerGroup{this, "innerModuleGroup", "", innerGroupModifier} {
// Here, findTag should give proper exceptions if HierarchyModifiers are used illegally
auto allAccessors = getOwner()->findTag(".*").getAccessorListRecursive();
// for(auto acc : allAccessors) {
// std::cout << " -- Accessor: " << acc.getName() << " of module: " << acc.getOwningModule()->getName()
// << std::endl;
// }
}
virtual ~OuterGroup() {}
TestModule outerModule{this, "outerModuleInGroup", "", ctk::HierarchyModifier::oneLevelUp};
InnerGroup innerGroup;
};
struct TestApplication : public ctk::Application {
TestApplication(ctk::HierarchyModifier outerModuleModifier,
ctk::HierarchyModifier innerGroupModifier = ctk::HierarchyModifier::none)
: Application("testApp"), outerModuleGroup1{this, "outerModuleGroup1", "", ctk::HierarchyModifier::none,
innerGroupModifier},
outerModule{this, "outerModule", "", outerModuleModifier} {}
~TestApplication() { shutdown(); }
OuterGroup outerModuleGroup1;
TestModule outerModule;
ctk::ControlSystemModule cs;
void defineConnections() {
findTag(".*").connectTo(cs);
//cs.dump();
}
};
// Check if HierarchyModifiers are properly handled in the call to findTag
// in the constructor of TestApplication
BOOST_AUTO_TEST_CASE(testIllegalModifiers) {
std::cout << "testIllegalModifiers" << std::endl;
{
std::cout << " Creating TestApplication with outerModuleModifier = none " << std::endl;
// Should work
TestApplication app(ctk::HierarchyModifier::none);
ctk::TestFacility test;
std::cout << std::endl;
}
{
std::cout << " Creating TestApplication with outerModuleModifier = oneLevelUp " << std::endl;
TestApplication app(ctk::HierarchyModifier::oneLevelUp);
// Should detect illegal usage of oneLevelUp on first level below Application and throw
BOOST_CHECK_THROW(ctk::TestFacility test, ctk::logic_error)
std::cout << std::endl;
}
// Should detect illegal usage of oneUpAndHide on first level below Application and throw
// Currently leads to memory access violation, should also throw
// Bug is described by issue #166.
// {
// std::cout << "Creating TestApplication with outerModuleModifier = oneUpAndHide " << std::endl;
// TestApplication app(ctk::HierarchyModifier::oneUpAndHide);
// ctk::TestFacility test;
// std::cout << std::endl;
// }
{
std::cout << " Creating TestApplication with outerModuleModifier = moveToRoot " << std::endl;
// Should work
TestApplication app(ctk::HierarchyModifier::moveToRoot);
ctk::TestFacility test;
std::cout << std::endl;
}
}
BOOST_AUTO_TEST_CASE(testGetVirtualQualifiedName) {
std::cout << "testGetVirtualQualifiedName" << std::endl;
{
TestApplication app(ctk::HierarchyModifier::none);
ctk::TestFacility test;
//app.cs.dump();
BOOST_CHECK_EQUAL(app.outerModule.getVirtualQualifiedName(), "/testApp/outerModule");
BOOST_CHECK_EQUAL(app.outerModuleGroup1.getVirtualQualifiedName(), "/testApp/outerModuleGroup1");
BOOST_CHECK_EQUAL(app.outerModuleGroup1.outerModule.getVirtualQualifiedName(), "/testApp/outerModuleInGroup");
BOOST_CHECK_EQUAL(
app.outerModuleGroup1.innerGroup.getVirtualQualifiedName(), "/testApp/outerModuleGroup1/innerModuleGroup");
BOOST_CHECK_EQUAL(app.outerModuleGroup1.innerGroup.innerModule.getVirtualQualifiedName(),
"/testApp/outerModuleGroup1/innerModuleGroup/innerModule");
BOOST_CHECK_EQUAL(app.outerModuleGroup1.innerGroup.innerModuleOneUpAndHide.getVirtualQualifiedName(),
"/testApp/outerModuleGroup1");
BOOST_CHECK_EQUAL(app.outerModuleGroup1.innerGroup.innerModuleMoveToRoot.getVirtualQualifiedName(),
"/testApp/innerModuleMoveToRoot");
BOOST_CHECK_EQUAL(app.outerModuleGroup1.innerGroup.innerModuleSameNameAsGroup.getVirtualQualifiedName(),
"/testApp/outerModuleGroup1/innerModuleGroup");
}
{
TestApplication app(ctk::HierarchyModifier::hideThis);
ctk::TestFacility test;
// app.cs.dump();
BOOST_CHECK_EQUAL(app.outerModule.getVirtualQualifiedName(), "/testApp");
}
{
TestApplication app(ctk::HierarchyModifier::moveToRoot, ctk::HierarchyModifier::moveToRoot);
ctk::TestFacility test;
// app.cs.dump();
BOOST_CHECK_EQUAL(app.outerModule.getVirtualQualifiedName(), "/testApp/outerModule");
auto virtualisedApp = app.findTag(".*");
BOOST_CHECK_NO_THROW(/*[[maybe_unused]]*/ ctk::Module& outerModuleRef = virtualisedApp["outerModule"];)
BOOST_CHECK_NO_THROW(
/*[[maybe_unused]]*/ ctk::Module& innerModuleMoveToRootRef = virtualisedApp["innerModuleMoveToRoot"];)
BOOST_CHECK_EQUAL(app.outerModuleGroup1.innerGroup.getVirtualQualifiedName(), "/testApp/innerModuleGroup");
BOOST_CHECK_EQUAL(app.outerModuleGroup1.innerGroup.innerModule.getVirtualQualifiedName(),
"/testApp/innerModuleGroup/innerModule");
}
}
BOOST_AUTO_TEST_CASE(testGetNetworkNodesOnVirtualHierarchy) {
std::cout << "testGetNetworkNodesOnVirtualHierarchy" << std::endl;
TestApplication app(ctk::HierarchyModifier::none);
ctk::TestFacility test;
//app.cs.dump();
auto virtualisedApplication = app.findTag(".*");
// Need to trip away "/appName/" in the submodule() calls
size_t firstModuleOffsetInPath = ("/" + app.getName() + "/").size();
auto pathToInnerModuleOneUpAndHide =
app.outerModuleGroup1.innerGroup.innerModuleOneUpAndHide.getVirtualQualifiedName();
// Get submodule by the virtual path
ctk::Module& module = virtualisedApplication.submodule(
{pathToInnerModuleOneUpAndHide.begin() + firstModuleOffsetInPath, pathToInnerModuleOneUpAndHide.end()});
auto node2 = module("input2");
BOOST_CHECK_EQUAL(node2.getName(), "input2");
// As a reference, navigate to the module using operator []
auto node2Ref = virtualisedApplication["outerModuleGroup1"]("input2");
BOOST_CHECK(node2 == node2Ref);
// Repeat test for other modules: Module moved to root
auto pathToInnerModuleMoveToRoot = app.outerModuleGroup1.innerGroup.innerModuleMoveToRoot.getVirtualQualifiedName();
ctk::Module& innerModuleMoveToRoot = virtualisedApplication.submodule(
{pathToInnerModuleMoveToRoot.begin() + firstModuleOffsetInPath, pathToInnerModuleMoveToRoot.end()});
auto node3 = innerModuleMoveToRoot("input3");
auto node3Ref = virtualisedApplication["innerModuleMoveToRoot"]("input3");
BOOST_CHECK(node3 == node3Ref);
// Repeat test for other modules: Module with same name as its group and modifier oneLevelUp
auto pathToInnerModuleSameNameAsGroup =
app.outerModuleGroup1.innerGroup.innerModuleSameNameAsGroup.getVirtualQualifiedName();
ctk::Module& innerModuleSameNameAsGroup = virtualisedApplication.submodule(
{pathToInnerModuleSameNameAsGroup.begin() + firstModuleOffsetInPath, pathToInnerModuleSameNameAsGroup.end()});
node3 = innerModuleSameNameAsGroup("input3");
node3Ref = virtualisedApplication["outerModuleGroup1"]["innerModuleGroup"]("input3");
BOOST_CHECK(node3 == node3Ref);
}
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