diff --git a/include/Accessor.h b/include/Accessor.h index 57c9e3892455e176c5c0982044814681bc4d51a1..4da240ea5fd28ea95cd354745dcffe049c383304 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 1b159b4e000b90b3e106936ac8c6f758e5bd4710..14c577d09ad8ffd26217f2628ec654b98bcca7a8 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 55b9507d2dddf283f070b87892b7f972e61932e2..a1f0ad39da381fdb7f3e5a727551a1173a9a40ce 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 f58d7dd7c744103bf61c943787d17adca529d841..95a44d1b94251533fda6ee76012de171f927b953 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 0000000000000000000000000000000000000000..bed4558270a1019a3c42c0abd114ba1c1fd1cedf --- /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 0000000000000000000000000000000000000000..3758cdcaba2ec6677d65cbb8511fe501471c529c --- /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 */ +