From c056439b463e243b4e056974ba1eda2bc17423d9 Mon Sep 17 00:00:00 2001
From: Martin Hierholzer <martin.hierholzer@desy.de>
Date: Tue, 8 Nov 2016 13:51:33 +0100
Subject: [PATCH] added VariableGroup, a module which contains a set of
 variables

---
 include/Accessor.h          | 20 +++++++++++++++----
 include/ApplicationModule.h | 18 -----------------
 include/Module.h            | 24 ++++++++++++++++++++--
 include/ScalarAccessor.h    | 21 +++++++------------
 include/VariableGroup.h     | 40 +++++++++++++++++++++++++++++++++++++
 src/VariableGroup.cc        | 32 +++++++++++++++++++++++++++++
 6 files changed, 117 insertions(+), 38 deletions(-)
 create mode 100644 include/VariableGroup.h
 create mode 100644 src/VariableGroup.cc

diff --git a/include/Accessor.h b/include/Accessor.h
index 57c9e389..4da240ea 100644
--- a/include/Accessor.h
+++ b/include/Accessor.h
@@ -49,7 +49,20 @@ namespace ChimeraTK {
 
       /* Obtain the unit of the variable */
       virtual const std::string& getUnit() const = 0;
-
+      
+      /** Read an input variable. In case of an output variable, an exception will be thrown. This function will block
+       *  the calling thread until the variable has been read. If the UpdateMode::push flag has been set when creating
+       *  the accessor, this function will wait until a new value has been provided to the variable. If a new value is
+       *  already available before calling this function, the function will be non-blocking and lock-free. */
+      virtual void read() = 0;
+      
+      /** Non-blocking read. Will return whether a new value was obtained. For pull-type varibales, always true is
+       *  returned, independently whether the value was changed or not. */
+      virtual bool readNonBlocking() = 0;
+      
+      /** Write an output variable. In case of an input variable, an exception will be thrown. */
+      virtual void write() = 0;
+      
     protected:
 
       friend class ApplicationModule;
@@ -79,8 +92,7 @@ namespace ChimeraTK {
 
       /** The default accessor takes no arguments and leaves the accessor uninitialised. It will be dysfunctional
        *  until it is properly initialised using connectTo(). */
-      Accessor(ApplicationModule *owner, const std::string &name, VariableDirection direction, std::string unit,
-          UpdateMode mode)
+      Accessor(Module *owner, const std::string &name, VariableDirection direction, std::string unit, UpdateMode mode)
       : _owner(owner), _name(name), _direction(direction), _unit(unit), _mode(mode), node{*this}
       {
         owner->registerAccessor(this);
@@ -114,7 +126,7 @@ namespace ChimeraTK {
 
     protected:
 
-      ApplicationModule *_owner;
+      Module *_owner;
       std::string _name;
       VariableDirection _direction;
       std::string _unit;
diff --git a/include/ApplicationModule.h b/include/ApplicationModule.h
index 1b159b4e..14c577d0 100644
--- a/include/ApplicationModule.h
+++ b/include/ApplicationModule.h
@@ -16,11 +16,6 @@
 
 namespace ChimeraTK {
 
-  template< typename UserType >
-  class Accessor;
-
-  class AccessorBase;
-
   class ApplicationModule : public Module {
 
     public:
@@ -37,22 +32,9 @@ namespace ChimeraTK {
 
     protected:
 
-      template< typename UserType >
-      friend class Accessor;
-
-      friend class AccessorBase;
-
-      /** Called inside the constructor of Accessor: adds the accessor to the list */
-      void registerAccessor(AccessorBase* accessor) {
-        accessorList.push_back(accessor);
-      }
-
       /** The thread executing mainLoop() */
       boost::thread moduleThread;
 
-      /** List of accessors owned by this module */
-      std::list<AccessorBase*> accessorList;
-
   };
 
 } /* namespace ChimeraTK */
diff --git a/include/Module.h b/include/Module.h
index 55b9507d..a1f0ad39 100644
--- a/include/Module.h
+++ b/include/Module.h
@@ -9,7 +9,12 @@
 #define CHIMERATK_MODULE_H
 
 namespace ChimeraTK {
-
+  
+  template< typename UserType >
+  class Accessor;
+  
+  class AccessorBase;
+  
   /** Base class for ApplicationModule, DeviceModule and ControlSystemModule, to have a common interface for these
    *  module types. */
   class Module {
@@ -30,7 +35,22 @@ namespace ChimeraTK {
 
       /** Terminate the module. Must be called before destruction, if run() was called previously. */
       virtual void terminate() {};
-
+      
+  protected:
+      
+      template< typename UserType >
+      friend class Accessor;
+      
+      friend class AccessorBase;
+      
+      /** Called inside the constructor of Accessor: adds the accessor to the list */
+      void registerAccessor(AccessorBase* accessor) {
+        accessorList.push_back(accessor);
+      }
+      
+      /** List of accessors owned by this module */
+      std::list<AccessorBase*> accessorList;
+      
   };
 
 } /* namespace ChimeraTK */
diff --git a/include/ScalarAccessor.h b/include/ScalarAccessor.h
index f58d7dd7..95a44d1b 100644
--- a/include/ScalarAccessor.h
+++ b/include/ScalarAccessor.h
@@ -16,7 +16,7 @@
 #include "Accessor.h"
 
 /** Macros to declare a scalar variable/accessor more easily. The call to this macro must be placed inside the
- *  class definiton of an ApplicationModule.
+ *  class definiton of a Module (e.g. ApplicationModule or VariableGroup).
  *
  *  UserType is the data type of the variable.
  *  name will be the C++ symbol name of the variable accessor. It will be of the type ChimeraTK::ScalarAccessor<UserType>
@@ -36,15 +36,11 @@ namespace ChimeraTK {
   template< typename UserType >
   class ScalarAccessor : public Accessor<UserType> {
     public:
-      ScalarAccessor(ApplicationModule *owner, const std::string &name, VariableDirection direction, std::string unit,
+      ScalarAccessor(Module *owner, const std::string &name, VariableDirection direction, std::string unit,
           UpdateMode mode)
       : Accessor<UserType>(owner, name, direction, unit, mode)
       {}
 
-      /** Read an input variable. In case of an output variable, an exception will be thrown. This function will block
-       *  the calling thread until the variable has been read. If the UpdateMode::push flag has been set when creating
-       *  the accessor, this function will wait until a new value has been provided to the variable. If a new value is
-       *  already available before calling this function, the function will be non-blocking and lock-free. */
       void read() {
         if(Accessor<UserType>::_mode == UpdateMode::push) {
           while(impl->readNonBlocking() == false) { /// @todo TODO proper blocking implementation
@@ -59,18 +55,15 @@ namespace ChimeraTK {
         }
       } // LCOV_EXCL_LINE this line somehow ends up having a negative counter in the coverage report, which leads to a failure
 
-      /** Check if an input variable has new data. In case of an output variable, an exception will be thrown. If the
-       *  wait_for_new_data access mode flag was not provided when creating the accessor, this function will return
-       *  always false. */
-      //bool hasNewData();  /// @todo TODO right now impossible to implement...
-
-      /** Write an output variable. In case of an input variable, an exception will be thrown. This function never
-       *  blocks and is always implemented in a lock-free manner. */
       void write() {
         impl->write();
         boost::this_thread::interruption_point();
       }
-
+      
+      bool readNonBlocking() {
+        return impl->readNonBlocking();
+      }
+        
       /** Implicit type conversion to user type T to access the first element (often the only element).
        *  This covers already a lot of operations like arithmetics and comparison */
       operator UserType() {
diff --git a/include/VariableGroup.h b/include/VariableGroup.h
new file mode 100644
index 00000000..bed45582
--- /dev/null
+++ b/include/VariableGroup.h
@@ -0,0 +1,40 @@
+/*
+ * VariableGroup.h
+ *
+ *  Created on: Nov 8, 2016
+ *      Author: Martin Hierholzer
+ */
+
+#ifndef CHIMERATK_VARIABLE_GROUP_H
+#define CHIMERATK_VARIABLE_GROUP_H
+
+#include <list>
+
+#include <boost/thread.hpp>
+
+#include "Module.h"
+
+namespace ChimeraTK {
+
+  class VariableGroup : public Module {
+
+    public:
+
+      /** Destructor */
+      virtual ~VariableGroup();
+      
+      /** Wait for receiving an update for any of the push-type variables in the group. Any poll-type variables are
+       *  read after receiving the update. If no push-type variables are in the group, this function will just read
+       *  all variables. */
+      void readAny();
+
+    protected:
+
+      /** The thread executing mainLoop() */
+      boost::thread moduleThread;
+
+  };
+
+} /* namespace ChimeraTK */
+
+#endif /* CHIMERATK_VARIABLE_GROUP_H */
diff --git a/src/VariableGroup.cc b/src/VariableGroup.cc
new file mode 100644
index 00000000..3758cdca
--- /dev/null
+++ b/src/VariableGroup.cc
@@ -0,0 +1,32 @@
+/*
+ * VariableGroup.cc
+ *
+ *  Created on: Jun 27, 2016
+ *      Author: Martin Hierholzer
+ */
+
+#include "VariableGroup.h"
+#include "Accessor.h"
+
+namespace ChimeraTK {
+  
+  void VariableGroup::readAny() {
+    bool gotUpdate = false;
+    while(!gotUpdate) {     /// @todo TODO FIXME make proper blocking implementation
+      boost::this_thread::yield();
+      boost::this_thread::interruption_point();
+      for(auto accessor : accessorList) {
+        if(accessor->getUpdateMode() == UpdateMode::poll) {
+          if(accessor->readNonBlocking()) gotUpdate = true;
+        }
+      }
+    }
+    for(auto accessor : accessorList) {
+      if(accessor->getUpdateMode() == UpdateMode::poll) {
+        accessor->read();
+      }
+    }
+  }
+  
+} /* namespace ChimeraTK */
+
-- 
GitLab