Skip to content
Snippets Groups Projects
TestDecoratorRegisterAccessor.h 8.64 KiB
Newer Older
/*
 * TestDecoratorRegisterAccessor.h
 *
 *  Created on: Feb 17, 2017
 *      Author: Martin Hierholzer
 */

#ifndef CHIMERATK_TEST_DECORATOR_REGISTER_ACCCESSOR
#define CHIMERATK_TEST_DECORATOR_REGISTER_ACCCESSOR

#include <mtca4u/NDRegisterAccessor.h>

  template<typename UserType>
  class TestDecoratorRegisterAccessor;

  /** Altered version of the TransferFuture since we need to deal with the lock in the wait() function. */
  template<typename UserType>
  class TestDecoratorTransferFuture : public TransferFuture {
    public:
      TestDecoratorTransferFuture() : _originalFuture{nullptr}, _accessor{nullptr} {}
        _accessor->releaseLock();
        _accessor->obtainLockAndDecrementCounter();
        _accessor->postRead();
        _accessor->hasActiveFuture = false;
      }

      void reset(PlainFutureType plainFuture, mtca4u::TransferElement *transferElement) = delete;
      void reset(TransferFuture &originalFuture, TestDecoratorRegisterAccessor<UserType> *accessor) {
        _originalFuture = &originalFuture;
        _accessor = accessor;
        TransferFuture::_theFuture = _originalFuture->getBoostFuture();
        TransferFuture::_transferElement = accessor;
      }
      // plain pointers are ok, since this class is non-copyable and always owned by the TestDecoratorRegisterAccessor
      // (which also holds a shared pointer on the actual accessor, which in turn owns the originalFuture).
      TestDecoratorRegisterAccessor<UserType> *_accessor;
  /*********************************************************getTwoDRegisterAccessor**********************************************************/
  /** Decorator of the NDRegisterAccessor which facilitates tests of the application */
  template<typename UserType>
  class TestDecoratorRegisterAccessor : public mtca4u::NDRegisterAccessor<UserType> {
    public:
      TestDecoratorRegisterAccessor(boost::shared_ptr<mtca4u::NDRegisterAccessor<UserType>> accessor)
      : mtca4u::NDRegisterAccessor<UserType>(accessor->getName(), accessor->getUnit(), accessor->getDescription()),
        // set ID to match the decorated accessor
        this->_id = _accessor->getId();
        variableId = Application::getInstance().idMap[this->_id];
        assert(variableId != 0);

        try {
          // initialise buffers
          buffer_2D.resize(_accessor->getNumberOfChannels());
          for(size_t i=0; i<_accessor->getNumberOfChannels(); ++i) buffer_2D[i] = _accessor->accessChannel(i);
          // if receiving end, register for testable mode (stall detection)
          if(isReadable()) {
            Application::getInstance().testableMode_processVars[variableId] = accessor;
      virtual ~TestDecoratorRegisterAccessor() { this->shutdown(); }
      bool write(ChimeraTK::VersionNumber versionNumber={}) override {
        if(!Application::testableModeTestLock()) {
          // may happen if first write in thread is done before first blocking read
          Application::testableModeLock("write "+this->getName());
        }
        if(!dataLost) {
          ++Application::getInstance().testableMode_counter;
          ++Application::getInstance().testableMode_perVarCounter[variableId];
          if(Application::getInstance().enableDebugTestableMode) {
            std::cout << "TestDecoratorRegisterAccessor::write[name='"<<this->getName()<<"', id="<<variableId<<"]: testableMode_counter "
                         "increased, now at value " << Application::getInstance().testableMode_counter << std::endl;
          }
        }
        else {
          if(Application::getInstance().enableDebugTestableMode) {
            std::cout << "TestDecoratorRegisterAccessor::write[name='"<<this->getName()<<"', id="<<variableId<<"]: testableMode_counter not "
                        "increased due to lost data" << std::endl;
        releaseLock();
        _accessor->doReadTransfer();
        obtainLockAndDecrementCounter();
      }
      /** Release the testableModeLock */
      void releaseLock() {
          Application::testableModeUnlock("doReadTransfer "+this->getName());
        catch(std::system_error &e) {   // ignore operation not permitted errors, since they happen the first time (lock not yet owned)
          if(e.code() != std::errc::operation_not_permitted) throw e;
      /** Obtain the testableModeLock if not owned yet, and decrement the counter. */
      void obtainLockAndDecrementCounter() {
        if(!Application::testableModeTestLock()) Application::testableModeLock("doReadTransfer "+this->getName());
        assert(Application::getInstance().testableMode_perVarCounter[variableId] > 0);
        assert(Application::getInstance().testableMode_counter > 0);
        --Application::getInstance().testableMode_counter;
        --Application::getInstance().testableMode_perVarCounter[variableId];
        if(Application::getInstance().enableDebugTestableMode) {
          std::cout << "TestDecoratorRegisterAccessor[name='"<<this->getName()<<"', id="<<variableId<<"]: testableMode_counter "
                       "decreased, now at value " << Application::getInstance().testableMode_counter << " / " << Application::getInstance().testableMode_perVarCounter[variableId] << std::endl;
        bool newData = _accessor->doReadTransferNonBlocking();
        if(!newData) return false;
        obtainLockAndDecrementCounter();
        return true;
        bool newData = _accessor->doReadTransferLatest();
        if(!newData) return false;

        // the queue has been emptied, so make sure that the testableMode_counter reflects this
        app.testableMode_counter -= app.testableMode_perVarCounter[variableId];
        app.testableMode_perVarCounter[variableId] = 0;
        if(TransferElement::hasActiveFuture) {
          return activeTestDecoratorFuture;
        }
        auto &future = _accessor->readAsync();
        TransferElement::hasActiveFuture = true;
        if(!TransferElement::hasActiveFuture) _accessor->postRead();
        for(size_t i=0; i<_accessor->getNumberOfChannels(); ++i) buffer_2D[i].swap(_accessor->accessChannel(i));
      }

      void preWrite() override {
        for(size_t i=0; i<_accessor->getNumberOfChannels(); ++i) buffer_2D[i].swap(_accessor->accessChannel(i));
        for(size_t i=0; i<_accessor->getNumberOfChannels(); ++i) buffer_2D[i].swap(_accessor->accessChannel(i));
      }

      bool isSameRegister(const boost::shared_ptr<mtca4u::TransferElement const> &other) const override {
        return _accessor->isSameRegister(other);
      }

      bool isReadOnly() const override {
        return _accessor->isReadOnly();
      }

      bool isReadable() const override {
        return _accessor->isReadable();
      }
      bool isWriteable() const override {
        return _accessor->isWriteable();
      }
      std::vector< boost::shared_ptr<mtca4u::TransferElement> > getHardwareAccessingElements() override {
        return _accessor->getHardwareAccessingElements();
      }

      void replaceTransferElement(boost::shared_ptr<mtca4u::TransferElement> newElement) override {
        _accessor->replaceTransferElement(newElement);
      }

      void setPersistentDataStorage(boost::shared_ptr<ChimeraTK::PersistentDataStorage> storage) override {
        _accessor->setPersistentDataStorage(storage);
      }

    protected:
      boost::shared_ptr<mtca4u::NDRegisterAccessor<UserType>> _accessor;
      friend class TestDecoratorTransferFuture<UserType>;
      TestDecoratorTransferFuture<UserType> activeTestDecoratorFuture;
  };

} /* namespace ChimeraTK */

#endif /* CHIMERATK_TEST_DECORATOR_REGISTER_ACCCESSOR */