Newer
Older
Martin Christoph Hierholzer
committed
/*
* DeviceModule.cc
*
* Created on: Jun 27, 2016
* Author: Martin Hierholzer
*/
#include <ChimeraTK/DeviceBackend.h>
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 {
DeviceModule::DeviceModule(const std::string& _deviceAliasOrURI, const std::string& _registerNamePrefix)
Martin Christoph Hierholzer
committed
: Module(nullptr,
_registerNamePrefix.empty() ? "<Device:" + _deviceAliasOrURI + ">" :
_registerNamePrefix.substr(_registerNamePrefix.find_last_of("/") + 1),
""),
deviceAliasOrURI(_deviceAliasOrURI), registerNamePrefix(_registerNamePrefix) {}
/*********************************************************************************************************************/
DeviceModule::DeviceModule(Application* application,
const std::string& _deviceAliasOrURI,
const std::string& _registerNamePrefix)
_registerNamePrefix.empty() ? "<Device:" + _deviceAliasOrURI + ">" :
_registerNamePrefix.substr(_registerNamePrefix.find_last_of("/") + 1),
""),
deviceAliasOrURI(_deviceAliasOrURI), registerNamePrefix(_registerNamePrefix) {
application->registerDeviceModule(this);
}
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
DeviceModule::~DeviceModule() { assert(!moduleThread.joinable()); }
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(subModules.count(moduleName) == 0) {
subModules[moduleName] = {deviceAliasOrURI, registerNamePrefix / moduleName};
Martin Christoph Hierholzer
committed
}
return subModules[moduleName];
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
const Module& DeviceModule::virtualise() const { return *this; }
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
void DeviceModule::connectTo(const Module& target, VariableNetworkNode trigger) const {
auto& cat = virtualiseFromCatalog();
Martin Christoph Hierholzer
committed
cat.connectTo(target, trigger);
}
/*********************************************************************************************************************/
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)!
Martin Christoph Hierholzer
committed
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;
Martin Christoph Hierholzer
committed
// ignore 2D registers
if(reg.getNumberOfDimensions() > 1) continue;
// guess direction and determine update mode
VariableDirection direction;
UpdateMode updateMode;
if(reg.isWriteable()) {
Martin Christoph Hierholzer
committed
direction = {VariableDirection::consuming, false};
Martin Christoph Hierholzer
committed
updateMode = UpdateMode::push;
}
else {
Martin Christoph Hierholzer
committed
direction = {VariableDirection::feeding, false};
Martin Christoph Hierholzer
committed
if(reg.getSupportedAccessModes().has(AccessMode::wait_for_new_data)) {
updateMode = UpdateMode::push;
}
else {
updateMode = UpdateMode::poll;
}
}
// guess type
const std::type_info* valTyp{&typeid(AnyType)};
auto& dd = reg.getDataDescriptor(); // numeric, string, boolean, nodata, undefined
Martin Christoph Hierholzer
committed
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
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);
}
}
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
valTyp = &typeid(double);
}
}
else if(dd.fundamentalType() == RegisterInfo::FundamentalType::boolean) {
valTyp = &typeid(int32_t);
}
else if(dd.fundamentalType() == RegisterInfo::FundamentalType::string) {
valTyp = &typeid(std::string);
}
Martin Christoph Hierholzer
committed
auto name = std::string(reg.getRegisterName()).substr(prefixLength);
Martin Christoph Hierholzer
committed
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());
Martin Christoph Hierholzer
committed
virtualisedModuleFromCatalog.createAndGetSubmoduleRecursive(dirname).addAccessor(node);
Martin Christoph Hierholzer
committed
}
virtualisedModuleFromCatalog_isValid = true;
return virtualisedModuleFromCatalog;
}
/*********************************************************************************************************************/
void DeviceModule::reportException(std::string errMsg) {
std::unique_lock<std::mutex> lk(errorMutex);
errorQueue.push(errMsg);
errorCondVar.wait(lk);
/*********************************************************************************************************************/
void DeviceModule::handleException() {
Application::registerThread("DM_" + getName());
Device d;
try {
while(true) {
errorQueue.pop_wait(error);
boost::this_thread::interruption_point();
std::lock_guard<std::mutex> lk(errorMutex);
deviceError.status = 1;
deviceError.message = error;
deviceError.setCurrentVersionNumber({});
while(true) {
boost::this_thread::interruption_point();
try {
d.open(deviceAliasOrURI);
catch(std::exception& ex) {
deviceError.status = 1;
deviceError.message = ex.what();
deviceError.setCurrentVersionNumber({});
usleep(500000);
}
deviceError.status = 0;
deviceError.message = "";
deviceError.setCurrentVersionNumber({});
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);
}
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
void DeviceModule::terminate() {
if(moduleThread.joinable()) {
moduleThread.interrupt();
reportException("ExitOnNone");
moduleThread.join();
}
assert(!moduleThread.joinable());
}
Martin Christoph Hierholzer
committed
void DeviceModule::defineConnections() {
std::string prefix = "Devices/" + deviceAliasOrURI + "/";
ControlSystemModule cs(prefix);
deviceError.connectTo(cs);
} // namespace ChimeraTK