Newer
Older
Martin Christoph Hierholzer
committed
/*
* DeviceModule.cc
*
* Created on: Jun 27, 2016
* Author: Martin Hierholzer
*/
Martin Christoph Hierholzer
committed
#include <ChimeraTK/Device.h>
Martin Christoph Hierholzer
committed
#include "Application.h"
#include "DeviceModule.h"
//#include "ControlSystemModule.h"
Martin Christoph Hierholzer
committed
namespace ChimeraTK {
/*********************************************************************************************************************/
namespace detail {
DeviceModuleProxy::DeviceModuleProxy(const DeviceModule& owner, const std::string& registerNamePrefix)
: Module(nullptr, registerNamePrefix.substr(registerNamePrefix.find_last_of("/") + 1), ""), _myowner(&owner),
_registerNamePrefix(registerNamePrefix) {}
DeviceModuleProxy::DeviceModuleProxy(DeviceModuleProxy&& other)
: Module(std::move(other)), _myowner(std::move(other._myowner)),
_registerNamePrefix(std::move(other._registerNamePrefix)) {}
VariableNetworkNode DeviceModuleProxy::operator()(
const std::string& registerName, UpdateMode mode, const std::type_info& valueType, size_t nElements) const {
return (*_myowner)(_registerNamePrefix + "/" + registerName, mode, valueType, nElements);
}
VariableNetworkNode DeviceModuleProxy::operator()(
const std::string& registerName, const std::type_info& valueType, size_t nElements, UpdateMode mode) const {
return (*_myowner)(_registerNamePrefix + "/" + registerName, valueType, nElements, mode);
}
VariableNetworkNode DeviceModuleProxy::operator()(const std::string& variableName) const {
return (*_myowner)(_registerNamePrefix + "/" + variableName);
}
Module& DeviceModuleProxy::operator[](const std::string& moduleName) const {
return (*_myowner)[_registerNamePrefix + "/" + moduleName];
}
Martin Christoph Hierholzer
committed
const Module& DeviceModuleProxy::virtualise() const {
return _myowner->virtualise().submodule(_registerNamePrefix);
}
void DeviceModuleProxy::connectTo(const Module& target, VariableNetworkNode trigger) const {
Martin Christoph Hierholzer
committed
_myowner->virtualiseFromCatalog().submodule(_registerNamePrefix).connectTo(target, trigger);
}
DeviceModuleProxy& DeviceModuleProxy::operator=(DeviceModuleProxy&& other) {
_name = std::move(other._name);
_myowner = std::move(other._myowner);
_registerNamePrefix = std::move(other._registerNamePrefix);
return *this;
}
}; // namespace detail
/*********************************************************************************************************************/
DeviceModule::DeviceModule(Application* application, const std::string& _deviceAliasOrURI)
: Module(nullptr, "<Device:" + _deviceAliasOrURI + ">", ""), deviceAliasOrURI(_deviceAliasOrURI),
Martin Christoph Hierholzer
committed
registerNamePrefix(""), owner(application) {
application->registerDeviceModule(this);
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
DeviceModule::~DeviceModule() { assert(!moduleThread.joinable()); }
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
VariableNetworkNode DeviceModule::operator()(
const std::string& registerName, UpdateMode mode, const std::type_info& valueType, size_t nElements) const {
return {registerName, deviceAliasOrURI, registerNamePrefix / registerName, mode,
{VariableDirection::invalid, false}, valueType, nElements};
}
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
Module& DeviceModule::operator[](const std::string& moduleName) const {
if(proxies.find(moduleName) == proxies.end()) {
proxies[moduleName] = {*this, moduleName};
}
/*********************************************************************************************************************/
const Module& DeviceModule::virtualise() const { return *this; }
/*********************************************************************************************************************/
void DeviceModule::connectTo(const Module& target, VariableNetworkNode trigger) const {
auto& cat = virtualiseFromCatalog();
cat.connectTo(target, trigger);
}
Martin Christoph Hierholzer
committed
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
/*********************************************************************************************************************/
VirtualModule& DeviceModule::virtualiseFromCatalog() const {
if(virtualisedModuleFromCatalog_isValid) return virtualisedModuleFromCatalog;
virtualisedModuleFromCatalog = VirtualModule(deviceAliasOrURI, "Device module", ModuleType::Device);
// obtain register catalogue
Device d;
d.open(deviceAliasOrURI); /// @todo: do not actually open the device (needs
/// extension of DeviceAccess)!
auto catalog = d.getRegisterCatalogue();
// iterate catalogue, create VariableNetworkNode for all registers starting
// with the registerNamePrefix
size_t prefixLength = registerNamePrefix.length();
for(auto& reg : catalog) {
if(std::string(reg.getRegisterName()).substr(0, prefixLength) != std::string(registerNamePrefix)) continue;
// ignore 2D registers
if(reg.getNumberOfDimensions() > 1) continue;
// guess direction and determine update mode
VariableDirection direction;
UpdateMode updateMode;
if(reg.isWriteable()) {
direction = {VariableDirection::consuming, false};
Martin Christoph Hierholzer
committed
updateMode = UpdateMode::push;
}
else {
direction = {VariableDirection::feeding, false};
if(reg.getSupportedAccessModes().has(AccessMode::wait_for_new_data)) {
updateMode = UpdateMode::push;
}
else {
updateMode = UpdateMode::poll;
}
}
Martin Christoph Hierholzer
committed
// guess type
const std::type_info* valTyp{&typeid(AnyType)};
auto& dd = reg.getDataDescriptor(); // numeric, string, boolean, nodata, undefined
if(dd.fundamentalType() == RegisterInfo::FundamentalType::numeric) {
if(dd.isIntegral()) {
if(dd.isSigned()) {
if(dd.nDigits() > 11) {
valTyp = &typeid(int64_t);
}
else if(dd.nDigits() > 6) {
valTyp = &typeid(int32_t);
}
else if(dd.nDigits() > 4) {
valTyp = &typeid(int16_t);
}
else {
valTyp = &typeid(int8_t);
}
Martin Christoph Hierholzer
committed
}
else {
if(dd.nDigits() > 10) {
valTyp = &typeid(uint64_t);
}
else if(dd.nDigits() > 5) {
valTyp = &typeid(uint32_t);
}
else if(dd.nDigits() > 3) {
valTyp = &typeid(uint16_t);
}
else {
valTyp = &typeid(uint8_t);
}
Martin Christoph Hierholzer
committed
}
}
else { // fractional
valTyp = &typeid(double);
}
}
else if(dd.fundamentalType() == RegisterInfo::FundamentalType::boolean) {
valTyp = &typeid(int32_t);
Martin Christoph Hierholzer
committed
}
else if(dd.fundamentalType() == RegisterInfo::FundamentalType::string) {
valTyp = &typeid(std::string);
}
auto name = std::string(reg.getRegisterName()).substr(prefixLength);
auto lastSlash = name.find_last_of("/");
auto dirname = name.substr(0, lastSlash);
auto basename = name.substr(lastSlash + 1);
VariableNetworkNode node(
basename, deviceAliasOrURI, reg.getRegisterName(), updateMode, direction, *valTyp, reg.getNumberOfElements());
virtualisedModuleFromCatalog.createAndGetSubmoduleRecursive(dirname).addAccessor(node);
Martin Christoph Hierholzer
committed
}
virtualisedModuleFromCatalog_isValid = true;
return virtualisedModuleFromCatalog;
}
/*********************************************************************************************************************/
void DeviceModule::reportException(std::string errMsg) {
Martin Christoph Hierholzer
committed
// In testable mode, we need to increase the testableMode_counter before releasing the testableModeLock, but we have
// to release the testableModeLock lock before obtaining the errorMutex, which in turn has to be obtained before
// writing to the errorQueue. Otherwise deadlocks might occur (with race conditions).
if(owner->isTestableModeEnabled()) {
assert(owner->testableModeTestLock());
++owner->testableMode_counter;
}
owner->testableModeUnlock("reportException");
std::unique_lock<std::mutex> lk(errorMutex);
Martin Christoph Hierholzer
committed
retry:
bool success = errorQueue.push(errMsg);
if(!success) {
// retry error reporting until the message has been sent. It has to be successful since testableMode_counter
// has been increased already
goto retry;
}
Martin Christoph Hierholzer
committed
// release errorMutex before obtaining testableModeLock to prevent deadlocks
Martin Christoph Hierholzer
committed
owner->testableModeLock("reportException");
/*********************************************************************************************************************/
void DeviceModule::handleException() {
Application::registerThread("DM_" + getName());
Device d;
std::string error;
Martin Christoph Hierholzer
committed
owner->testableModeLock("Startup");
Martin Christoph Hierholzer
committed
owner->testableModeUnlock("Wait for exception");
boost::this_thread::interruption_point();
Martin Christoph Hierholzer
committed
owner->testableModeLock("Process exception");
if(owner->isTestableModeEnabled()) --owner->testableMode_counter;
std::lock_guard<std::mutex> lk(errorMutex);
deviceError.status = 1;
deviceError.message = error;
deviceError.setCurrentVersionNumber({});
deviceError.writeAll();
while(true) {
boost::this_thread::interruption_point();
try {
d.open(deviceAliasOrURI);
if(d.isOpened()) {
break;
}
catch(std::exception& ex) {
deviceError.status = 1;
deviceError.message = ex.what();
deviceError.setCurrentVersionNumber({});
deviceError.writeAll();
}
Martin Christoph Hierholzer
committed
// empty exception reporting queue
while(errorQueue.pop()) {
if(owner->isTestableModeEnabled()) --owner->testableMode_counter;
}
owner->testableModeUnlock("Wait for recovery");
Martin Christoph Hierholzer
committed
owner->testableModeLock("Try recovery");
deviceError.status = 0;
deviceError.message = "";
deviceError.setCurrentVersionNumber({});
deviceError.writeAll();
errorCondVar.notify_all();
}
catch(...) {
// before we leave this thread, we might need to notify other waiting
// threads. boost::this_thread::interruption_point() throws an exception
// when the thread should be interrupted, so we will end up here
/*********************************************************************************************************************/
void DeviceModule::run() {
// start the module thread
assert(!moduleThread.joinable());
moduleThread = boost::thread(&DeviceModule::handleException, this);
}
/*********************************************************************************************************************/
void DeviceModule::terminate() {
if(moduleThread.joinable()) {
moduleThread.interrupt();
Martin Christoph Hierholzer
committed
errorQueue.push("terminate");
moduleThread.join();
}
assert(!moduleThread.joinable());
}
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
void DeviceModule::defineConnections() {
// replace all slashes in the deviceAliasOrURI, because URIs might contain slashes and they are not allowed in
// module names
std::string deviceAliasOrURI_withoutSlashes = deviceAliasOrURI;
size_t i = 0;
while((i = deviceAliasOrURI_withoutSlashes.find_first_of('/', i)) != std::string::npos) {
deviceAliasOrURI_withoutSlashes[i] = '_';
}
// Connect deviceError module to the control system
ControlSystemModule cs;
deviceError.connectTo(cs["Devices"][deviceAliasOrURI_withoutSlashes]);
} // namespace ChimeraTK