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 {
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/*********************************************************************************************************************/
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];
}
const Module& DeviceModuleProxy::virtualise() const { return _myowner->virtualise()[_registerNamePrefix]; }
void DeviceModuleProxy::connectTo(const Module& target, VariableNetworkNode trigger) const {
virtualise().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),
registerNamePrefix("") {
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
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
/*********************************************************************************************************************/
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) {
std::unique_lock<std::mutex> lk(errorMutex);
errorQueue.push(errMsg);
errorCondVar.wait(lk);
lk.unlock();
/*********************************************************************************************************************/
void DeviceModule::handleException() {
Application::registerThread("DM_" + getName());
Device d;
std::string error;
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({});
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();
}
usleep(500000);
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();
reportException("ExitOnNone");
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