Skip to content
Snippets Groups Projects
DeviceModule.h 6.05 KiB
Newer Older
/*!
 * \author Nadeem Shehzad (DESY)
 * \date 21.02.2019
 * \page excpetiondoc Exception Handling
 * \section Introduction
 *
 * To handle expection, the current simple implementation includes two error state variables:
 *  - "state" (boolean flag if error occurred)
 *  - "message" (string with error message)
 * 
 * These variables are placed together in a VariableGroup called "DeviceError". 
 * They are connected to the contorlsystem in this format 
 *  - /Devices.{AliasName}/DeviceError/message 
 *  - /Devices.{AliasName}/DeviceError/status.
 * 
 * In this implementation a user/application can report an exception
 * by calling reportException of DeviceModule with an exception string.
 * The reportException packs the exception in a queue and the blocks the thread.
 * This queue is processed by an internal function handleException which
 * updates the DeviceError variables (status=1 and message= YourExceptionString)
 * and tries to open the device. Once device can be opened the DeviceError 
 * variables are updated (status=0 and message="") and blocking threads
 * are notified to continue. It must be noted that whatever operation which
 * lead to exception e.g., read or write, should be repeated after the exception
 * is handled.
 * 
 * Checkout testExceptionTest.cc under tests/executables_src to see how it works.
*/

/*
 * DeviceModule.h
 *
 *  Created on: Jun 27, 2016
 *      Author: Martin Hierholzer
 */

#ifndef CHIMERATK_DEVICE_MODULE_H
#define CHIMERATK_DEVICE_MODULE_H

#include <ChimeraTK/ForwardDeclarations.h>
#include <ChimeraTK/RegisterPath.h>
#include "VariableNetworkNode.h"
#include "Module.h"
#include "VariableGroup.h"
#include "ScalarAccessor.h"
#include "ControlSystemModule.h"
class Application;
class DeviceModule : public Module {
    public:

      /** Constructor: The device represented by this DeviceModule is identified by either the device alias found
       *  in the DMAP file or directly an URI. The given optional prefix will be prepended to all register names
       *  (separated by a slash). */
      
      DeviceModule(Application *application, const std::string& deviceAliasOrURI, const std::string& registerNamePrefix="");

      DeviceModule(const std::string& deviceAliasOrURI, const std::string& registerNamePrefix="");

      /** Default constructor: create dysfunctional device module */
      DeviceModule() {}
      
      /** Destructor */
      virtual ~DeviceModule();
      
      
      DeviceModule(DeviceModule &&other) { operator=(std::move(other)); }

      /** Move assignment */
      DeviceModule& operator=(DeviceModule &&other) {
        Module::operator=(std::move(other));
        deviceAliasOrURI = std::move(other.deviceAliasOrURI);
        registerNamePrefix = std::move(other.registerNamePrefix);
        subModules = std::move(other.subModules);
        deviceError = std::move(other.deviceError);
      /** The subscript operator returns a VariableNetworkNode which can be used in the Application::initialise()
       *  function to connect the register with another variable. */
      VariableNetworkNode operator()(const std::string& registerName, UpdateMode mode,
          const std::type_info &valueType=typeid(AnyType), size_t nElements=0) const;
      VariableNetworkNode operator()(const std::string& registerName, const std::type_info &valueType,
          size_t nElements=0, UpdateMode mode=UpdateMode::poll) const {
        return operator()(registerName, mode, valueType, nElements);
      VariableNetworkNode operator()(const std::string& variableName) const override {
      Module& operator[](const std::string& moduleName) const override;
      void connectTo(const Module &target, VariableNetworkNode trigger={}) const override;
      ModuleType getModuleType() const override { return ModuleType::Device; }
      /** Use this function to report an exception*/
      void reportException(std::string errMsg);
      
      void run() override;
      
      void terminate() override;
      
      VersionNumber getCurrentVersionNumber() const override { return currentVersionNumber; }
      void setCurrentVersionNumber(VersionNumber versionNumber) override {
        if(versionNumber > currentVersionNumber) currentVersionNumber = versionNumber;
      }

      VersionNumber currentVersionNumber;
      /** This function connects DeviceError VariableGroup to ContolSystem*/
      void defineConnections() override;
      // populate virtualisedModuleFromCatalog based on the information in the device's catalogue
      VirtualModule& virtualiseFromCatalog() const;
      mutable VirtualModule virtualisedModuleFromCatalog{"INVALID", "", ModuleType::Invalid};
      mutable bool virtualisedModuleFromCatalog_isValid{false};

      ChimeraTK::RegisterPath registerNamePrefix;
      // List of sub modules accessed through the operator[]. This is mutable since it is little more than a cache and
      // thus does not change the logical state of this module
      mutable std::map<std::string, DeviceModule> subModules;
    /** A  VariableGroup for exception status and message*/ 
    public:
      struct DeviceError : public VariableGroup {
        using VariableGroup::VariableGroup;
        ScalarOutput<int> status{this,"status","",""}; 
        ScalarOutput<std::string> message{this,"message","",""}; 
      DeviceError deviceError{this, "deviceError", "Error"}; 
    private:
      /** The thread executing reportException() */
      boost::thread moduleThread;
      cppext::future_queue<std::string> errorQueue{5};
      std::mutex errorMutex;
      std::condition_variable errorCondVar;
Nadeem Shehzad's avatar
Nadeem Shehzad committed
      /** This functions tries to open the device and set the deviceError.
       * Once done it notifies the waiting thread(s) */
      void handleException();
  };

} /* namespace ChimeraTK */

#endif /* CHIMERATK_DEVICE_MODULE_H */