Newer
Older
Nadeem Shehzad
committed
/*!
* \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:
Nadeem Shehzad
committed
* - "state" (boolean flag if error occurred)
* - "message" (string with error message)
* These variables are automatically connected to the control systen in this
* format:
* - /Devices/{AliasName}/message
* - /Devices/{AliasName}/status
Nadeem Shehzad
committed
* 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
Nadeem Shehzad
committed
* 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.
Nadeem Shehzad
committed
Martin Christoph Hierholzer
committed
/*
* DeviceModule.h
*
* Created on: Jun 27, 2016
* Author: Martin Hierholzer
*/
#ifndef CHIMERATK_DEVICE_MODULE_H
#define CHIMERATK_DEVICE_MODULE_H
Martin Christoph Hierholzer
committed
#include "Module.h"
#include "ScalarAccessor.h"
#include "VariableGroup.h"
#include "VariableNetworkNode.h"
#include "VirtualModule.h"
#include <ChimeraTK/ForwardDeclarations.h>
#include <ChimeraTK/RegisterPath.h>
Martin Christoph Hierholzer
committed
namespace ChimeraTK {
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
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 ®isterNamePrefix = "");
DeviceModule(const std::string &deviceAliasOrURI,
const std::string ®isterNamePrefix = "");
/** Default constructor: create dysfunctional device module */
DeviceModule() {}
/** Destructor */
virtual ~DeviceModule();
/** Move operation with the move constructor */
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);
return *this;
}
/** 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 ®isterName, UpdateMode mode,
const std::type_info &valueType = typeid(AnyType),
size_t nElements = 0) const;
VariableNetworkNode operator()(const std::string ®isterName,
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 {
return operator()(variableName, UpdateMode::poll);
}
Module &operator[](const std::string &moduleName) const override;
const Module &virtualise() 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. It should be called whenever a
* ChimeraTK::runtime_error has been caught when trying to interact with this
* device. This function shall not be called by the user, all exception
* handling is done internally by ApplicationCore. */
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;
protected:
// 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};
std::string deviceAliasOrURI;
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. It can be protected, as
* it is automatically connected to the control system in
* DeviceModule::defineConnections() */
struct DeviceError : public VariableGroup {
using VariableGroup::VariableGroup;
ScalarOutput<int> status{this, "status", "", ""};
ScalarOutput<std::string> message{this, "message", "", ""};
};
DeviceError deviceError{this, "DeviceError", "Error status of the device"};
/** The thread waiting for reportException(). It runs handleException() */
boost::thread moduleThread;
/** Queue used for communication between reportException() and the
* moduleThread. */
cppext::future_queue<std::string> errorQueue{5};
/** Mutex for errorCondVar */
std::mutex errorMutex;
/** This condition variable is used to block reportException() until the error
* state has been resolved by the moduleThread. */
std::condition_variable errorCondVar;
/** This functions tries to open the device and set the deviceError. Once done
* it notifies the waiting thread(s).
* The function is running an endless loop inside its own thread
* (moduleThread). */
void handleException();
};
Martin Christoph Hierholzer
committed
} /* namespace ChimeraTK */
#endif /* CHIMERATK_DEVICE_MODULE_H */