Skip to content
Snippets Groups Projects
  • Martin Christoph Hierholzer's avatar
    caefca07
    Mhier/wip hierarchy modifying group (#207) · caefca07
    Martin Christoph Hierholzer authored
    * add HierarchyModifyingGroup
    
    * VirtualModule: allow nested oneLevelUp etc.; simplify implementation
    
    This is achieved by properly setting the _owner of each VirtualModule (unless highest level).
    
    The simplification comes from then being able to use getOwner() instead of keeping track of "grandparents", and from using createAndGetSubmodule() instead of implementing something similar again in findTagAndAppendToModule().
    
    * DataLossCounter constructor now takes qualified path to trigger variable
    Mhier/wip hierarchy modifying group (#207)
    Martin Christoph Hierholzer authored
    * add HierarchyModifyingGroup
    
    * VirtualModule: allow nested oneLevelUp etc.; simplify implementation
    
    This is achieved by properly setting the _owner of each VirtualModule (unless highest level).
    
    The simplification comes from then being able to use getOwner() instead of keeping track of "grandparents", and from using createAndGetSubmodule() instead of implementing something similar again in findTagAndAppendToModule().
    
    * DataLossCounter constructor now takes qualified path to trigger variable
EntityOwner.h 11.37 KiB
/*
 * EntityOwner.h
 *
 *  Created on: Nov 15, 2016
 *      Author: Martin Hierholzer
 */

#ifndef CHIMERATK_ENTITY_OWNER_H
#define CHIMERATK_ENTITY_OWNER_H

#include <list>
#include <string>

#include "VariableNetworkNode.h"
#include "Flags.h"

namespace ChimeraTK {

  class AccessorBase;
  class Module;
  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
   * basically just implement the "Composite Pattern". The classes AccessorBase,
   * Module and Owner should have a common base class called "Component".
   */
  class EntityOwner {
   public:
    /** Constructor: Create EntityOwner by the given name with the given description. The hierarchy will be modified
     *  according to the hierarchyModifier (when VirtualModules are created e.g. in findTag()). The specified list of
     *  tags will be added to all elements directly or indirectly owned by this instance. */
    EntityOwner(const std::string& name, const std::string& description,
        HierarchyModifier hierarchyModifier = HierarchyModifier::none,
        const std::unordered_set<std::string>& tags = {});

    /** Deprecated form of the constructor. Use the new signature instead. */
    EntityOwner(const std::string& name, const std::string& description, bool eliminateHierarchy,
        const std::unordered_set<std::string>& tags = {});

    /** Default constructor just for late initialisation */
    EntityOwner()
    : _name("**INVALID**"), _description("Invalid EntityOwner created by default constructor just "
                                         "as a place holder") {}

    /** Virtual destructor to make the type polymorphic */
    virtual ~EntityOwner();

    /** Move constructor */
    EntityOwner(EntityOwner&& other) { operator=(std::move(other)); }
    EntityOwner(const EntityOwner& other) = delete;

    /** Move assignment operator */
    EntityOwner& operator=(EntityOwner&& other);
    EntityOwner& operator=(const EntityOwner& other) = delete;

    /** Get the name of the module instance */
    const std::string& getName() const { return _name; }

    /** Get the fully qualified name of the module instance, i.e. the name
     * containing all module names further up in the hierarchy. */
    virtual std::string getQualifiedName() const = 0;

    /** Get the description of the module instance */
    const std::string& getDescription() const { return _description; }

    /** Obtain the full description including the full description of the owner.
     */
    virtual std::string getFullDescription() const = 0;
    /** Obtain the list of accessors/variables directly associated with this
     * instance */
    virtual std::list<VariableNetworkNode> getAccessorList() const { return accessorList; }

    /** Obtain the list of submodules associated with this instance */
    virtual std::list<Module*> getSubmoduleList() const { return moduleList; }

    /** Obtain the list of accessors/variables associated with this instance and
     * any submodules */
    std::list<VariableNetworkNode> getAccessorListRecursive();

    /** Obtain the list of submodules associated with this instance and any
     * submodules */
    std::list<Module*> getSubmoduleListRecursive();

    /** Check whether a submodule exists by the given name (not taking into
     * account eliminated hierarchies etc.) */
    bool hasSubmodule(const std::string& name) const;

    /** Get a submodule by the given name (not taking into account eliminated
     * hierarchies etc.) */
    Module* getSubmodule(const std::string& name) const;

    /** Return a VirtualModule containing the part of the tree structure matching
     * the given tag. The resulting VirtualModule might have virtual sub-modules,
     * if this EntityOwner contains sub-EntityOwners with entities matching the
     * tag. "tag" is interpreted as a regular expression (see std::regex_match).
     */
    VirtualModule findTag(const std::string& tag) const;

    /** Return a VirtualModule containing the part of the tree structure not
     * matching the given tag. This is the negation of findTag(), this function
     * will keep those variables which findTag() would remove from the tree.
     * Again, "tag" is interpreted as a regular expression (see std::regex_match).
     */
    VirtualModule excludeTag(const std::string& tag) const;

    /** Called inside the constructor of Accessor: adds the accessor to the list
     */
    void registerAccessor(VariableNetworkNode accessor) {
      for(auto& tag : _tags) accessor.addTag(tag);
      accessorList.push_back(accessor);
    }

    /** Called inside the destructor of Accessor: removes the accessor from the
     * list */
    void unregisterAccessor(VariableNetworkNode accessor) { accessorList.remove(accessor); }

    /** Register another module as a sub-module. Will be called automatically by
     * all modules in their constructors. If addTags is set to false, the tags of
     * this EntityOwner will not be set to the module being registered. This is
     * e.g. used in the move-constructor of Module to prevent from altering the
     * tags in the move operation. */
    void registerModule(Module* module, bool addTags = true);

    /** Unregister another module as a sub-module. Will be called automatically by
     * all modules in their destructors. */
    void unregisterModule(Module* module);

    /** Add a tag to all Application-type nodes inside this group. It will recurse
     * into any subgroups. See VariableNetworkNode::addTag() for additional
     * information about tags. */
    void addTag(const std::string& tag);

    /** Note: this function is deprectated. Use the constructor parameter instead. If this is not sufficient, write a
     *  feature request for a function to set the HierarchyModifier.
     *
     * Eliminate the level of hierarchy represented by this EntityOwner. This is
     * e.g. used when building the hierarchy of VirtualModules in findTag().
     * Eliminating one level of hierarchy will make all childs of that hierarchy
     * level to appear as if there were direct childs of the next higher hierarchy
     * level. If e.g. there is a variable on the third level "A.B.C" and one
     * selects to eliminate the second level of hierarchy (e.g. calls
     *  B.eliminateHierarchy()), the structure would look like "A.C". This of
     * course only affects the "dynamic" data
     *  model, while the static C++ model is fixed at compile time. */
    void setEliminateHierarchy() { _hierarchyModifier = HierarchyModifier::hideThis; }

    /** Returns the flag whether this level of hierarchy should be eliminated. It returns true
        if hiding the hierarchy is required by the hierarchy modifier (HierarchyModifier::hideThis or HierarchyModifier::oneUpAndHide) */
    bool getEliminateHierarchy() const;

    /** Returns the hierarchy modifier of this entity. FIXE: One of those useless code comments.
     */
    HierarchyModifier getHierarchyModifier() const { return _hierarchyModifier; }

    /** Create a VirtualModule which contains all variables of this EntityOwner in
     * a flat hierarchy. It will recurse
     *  through all sub-modules and add all found variables directly to the
     * VirtualModule. */
    VirtualModule flatten();

    void accept(Visitor<EntityOwner>& visitor) const { visitor.dispatch(*this); }

    /** Print the full hierarchy to stdout. */
    void dump(const std::string& prefix = "") const;

    /** Create Graphviz dot graph and write to file. The graph will contain the
     * full hierarchy of modules and variables below (and including) this module.
     * Each variable will also show which tags are attached to it. ModuleGroups
     * will be drawn with a double line, ApplicationModules with a bold line.
     * Hierarchies which will be eliminated in the dynamic information model are
     * shown with a dotted line. */
    void dumpGraph(const std::string& fileName = "graph.dot") const;

    /** Create a Graphiz dot graph similar to the one created with dumpGraph, but
     * just show the modules and not the
     *  variables. This allows to get an overview over more complex applications.
     */
    void dumpModuleGraph(const std::string& fileName = "graph.dot") const;

    enum class ModuleType { ApplicationModule, ModuleGroup, VariableGroup, ControlSystem, Device, Invalid };

    /** Return the module type of this module, or in case of a VirtualModule the
     * module type this VirtualModule was derived from. */
    virtual ModuleType getModuleType() const = 0;

    /** Return the current version number which has been received with the last
     * push-type read operation. */
    virtual VersionNumber getCurrentVersionNumber() const = 0;

    /** Set the current version number. This function is called by the push-type
     * input accessors in their read functions. */
    virtual void setCurrentVersionNumber(VersionNumber versionNumber) = 0;

    /** Return the data validity flag. If any This function will be called by all output accessors in their write
     *  functions. */
    virtual DataValidity getDataValidity() const = 0;

    /** Set the data validity flag to fault and increment the fault counter. This function will be called by all input
     *  accessors when receiving the a faulty update if the previous update was ok. The caller of this function must
     *  ensure that calls to this function are paired to a subsequent call to decrementDataFaultCounter(). */
    virtual void incrementDataFaultCounter() = 0;

    /** Decrement the fault counter and set the data validity flag to ok if the counter has reached 0. This function
     *  will be called by all input accessors when receiving the an ok update if the previous update was faulty. The
     *  caller of this function must ensure that calles to this function are paired to a previous call to
     *  incrementDataFaultCounter(). */
    virtual void decrementDataFaultCounter() = 0;
   protected:
    /** Add the part of the tree structure matching the given tag to a
     * VirtualModule. Users normally will use findTag() instead. "tag" is
     * interpreted as a regular expression (see std::regex_match). */
    void findTagAndAppendToModule(VirtualModule& virtualParent, const std::string& tag, bool eliminateAllHierarchies,
        bool eliminateFirstHierarchy, bool negate, VirtualModule& root) const;

    /** The name of this instance */
    std::string _name;

    /** The description of this instance */
    std::string _description;

    /** List of accessors owned by this instance */
    std::list<VariableNetworkNode> accessorList;

    /** List of modules owned by this instance */
    std::list<Module*> moduleList;

    /** Hierarchy modifier flag */
    HierarchyModifier _hierarchyModifier{HierarchyModifier::none};

    /** List of tags to be added to all accessors and modules inside this module
     */
    std::unordered_set<std::string> _tags;

    /** Flag used by the testable mode to identify whether a thread within the EntityOwner has reached the point where
     *  the testable mode lock is acquired.
     *  @todo This should be moved to a more proper place in the hierarchy (e.g. ModuleImpl) after InternalModule class
     *  has been properly unified with the normal Module class. */
    std::atomic<bool> testableModeReached{false};

   public:
    /** Check whether this module has declared that it reached the testable mode. */

    bool hasReachedTestableMode();
  };

} /* namespace ChimeraTK */

#endif /* CHIMERATK_ENTITY_OWNER_H */