From 63483a4db8b7d1df2a29d6f3065ee0d701f3130d Mon Sep 17 00:00:00 2001
From: Martin Hierholzer <martin.hierholzer@desy.de>
Date: Thu, 21 Sep 2017 08:47:56 +0200
Subject: [PATCH] improved generation of dot graph

---
 include/Application.h         |  2 ++
 include/ApplicationModule.h   |  4 +++-
 include/ControlSystemModule.h |  2 ++
 include/DeviceModule.h        |  2 ++
 include/EntityOwner.h         | 15 ++++++++++++--
 include/ModuleGroup.h         | 18 +++++++++++++++++
 include/VariableGroup.h       |  2 ++
 include/VirtualModule.h       |  7 ++++++-
 src/EntityOwner.cc            | 37 ++++++++++++++++++++++++-----------
 9 files changed, 74 insertions(+), 15 deletions(-)

diff --git a/include/Application.h b/include/Application.h
index 94d40ab8..6503d497 100644
--- a/include/Application.h
+++ b/include/Application.h
@@ -139,6 +139,8 @@ namespace ChimeraTK {
         Profiler::registerThread(name);
       }
 
+      ModuleType getModuleType() const override { return ModuleType::ModuleGroup; }
+
     protected:
 
       friend class Module;
diff --git a/include/ApplicationModule.h b/include/ApplicationModule.h
index 8eb56f3b..19a914a3 100644
--- a/include/ApplicationModule.h
+++ b/include/ApplicationModule.h
@@ -63,7 +63,9 @@ namespace ChimeraTK {
         moduleList = std::move(rhs.moduleList);
         return *this;
       }
-    
+
+      ModuleType getModuleType() const override { return ModuleType::ApplicationModule; }
+
     protected:
 
       /** Wrapper around mainLoop(), to execute additional tasks in the thread before entering the main loop */
diff --git a/include/ControlSystemModule.h b/include/ControlSystemModule.h
index ab0012a9..417b7677 100644
--- a/include/ControlSystemModule.h
+++ b/include/ControlSystemModule.h
@@ -35,6 +35,8 @@ namespace ChimeraTK {
 
       Module& operator[](const std::string& moduleName) const override;
 
+      ModuleType getModuleType() const override { return ModuleType::ControlSystem; }
+
     protected:
 
       mtca4u::RegisterPath variableNamePrefix;
diff --git a/include/DeviceModule.h b/include/DeviceModule.h
index a4a95cb6..e40eaa71 100644
--- a/include/DeviceModule.h
+++ b/include/DeviceModule.h
@@ -42,6 +42,8 @@ namespace ChimeraTK {
 
       Module& operator[](const std::string& moduleName) const override;
 
+      ModuleType getModuleType() const override { return ModuleType::Device; }
+
     protected:
 
       std::string deviceAliasOrURI;
diff --git a/include/EntityOwner.h b/include/EntityOwner.h
index 7bee80dc..adf3e4d4 100644
--- a/include/EntityOwner.h
+++ b/include/EntityOwner.h
@@ -113,10 +113,21 @@ namespace ChimeraTK {
       /** Create Graphviz dot graph write to file */
       void dumpGraph(const std::string &fileName="graph.dot") const;
       
-      /** Create Graphviz dot graph write to stream, excluding the surrounding digraph command */
-      void dumpGraphInternal(std::ostream &stream) const;
+      enum class ModuleType {
+        ApplicationModule, ModuleGroup, VariableGroup, ControlSystem, Device
+      };
+      
+      /** 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;
 
   protected:
+      
+      /** Create Graphviz dot graph write to stream, excluding the surrounding digraph command */
+      void dumpGraphInternal(std::ostream &stream) const;
+      
+      /** Clean a fully qualified entity name so it can be used as a dot node name (i.e. strip slashes etc.) */
+      std::string cleanDotNode(std::string fullName) const;
     
       /** The name of this instance */
       std::string _name;
diff --git a/include/ModuleGroup.h b/include/ModuleGroup.h
index f4a0cdf6..c37582bf 100644
--- a/include/ModuleGroup.h
+++ b/include/ModuleGroup.h
@@ -40,17 +40,35 @@ namespace ChimeraTK {
       /** Destructor */
       virtual ~ModuleGroup() {};
       
+      /** Move operation with the move constructor
+          @todo should be in the base class!? */
+      ModuleGroup(ModuleGroup &&rhs) {
+        _name = std::move(rhs._name);
+        _owner = std::move(rhs._owner);
+        _description = std::move(rhs._description);
+        accessorList = std::move(rhs.accessorList);
+        moduleList = std::move(rhs.moduleList);
+        _eliminateHierarchy = rhs._eliminateHierarchy;
+        _tags = std::move(rhs._tags);
+      }
+
       /** Move operation with the assignment operator
           @todo should be in the base class!? */
       ModuleGroup& operator=(ModuleGroup &&rhs) {
         _name = std::move(rhs._name);
         _owner = std::move(rhs._owner);
+        _description = std::move(rhs._description);
         accessorList = std::move(rhs.accessorList);
         moduleList = std::move(rhs.moduleList);
+        _eliminateHierarchy = rhs._eliminateHierarchy;
+        _tags = std::move(rhs._tags);
         return *this;
       }
       
       ModuleGroup& operator=(ModuleGroup &rhs) = delete;
+      ModuleGroup& operator=(const ModuleGroup &rhs) = delete;
+
+      ModuleType getModuleType() const override { return ModuleType::ModuleGroup; }
 
   };
 
diff --git a/include/VariableGroup.h b/include/VariableGroup.h
index f6bdb9d8..381df7dd 100644
--- a/include/VariableGroup.h
+++ b/include/VariableGroup.h
@@ -54,6 +54,8 @@ namespace ChimeraTK {
       
       VariableGroup& operator=(VariableGroup &rhs) = delete;
 
+      ModuleType getModuleType() const override { return ModuleType::VariableGroup; }
+
   };
 
 } /* namespace ChimeraTK */
diff --git a/include/VirtualModule.h b/include/VirtualModule.h
index 5068a8a2..0d12ff36 100644
--- a/include/VirtualModule.h
+++ b/include/VirtualModule.h
@@ -22,7 +22,9 @@ namespace ChimeraTK {
     public:
 
       /** Constructor */
-      VirtualModule(const std::string &name, const std::string &description) : Module(nullptr, name, description) {}
+      VirtualModule(const std::string &name, const std::string &description, ModuleType moduleType)
+      : Module(nullptr, name, description), _moduleType(moduleType)
+      {}
 
       /** Copy constructor */
       VirtualModule(const VirtualModule &other);
@@ -36,10 +38,13 @@ namespace ChimeraTK {
       
       /** Add a virtual sub-module. The module instance will be added to an internal list. */
       void addSubModule(VirtualModule module);
+
+      ModuleType getModuleType() const override { return _moduleType; }
       
     protected:
     
       std::list<VirtualModule> submodules;
+      ModuleType _moduleType;
 
   };
 
diff --git a/src/EntityOwner.cc b/src/EntityOwner.cc
index 2dc70103..ad90e90e 100644
--- a/src/EntityOwner.cc
+++ b/src/EntityOwner.cc
@@ -80,7 +80,7 @@ namespace ChimeraTK {
   VirtualModule EntityOwner::findTag(const std::string &tag, bool eliminateAllHierarchies) const {
 
     // create new module to return
-    VirtualModule module{_name, _description};
+    VirtualModule module{_name, _description, getModuleType()};
     
     // add everything matching the tag to the virtual module and return it
     findTagAndAppendToModule(module, tag, eliminateAllHierarchies, true);
@@ -92,7 +92,7 @@ namespace ChimeraTK {
   void EntityOwner::findTagAndAppendToModule(VirtualModule &module, const std::string &tag, bool eliminateAllHierarchies,
                                              bool eliminateFirstHierarchy) const {
     
-    VirtualModule nextmodule{_name, _description};
+    VirtualModule nextmodule{_name, _description, getModuleType()};
     VirtualModule *moduleToAddTo;
     
     bool needToAddSubModule = false;
@@ -152,20 +152,36 @@ namespace ChimeraTK {
     dumpGraphInternal(file);
     file << "}" << std::endl;
     file.close();
-    std::cout << "HIER " << fileName << std::endl;
+  }
+
+  /*********************************************************************************************************************/
+
+  std::string EntityOwner::cleanDotNode(std::string fullName) const {
+    std::replace(fullName.begin(), fullName.end(), '/', '_');
+    std::replace(fullName.begin(), fullName.end(), ':', '_');
+    return fullName;
   }
 
   /*********************************************************************************************************************/
 
   void EntityOwner::dumpGraphInternal(std::ostream &stream) const {
     
-    std::string myDotNode = getQualifiedName();
-    std::replace(myDotNode.begin(), myDotNode.end(), '/', '_');
-    stream << myDotNode << "[label=\"" << getName() << "\"]" << std::endl;
+    std::string myDotNode = cleanDotNode(getQualifiedName());
+    
+    stream << myDotNode << "[label=\"" << getName() << "\"";
+    if(_eliminateHierarchy) {
+      stream << ",style=dotted";
+    }
+    if(getModuleType() == ModuleType::ModuleGroup) {
+      stream << ",peripheries=2";
+    }
+    if(getModuleType() == ModuleType::ApplicationModule) {
+      stream << ",style=bold";
+    }
+    stream << "]" << std::endl;
     
     for(auto &node : getAccessorList()) {
-      std::string dotNode = node.getQualifiedName();
-      std::replace(dotNode.begin(), dotNode.end(), '/', '_');
+      std::string dotNode = cleanDotNode(node.getQualifiedName());
       stream << dotNode << "[label=\"{" << node.getName() << "| {";
       bool first = true;
       for(auto tag : node.getTags()) {
@@ -182,8 +198,7 @@ namespace ChimeraTK {
     }
 
     for(auto &submodule : getSubmoduleList()) {
-      std::string dotNode = submodule->getQualifiedName();
-      std::replace(dotNode.begin(), dotNode.end(), '/', '_');
+      std::string dotNode = cleanDotNode(submodule->getQualifiedName());
       stream << "  " << myDotNode << " -> " << dotNode << std::endl;
       submodule->dumpGraphInternal(stream);
     }
@@ -201,7 +216,7 @@ namespace ChimeraTK {
   /*********************************************************************************************************************/
 
   VirtualModule EntityOwner::flatten() {
-    VirtualModule nextmodule{_name, _description};
+    VirtualModule nextmodule{_name, _description, getModuleType()};
     for(auto &node : getAccessorListRecursive()) {
       nextmodule.registerAccessor(node);
     }
-- 
GitLab