Skip to content
Snippets Groups Projects
Accessor.h 6.62 KiB
/*
 * Accessor.h
 *
 *  Created on: Jun 09, 2016
 *      Author: Martin Hierholzer
 */

#ifndef CHIMERATK_ACCESSOR_H
#define CHIMERATK_ACCESSOR_H

#include <string>
#include <atomic>

#include <mtca4u/RegisterPath.h>

#include "Flags.h"
#include "Application.h"
#include "ApplicationModule.h"

namespace ChimeraTK {

  using namespace mtca4u;

  class Application;

  /*********************************************************************************************************************/

  // stupid temporary base class for accessors, which is not templated @todo TODO replace with proper class structure
  class AccessorBase {
    public:
      virtual ~AccessorBase() {}

      /** Return if the accessor is properly initialised. It is initialised if it was constructed passing the pointer
       *  to an implementation (a NDRegisterAccessor), it is not initialised if it was constructed only using the
       *  placeholder constructor without arguments. */
      virtual bool isInitialised() const = 0;

      /** Use a ProcessVariable as implementation. */
      virtual void useProcessVariable(const boost::shared_ptr<TransferElement> &var) = 0;

      /* Obtain the type info of the UserType */
      virtual const std::type_info& getValueType() const = 0;

      /** Obtain direction of the accessor */
      virtual VariableDirection getDirection() const = 0;

      /** Obtain the update mode of the accessor */
      virtual UpdateMode getUpdateMode() const = 0;

      /* Obtain the unit of the variable */
      virtual const std::string& getUnit() const = 0;

      /* Obtain the description of the variable */
      virtual const std::string& getDescription() const = 0;

      /* Obtain the name of the accessor */
      virtual const std::string& getName() 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;
      
      /** Return number of elements in the variable (array size, or 1 for scalars)
       *  @todo TODO FIXME This function is not needed like this. It is only used during the setup phase while making
       *  the connections. There we should instead just obtain the number of elements from the VariableNetworkNode.
       *  Like this, this function might easily be confused with the getNElements() function! */
      virtual size_t getNumberOfElements() const = 0;

      /** Convert into VariableNetworkNode */
      operator const VariableNetworkNode&() const {
        return node;
      }
      operator VariableNetworkNode&() {
        return node;
      }
      
      /** Return the implementation as a TransferElement */
      virtual boost::shared_ptr<TransferElement> getTransferElement() = 0;
      
      /** Explicitly return the node */
      VariableNetworkNode& getNode() {
        return node;
      }

      /** Connect with another node */
      VariableNetworkNode operator>>(VariableNetworkNode other) {
        return node.operator>>(other);
      }

    protected:

      VariableNetworkNode node;

      friend class ApplicationModule;
  };

  /*********************************************************************************************************************/

  template< typename UserType >
  class Accessor : public AccessorBase {

    public:

      Accessor(Module *owner, const std::string &name, VariableDirection direction, std::string unit, size_t nElements,
               UpdateMode mode, const std::string &description)
      : _owner(owner), _name(name), _direction(direction), _unit(unit), _mode(mode), _description(description),
        _nElements{nElements}
      {
        _owner->registerAccessor(this);
        node = VariableNetworkNode(*this);
      }

      /** The default constructor takes no arguments and leaves the accessor uninitialised. It will be dysfunctional
       *  until it is properly initialised using connectTo(). */
      Accessor()
      : _owner(nullptr), _name("dysfunctional"), _direction(VariableDirection::invalid), _unit("invalid"),
        _mode(UpdateMode::invalid), _description("dysfunctional accessor"), _nElements{0}
      {}
      
      /** The destructor unregisters the accessor from the owner module */
      ~Accessor() {
        _owner->unregisterAccessor(this);
      }

      VariableDirection getDirection() const {return _direction;}

      UpdateMode getUpdateMode() const {return _mode;}

      const std::string& getUnit() const {return _unit;}

      const std::string& getDescription() const {return _description;}

      const std::string& getName() const {return _name;}

      const std::type_info& getValueType() const {
        return typeid(UserType);
      }
      
      size_t getNumberOfElements() const { return _nElements; }

      /** Assign a new accessor to this accessor. */
      void replace(const Accessor<UserType> &newAccessor) {
        _owner = newAccessor._owner;
        _name = newAccessor._name;
        _direction = newAccessor._direction;
        _unit = newAccessor._unit;
        _mode = newAccessor._mode;
        _description = newAccessor._description;
        _nElements = newAccessor._nElements;
        impl = newAccessor.impl;
        _owner->registerAccessor(this);
        node = VariableNetworkNode(*this);
      }

      boost::shared_ptr<TransferElement> getTransferElement() override {
        return boost::static_pointer_cast<TransferElement>(impl);
      }

    protected:

      Module *_owner;
      std::string _name;
      VariableDirection _direction;
      std::string _unit;
      UpdateMode _mode;
      std::string _description;
      size_t _nElements;

      boost::shared_ptr< NDRegisterAccessor<UserType> > impl;

    private:

      /** prevent copying by operator=, since it will be confusing (operator= may also be overloaded to access the
       *  content of the buffer!) */
      const Accessor<UserType>& operator=(const Accessor<UserType>& rightHandSide) const;


  };

} /* namespace ChimeraTK */

#endif /* CHIMERATK_ACCESSOR_H */