Newer
Older
// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
// SPDX-License-Identifier: LGPL-3.0-or-later
Martin Christoph Hierholzer
committed
#include "Module.h"
#include "ApplicationModule.h"
Martin Christoph Hierholzer
committed
#include "DeviceModule.h"
Martin Christoph Hierholzer
committed
namespace ChimeraTK {
Martin Christoph Hierholzer
committed
Module::Module(EntityOwner* owner, const std::string& name, const std::string& description,
HierarchyModifier hierarchyModifier, const std::unordered_set<std::string>& tags)
: EntityOwner(name, description, hierarchyModifier, tags), _owner(owner) {
if(_owner != nullptr) _owner->registerModule(this);
}
/*********************************************************************************************************************/
Module::Module(EntityOwner* owner, const std::string& name, const std::string& description, bool eliminateHierarchy,
const std::unordered_set<std::string>& tags)
: EntityOwner(name, description, eliminateHierarchy, tags), _owner(owner) {
if(_owner != nullptr) _owner->registerModule(this);
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
Module::~Module() {
if(_owner != nullptr) _owner->unregisterModule(this);
}
/*********************************************************************************************************************/
Module& Module::operator=(Module&& other) {
EntityOwner::operator=(std::move(other));
_owner = other._owner;
if(_owner != nullptr) _owner->registerModule(this, false);
// note: the other module unregisters itself in its destructor - which will be called next after any move operation
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
void Module::run() {
testableModeReached = true; // Modules which don't implement run() have now reached testable mode
}
/*********************************************************************************************************************/
ChimeraTK::ReadAnyGroup Module::readAnyGroup() {
auto recursiveAccessorList = getAccessorListRecursive();
// put push-type transfer elements into a ReadAnyGroup
ChimeraTK::ReadAnyGroup group;
for(auto& accessor : recursiveAccessorList) {
if(accessor.getDirection() == VariableDirection{VariableDirection::feeding, false}) continue;
group.add(accessor.getAppAccessorNoType());
}
group.finalise();
return group;
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
void Module::readAll(bool includeReturnChannels) {
auto recursiveAccessorList = getAccessorListRecursive();
// first blockingly read all push-type variables
for(auto& accessor : recursiveAccessorList) {
if(accessor.getMode() != UpdateMode::push) continue;
Martin Christoph Hierholzer
committed
if(includeReturnChannels) {
if(accessor.getDirection() == VariableDirection{VariableDirection::feeding, false}) continue;
}
else {
if(accessor.getDirection().dir != VariableDirection::consuming) continue;
}
accessor.getAppAccessorNoType().read();
}
// next non-blockingly read the latest values of all poll-type variables
for(auto& accessor : recursiveAccessorList) {
if(accessor.getMode() == UpdateMode::push) continue;
Martin Christoph Hierholzer
committed
// poll-type accessors cannot have a readback channel
if(accessor.getDirection().dir != VariableDirection::consuming) continue;
accessor.getAppAccessorNoType().readLatest();
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
void Module::readAllNonBlocking(bool includeReturnChannels) {
auto recursiveAccessorList = getAccessorListRecursive();
for(auto& accessor : recursiveAccessorList) {
if(accessor.getMode() != UpdateMode::push) continue;
Martin Christoph Hierholzer
committed
if(includeReturnChannels) {
if(accessor.getDirection() == VariableDirection{VariableDirection::feeding, false}) continue;
}
else {
if(accessor.getDirection().dir != VariableDirection::consuming) continue;
}
accessor.getAppAccessorNoType().readNonBlocking();
}
for(auto& accessor : recursiveAccessorList) {
if(accessor.getMode() == UpdateMode::push) continue;
Martin Christoph Hierholzer
committed
// poll-type accessors cannot have a readback channel
if(accessor.getDirection().dir != VariableDirection::consuming) continue;
accessor.getAppAccessorNoType().readLatest();
}
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
void Module::readAllLatest(bool includeReturnChannels) {
auto recursiveAccessorList = getAccessorListRecursive();
for(auto& accessor : recursiveAccessorList) {
Martin Christoph Hierholzer
committed
if(includeReturnChannels) {
if(accessor.getDirection() == VariableDirection{VariableDirection::feeding, false}) continue;
}
else {
if(accessor.getDirection().dir != VariableDirection::consuming) continue;
}
accessor.getAppAccessorNoType().readLatest();
}
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
void Module::writeAll(bool includeReturnChannels) {
auto versionNumber = getCurrentVersionNumber();
auto recursiveAccessorList = getAccessorListRecursive();
for(auto& accessor : recursiveAccessorList) {
Martin Christoph Hierholzer
committed
if(includeReturnChannels) {
if(accessor.getDirection() == VariableDirection{VariableDirection::consuming, false}) continue;
}
else {
if(accessor.getDirection().dir != VariableDirection::feeding) continue;
}
accessor.getAppAccessorNoType().write(versionNumber);
}
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
void Module::writeAllDestructively(bool includeReturnChannels) {
auto versionNumber = getCurrentVersionNumber();
auto recursiveAccessorList = getAccessorListRecursive();
for(auto& accessor : recursiveAccessorList) {
Martin Christoph Hierholzer
committed
if(includeReturnChannels) {
if(accessor.getDirection() == VariableDirection{VariableDirection::consuming, false}) continue;
}
else {
if(accessor.getDirection().dir != VariableDirection::feeding) continue;
}
accessor.getAppAccessorNoType().writeDestructively(versionNumber);
}
}
/*********************************************************************************************************************/
const Module& Module::submodule(std::string_view moduleName) const {
// strip leading slash if present
while(moduleName[0] == '/') moduleName.remove_prefix(1);
// an empty string or a "/" refers to the module itself
if(moduleName.size() == 0) return *this;
// search for first slash
Martin Christoph Hierholzer
committed
size_t slash = moduleName.find_first_of("/");
// no slash found: call subscript operator
if(slash == std::string::npos) return (*this)[std::string(moduleName)];
Martin Christoph Hierholzer
committed
// slash found: split module name at slash
auto upperModuleName = moduleName.substr(0, slash);
auto remainingModuleNames = moduleName.substr(slash + 1);
return (*this)[std::string(upperModuleName)].submodule(remainingModuleNames);
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
Module& Module::submodule(std::string_view moduleName) {
// According to Scott Meyers "Effective C++", the const cast is fine here
return const_cast<Module&>(static_cast<const Module&>(*this).submodule(moduleName));
}
/*********************************************************************************************************************/
std::string Module::getVirtualQualifiedName() const {
std::string virtualQualifiedName{""};
const EntityOwner* currentLevelModule{this};
bool rootReached{false};
do {
if(currentLevelModule == &Application::getInstance()) {
Martin Christoph Hierholzer
committed
break;
}
auto currentLevelModifier = currentLevelModule->getHierarchyModifier();
bool skipNextLevel{false};
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;
skipNextLevel = true;
break;
case HierarchyModifier::oneUpAndHide:
skipNextLevel = true;
break;
case HierarchyModifier::moveToRoot:
Martin Christoph Hierholzer
committed
virtualQualifiedName = "/" + currentLevelModule->getName() + virtualQualifiedName;
rootReached = true;
Martin Christoph Hierholzer
committed
break;
}
if(skipNextLevel) {
auto lastLevelModule = currentLevelModule;
currentLevelModule = dynamic_cast<const Module*>(currentLevelModule)->getOwner();
if(currentLevelModule == &Application::getInstance()) {
throw logic_error(std::string("Module ") + lastLevelModule->getName() +
": cannot have hierarchy modifier 'oneLevelUp' or oneUpAndHide in root of the application.");
}
}
currentLevelModule = dynamic_cast<const Module*>(currentLevelModule)->getOwner();
} while(!rootReached);
Martin Christoph Hierholzer
committed
if(virtualQualifiedName == "") virtualQualifiedName = "/";
return virtualQualifiedName;
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
Module* Module::findApplicationModule() {
if(getModuleType() == ModuleType::ApplicationModule) {
auto* ret = dynamic_cast<ApplicationModule*>(this);
assert(ret != nullptr);
return ret;
}
Martin Christoph Hierholzer
committed
else if(getModuleType() == ModuleType::Device) {
auto* ret = dynamic_cast<DeviceModule*>(this);
assert(ret != nullptr);
return ret;
}
else if(getModuleType() == ModuleType::VariableGroup) {
auto* owningModule = dynamic_cast<Module*>(getOwner());
assert(owningModule != nullptr);
return owningModule->findApplicationModule();
}
else {
throw ChimeraTK::logic_error(
"EntityOwner::findApplicationModule() called on neither an ApplicationModule nor a VariableGroup.");
}
}
/*********************************************************************************************************************/
std::string Module::getQualifiedName() const {
return ((_owner != nullptr) ? _owner->getQualifiedName() : "") + "/" + _name;
}
/*********************************************************************************************************************/
std::string Module::getFullDescription() const {
if(_owner == nullptr) return _description;
auto ownerDescription = _owner->getFullDescription();
if(ownerDescription == "") return _description;
if(_description == "") return ownerDescription;
return ownerDescription + " - " + _description;
}
/*********************************************************************************************************************/
std::list<EntityOwner*> Module::getInputModulesRecursively(std::list<EntityOwner*> startList) {
return _owner->getInputModulesRecursively(startList);
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
} /* namespace ChimeraTK */