Skip to content
Snippets Groups Projects
EntityOwner.h 11.40 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, VirtualModule* virtualGrandparent) 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 */