Skip to content
Snippets Groups Projects
Module.cc 11.5 KiB
Newer Older
// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "Application.h"
#include "ApplicationModule.h"
#include "VirtualModule.h"
  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);
  /*********************************************************************************************************************/

  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
    return *this;
  /*********************************************************************************************************************/

  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;

  /*********************************************************************************************************************/

  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;
      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;
      // poll-type accessors cannot have a readback channel
      if(accessor.getDirection().dir != VariableDirection::consuming) continue;
      accessor.getAppAccessorNoType().readLatest();
    }

  /*********************************************************************************************************************/

  void Module::readAllNonBlocking(bool includeReturnChannels) {
    auto recursiveAccessorList = getAccessorListRecursive();
    for(auto& accessor : recursiveAccessorList) {
      if(accessor.getMode() != UpdateMode::push) continue;
      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;
      // poll-type accessors cannot have a readback channel
      if(accessor.getDirection().dir != VariableDirection::consuming) continue;
      accessor.getAppAccessorNoType().readLatest();
    }
  /*********************************************************************************************************************/
  void Module::readAllLatest(bool includeReturnChannels) {
    auto recursiveAccessorList = getAccessorListRecursive();
    for(auto& accessor : recursiveAccessorList) {
      if(includeReturnChannels) {
        if(accessor.getDirection() == VariableDirection{VariableDirection::feeding, false}) continue;
      }
      else {
        if(accessor.getDirection().dir != VariableDirection::consuming) continue;
      }
      accessor.getAppAccessorNoType().readLatest();
    }

  /*********************************************************************************************************************/

  void Module::writeAll(bool includeReturnChannels) {
    auto versionNumber = getCurrentVersionNumber();
    auto recursiveAccessorList = getAccessorListRecursive();
    for(auto& accessor : recursiveAccessorList) {
      if(includeReturnChannels) {
        if(accessor.getDirection() == VariableDirection{VariableDirection::consuming, false}) continue;
      }
      else {
        if(accessor.getDirection().dir != VariableDirection::feeding) continue;
      }
      accessor.getAppAccessorNoType().write(versionNumber);
    }
  /*********************************************************************************************************************/

  void Module::writeAllDestructively(bool includeReturnChannels) {
    auto versionNumber = getCurrentVersionNumber();
    auto recursiveAccessorList = getAccessorListRecursive();
    for(auto& accessor : recursiveAccessorList) {
      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
    size_t slash = moduleName.find_first_of("/");
    // no slash found: call subscript operator
    if(slash == std::string::npos) return (*this)[std::string(moduleName)];
    // 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);
  /*********************************************************************************************************************/

  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()) {
      }

      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;
          break;
        case HierarchyModifier::oneUpAndHide:
          break;
        case HierarchyModifier::moveToRoot:
          virtualQualifiedName = "/" + currentLevelModule->getName() + virtualQualifiedName;
      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.");
        }
      }

      if(!rootReached) {
        currentLevelModule = dynamic_cast<const Module*>(currentLevelModule)->getOwner();
    if(virtualQualifiedName == "") virtualQualifiedName = "/";

  /*********************************************************************************************************************/

    if(getModuleType() == ModuleType::ApplicationModule) {
      auto* ret = dynamic_cast<ApplicationModule*>(this);
      assert(ret != nullptr);
      return ret;
    }
    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);
  }

  /*********************************************************************************************************************/