Newer
Older
Martin Christoph Hierholzer
committed
/*
* Application.cc
*
* Created on: Jun 10, 2016
* Author: Martin Hierholzer
*/
#include <string>
#include <thread>
#include <boost/fusion/container/map.hpp>
Martin Christoph Hierholzer
committed
#include <libxml++/libxml++.h>
Martin Christoph Hierholzer
committed
#include <mtca4u/BackendFactory.h>
Martin Christoph Hierholzer
committed
#include "Application.h"
#include "ApplicationModule.h"
#include "Accessor.h"
Martin Christoph Hierholzer
committed
#include "DeviceAccessor.h"
Martin Christoph Hierholzer
committed
#include "FanOut.h"
Martin Christoph Hierholzer
committed
using namespace ChimeraTK;
Application *Application::instance = nullptr;
std::mutex Application::instance_mutex;
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
Application::Application(const std::string& name)
: applicationName(name)
{
Martin Christoph Hierholzer
committed
std::lock_guard<std::mutex> lock(instance_mutex);
if(instance != nullptr) {
throw std::string("Multiple instances of ChimeraTK::Application cannot be created."); // @todo TODO throw proper exception
}
instance = this;
}
/*********************************************************************************************************************/
void Application::run() {
Martin Christoph Hierholzer
committed
// call the user-defined initialise() function which describes the structure of the application
Martin Christoph Hierholzer
committed
initialise();
Martin Christoph Hierholzer
committed
// realise the connections between variable accessors as described in the initialise() function
Martin Christoph Hierholzer
committed
makeConnections();
Martin Christoph Hierholzer
committed
// start the necessary threads for the FanOuts etc.
for(auto &adapter : adapterList) {
adapter->activate();
}
// start the threads for the modules
Martin Christoph Hierholzer
committed
for(auto module : moduleList) {
module->run();
}
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
void Application::generateXML() {
initialise();
Martin Christoph Hierholzer
committed
// create XML document with root node
xmlpp::Document doc;
Martin Christoph Hierholzer
committed
xmlpp::Element *rootElement = doc.create_root_node("application", "https://github.com/ChimeraTK/ApplicationCore");
rootElement->set_attribute("name",applicationName);
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
for(auto &network : networkList) {
Martin Christoph Hierholzer
committed
// perform checks
Martin Christoph Hierholzer
committed
network.dump();
Martin Christoph Hierholzer
committed
network.check();
Martin Christoph Hierholzer
committed
// create xml code for the feeder (if it is a control system node)
Martin Christoph Hierholzer
committed
auto feeder = network.getFeedingNode();
Martin Christoph Hierholzer
committed
feeder.createXML(rootElement);
// create xml code for the consumers
for(auto &consumer : network.getConsumingNodes()) {
consumer.createXML(rootElement);
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
/// @todo TODO don't write to stdout but to a file...
Martin Christoph Hierholzer
committed
doc.write_to_file_formatted(applicationName+".xml");
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
void Application::connectAccessors(AccessorBase &a, AccessorBase &b) {
Martin Christoph Hierholzer
committed
VariableNetwork &network = findOrCreateNetwork(&a,&b);
Martin Christoph Hierholzer
committed
network.addAppNode(a);
network.addAppNode(b);
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
template<typename UserType>
boost::shared_ptr<mtca4u::ProcessVariable> Application::createDeviceAccessor(const std::string &deviceAlias,
const std::string ®isterName, VariableDirection direction, UpdateMode mode) {
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
// open device if needed
if(deviceMap.count(deviceAlias) == 0) {
deviceMap[deviceAlias] = mtca4u::BackendFactory::getInstance().createBackend(deviceAlias);
deviceMap[deviceAlias]->open();
}
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
// use wait_for_new_data mode if push update mode was requested
Martin Christoph Hierholzer
committed
mtca4u::AccessModeFlags flags{};
Martin Christoph Hierholzer
committed
if(mode == UpdateMode::push && direction == VariableDirection::consuming) flags = {AccessMode::wait_for_new_data};
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
// create DeviceAccessor for the proper UserType
boost::shared_ptr<mtca4u::ProcessVariable> impl;
Martin Christoph Hierholzer
committed
auto regacc = deviceMap[deviceAlias]->getRegisterAccessor<UserType>(registerName, 1, 0, flags);
impl.reset(new DeviceAccessor<UserType>(regacc, direction, mode));
Martin Christoph Hierholzer
committed
return impl;
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
template<typename UserType>
boost::shared_ptr<mtca4u::ProcessVariable> Application::createProcessScalar(VariableDirection direction,
const std::string &name) {
Martin Christoph Hierholzer
committed
// determine the SynchronizationDirection
SynchronizationDirection dir;
if(direction == VariableDirection::consuming) {
Martin Christoph Hierholzer
committed
dir = SynchronizationDirection::controlSystemToDevice;
Martin Christoph Hierholzer
committed
}
else {
Martin Christoph Hierholzer
committed
dir = SynchronizationDirection::deviceToControlSystem;
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
// create the ProcessScalar for the proper UserType
boost::shared_ptr<mtca4u::ProcessVariable> impl;
Martin Christoph Hierholzer
committed
impl = _processVariableManager->createProcessScalar<UserType>(dir, name);
Martin Christoph Hierholzer
committed
return impl;
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
template<typename UserType>
Martin Christoph Hierholzer
committed
std::pair< boost::shared_ptr<mtca4u::ProcessVariable>, boost::shared_ptr<mtca4u::ProcessVariable> >
Martin Christoph Hierholzer
committed
Application::createProcessScalar() {
Martin Christoph Hierholzer
committed
// create the ProcessScalar for the proper UserType
Martin Christoph Hierholzer
committed
return createSynchronizedProcessScalar<UserType>();
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
void Application::makeConnections() {
Martin Christoph Hierholzer
committed
// make the connections for all networks
for(auto &network : networkList) {
makeConnectionsForNetwork(network);
}
}
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
void Application::makeConnectionsForNetwork(VariableNetwork &network) {
// if the network has been created already, do nothing
if(network.isCreated()) return;
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
// check if the network is legal
network.check();
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
// if the trigger type is external, create the trigger first
if(network.getTriggerType() == VariableNetwork::TriggerType::external) {
VariableNetwork &dependency = network.getExternalTrigger();
if(!dependency.isCreated()) makeConnectionsForNetwork(dependency);
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// defer actual network creation to templated function
// @todo TODO replace with boost::mpl::for_each loop!
if(network.getValueType() == typeid(int8_t)) {
typedMakeConnection<int8_t>(network);
}
else if(network.getValueType() == typeid(uint8_t)) {
typedMakeConnection<uint8_t>(network);
}
else if(network.getValueType() == typeid(int16_t)) {
typedMakeConnection<int16_t>(network);
}
else if(network.getValueType() == typeid(uint16_t)) {
typedMakeConnection<uint16_t>(network);
}
else if(network.getValueType() == typeid(int32_t)) {
typedMakeConnection<int32_t>(network);
}
else if(network.getValueType() == typeid(uint32_t)) {
typedMakeConnection<uint32_t>(network);
}
else if(network.getValueType() == typeid(float)) {
typedMakeConnection<float>(network);
}
else if(network.getValueType() == typeid(double)) {
typedMakeConnection<double>(network);
}
// mark the network as created
network.markCreated();
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
template<typename UserType>
void Application::typedMakeConnection(VariableNetwork &network) {
bool connectionMade = false; // to check the logic...
size_t nNodes = network.countConsumingNodes()+1;
auto &feeder = network.getFeedingNode();
auto &consumers = network.getConsumingNodes();
Martin Christoph Hierholzer
committed
bool useExternalTrigger = network.getTriggerType() == VariableNetwork::TriggerType::external;
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
boost::shared_ptr<FanOut<UserType>> fanOut;
// 1st case: the feeder requires a fixed implementation
if(feeder.hasImplementation()) {
// Create feeding implementation. Note: though the implementation is derived from the feeder, it will be used as
// the implementation of the (or one of the) consumer. Logically, implementations are always pairs of
// implementations (sender and receiver), but in this case the feeder already has a fixed implementation pair.
// So our feedingImpl will contain the consumer-end of the implementation pair. This is the reason why the
// functions createProcessScalar() and createDeviceAccessor() get the VariableDirection::consuming.
boost::shared_ptr<mtca4u::ProcessVariable> feedingImpl;
Martin Christoph Hierholzer
committed
if(feeder.type == VariableNetwork::NodeType::Device) {
feedingImpl = createDeviceAccessor<UserType>(feeder.deviceAlias, feeder.registerName,
VariableDirection::consuming, feeder.mode);
Martin Christoph Hierholzer
committed
}
else if(feeder.type == VariableNetwork::NodeType::ControlSystem) {
feedingImpl = createProcessScalar<UserType>(VariableDirection::consuming, feeder.publicName);
Martin Christoph Hierholzer
committed
}
// if we just have two nodes, directly connect them
Martin Christoph Hierholzer
committed
if(nNodes == 2 && !useExternalTrigger) {
Martin Christoph Hierholzer
committed
auto consumer = consumers.front();
if(consumer.type == VariableNetwork::NodeType::Application) {
consumer.appNode->useProcessVariable(feedingImpl);
connectionMade = true;
}
else if(consumers.front().type == VariableNetwork::NodeType::Device) {
auto consumingImpl = createDeviceAccessor<UserType>(consumer.deviceAlias, consumer.registerName,
VariableDirection::feeding, consumer.mode);
// connect the Device with e.g. a ControlSystem node via an ImplementationAdapter
adapterList.push_back(boost::shared_ptr<ImplementationAdapterBase>(
new ImplementationAdapter<UserType>(consumingImpl,feedingImpl)));
connectionMade = true;
}
else if(consumer.type == VariableNetwork::NodeType::ControlSystem) {
auto consumingImpl = createProcessScalar<UserType>(VariableDirection::feeding, consumer.publicName);
Martin Christoph Hierholzer
committed
// connect the ControlSystem with e.g. a Device node via an ImplementationAdapter
Martin Christoph Hierholzer
committed
adapterList.push_back(boost::shared_ptr<ImplementationAdapterBase>(
new ImplementationAdapter<UserType>(consumingImpl,feedingImpl)));
Martin Christoph Hierholzer
committed
connectionMade = true;
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
else if(consumer.type == VariableNetwork::NodeType::TriggerReceiver) {
consumer.triggerReceiver->setExternalTriggerImpl(feedingImpl);
connectionMade = true;
}
Martin Christoph Hierholzer
committed
else {
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalParameter>("Unexpected node type!");
}
}
else {
// create FanOut
fanOut.reset(new FanOut<UserType>(feedingImpl));
Martin Christoph Hierholzer
committed
// use FanOut as implementation for the first application consumer node, add all others as slaves
// @todo TODO need a more sophisticated logic to take care of the UpdateMode
bool isFirst = true;
Martin Christoph Hierholzer
committed
// if external trigger is enabled, add it to FanOut
if(useExternalTrigger) {
isFirst = false; // don't use the FanOut as an implementation if we have an external trigger
fanOut->addExternalTrigger(network.getExternalTriggerImpl());
}
Martin Christoph Hierholzer
committed
for(auto &consumer : consumers) {
if(consumer.type == VariableNetwork::NodeType::Application) {
if(isFirst) {
consumer.appNode->useProcessVariable(fanOut);
isFirst = false;
}
else {
auto impls = createProcessScalar<UserType>();
fanOut->addSlave(impls.first);
consumer.appNode->useProcessVariable(impls.second);
}
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
else if(consumer.type == VariableNetwork::NodeType::ControlSystem) {
auto impl = createProcessScalar<UserType>(VariableDirection::feeding, consumer.publicName);
Martin Christoph Hierholzer
committed
fanOut->addSlave(impl);
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
else if(consumer.type == VariableNetwork::NodeType::Device) {
auto impl = createDeviceAccessor<UserType>(consumer.deviceAlias, consumer.registerName,
VariableDirection::feeding, consumer.mode);
Martin Christoph Hierholzer
committed
fanOut->addSlave(impl);
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
else if(consumer.type == VariableNetwork::NodeType::TriggerReceiver) {
auto impls = createProcessScalar<UserType>();
fanOut->addSlave(impls.first);
consumer.triggerReceiver->setExternalTriggerImpl(impls.second);
}
Martin Christoph Hierholzer
committed
else {
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalParameter>("Unexpected node type!");
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
if(isFirst || useExternalTrigger) { // FanOut wasn't used as implementation: store to list to keep it alive
Martin Christoph Hierholzer
committed
adapterList.push_back(fanOut);
Martin Christoph Hierholzer
committed
}
connectionMade = true;
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
}
// 2nd case: the feeder does not require a fixed implementation
else {
// we should be left with an application feeder node
if(feeder.type != VariableNetwork::NodeType::Application) {
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalParameter>("Unexpected node type!");
}
Martin Christoph Hierholzer
committed
assert(!useExternalTrigger);
Martin Christoph Hierholzer
committed
// if we just have two nodes, directly connect them
if(nNodes == 2) {
Martin Christoph Hierholzer
committed
auto consumer = consumers.front();
if(consumer.type == VariableNetwork::NodeType::Application) {
Martin Christoph Hierholzer
committed
auto impls = createProcessScalar<UserType>();
feeder.appNode->useProcessVariable(impls.first);
Martin Christoph Hierholzer
committed
consumer.appNode->useProcessVariable(impls.second);
Martin Christoph Hierholzer
committed
connectionMade = true;
}
Martin Christoph Hierholzer
committed
else if(consumer.type == VariableNetwork::NodeType::ControlSystem) {
auto impl = createProcessScalar<UserType>(VariableDirection::feeding, consumer.publicName);
Martin Christoph Hierholzer
committed
feeder.appNode->useProcessVariable(impl);
connectionMade = true;
}
Martin Christoph Hierholzer
committed
else if(consumer.type == VariableNetwork::NodeType::Device) {
auto impl = createDeviceAccessor<UserType>(consumer.deviceAlias, consumer.registerName,
VariableDirection::feeding, consumer.mode);
Martin Christoph Hierholzer
committed
feeder.appNode->useProcessVariable(impl);
connectionMade = true;
}
Martin Christoph Hierholzer
committed
else if(consumer.type == VariableNetwork::NodeType::TriggerReceiver) {
auto impls = createProcessScalar<UserType>();
feeder.appNode->useProcessVariable(impls.first);
consumer.triggerReceiver->setExternalTriggerImpl(impls.second);
connectionMade = true;
}
Martin Christoph Hierholzer
committed
else {
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalParameter>("Unexpected node type!");
}
}
else {
// create FanOut and use it as the feeder implementation
fanOut.reset(new FanOut<UserType>());
feeder.appNode->useProcessVariable(fanOut);
for(auto &consumer : consumers) {
if(consumer.type == VariableNetwork::NodeType::Application) {
auto impls = createProcessScalar<UserType>();
fanOut->addSlave(impls.first);
consumer.appNode->useProcessVariable(impls.second);
}
else if(consumer.type == VariableNetwork::NodeType::ControlSystem) {
auto impl = createProcessScalar<UserType>(VariableDirection::feeding, consumer.publicName);
Martin Christoph Hierholzer
committed
fanOut->addSlave(impl);
}
else if(consumer.type == VariableNetwork::NodeType::Device) {
auto impl = createDeviceAccessor<UserType>(consumer.deviceAlias, consumer.registerName,
VariableDirection::feeding, consumer.mode);
Martin Christoph Hierholzer
committed
fanOut->addSlave(impl);
}
Martin Christoph Hierholzer
committed
else if(consumer.type == VariableNetwork::NodeType::TriggerReceiver) {
auto impls = createProcessScalar<UserType>();
fanOut->addSlave(impls.first);
consumer.triggerReceiver->setExternalTriggerImpl(impls.second);
}
Martin Christoph Hierholzer
committed
else {
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalParameter>("Unexpected node type!");
}
}
connectionMade = true;
Martin Christoph Hierholzer
committed
}
}
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
if(!connectionMade) {
throw ApplicationExceptionWithID<ApplicationExceptionID::notYetImplemented>(
Martin Christoph Hierholzer
committed
"The variable network cannot be handled. Implementation missing!");
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
VariableNetwork& Application::findOrCreateNetwork(AccessorBase *a, AccessorBase *b) {
Martin Christoph Hierholzer
committed
// search for a and b in the networkList
auto &na = findNetwork(a);
auto &nb = findNetwork(b);
// if both accessors are found, check if both are in the same network
if(na != invalidNetwork && nb != invalidNetwork) {
if(na == nb) {
return na;
}
else {
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalParameter>(
"Trying to connect two accessors which already are part of a network.");
}
}
// if only one accessor is found, return its network
else if(na != invalidNetwork && nb == invalidNetwork) {
return na;
}
else if(na == invalidNetwork && nb != invalidNetwork) {
return nb;
}
// if no accessor is found, create a new network and add it to the list
Martin Christoph Hierholzer
committed
networkList.emplace_back();
Martin Christoph Hierholzer
committed
return networkList.back();
}
/*********************************************************************************************************************/
VariableNetwork& Application::findOrCreateNetwork(AccessorBase *a) {
// search in the networkList
auto &na = findNetwork(a);
// if the accessors is found, return its network
if(na != invalidNetwork) {
return na;
}
// if no accessor is found, create a new network and add it to the list
Martin Christoph Hierholzer
committed
networkList.emplace_back();
Martin Christoph Hierholzer
committed
return networkList.back();
}
/*********************************************************************************************************************/
VariableNetwork& Application::findNetwork(AccessorBase *a) {
// search for a and b in the networkList
Martin Christoph Hierholzer
committed
auto r = find_if(networkList.begin(), networkList.end(),
Martin Christoph Hierholzer
committed
[a](const VariableNetwork& n) { return n.hasAppNode(a); } );
Martin Christoph Hierholzer
committed
// if no network found, create one
if(r == networkList.end()) {
Martin Christoph Hierholzer
committed
return invalidNetwork;
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
// return the found network
return *r;
}
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
template<typename UserType>
Martin Christoph Hierholzer
committed
void Application::feedDeviceRegisterToControlSystem(const std::string &deviceAlias, const std::string ®isterName,
Martin Christoph Hierholzer
committed
const std::string& publicName, AccessorBase &trigger) {
Martin Christoph Hierholzer
committed
networkList.emplace_back();
VariableNetwork& network = networkList.back();
Martin Christoph Hierholzer
committed
UpdateMode mode = UpdateMode::push;
if(dynamic_cast<InvalidAccessor*>(&trigger) != nullptr) {
mode = UpdateMode::poll;
network.addTrigger(trigger);
}
Martin Christoph Hierholzer
committed
network.addFeedingDeviceRegister(typeid(UserType), "arbitrary", deviceAlias, registerName, mode);
Martin Christoph Hierholzer
committed
network.addConsumingPublication(publicName);
}
Martin Christoph Hierholzer
committed
template void Application::feedDeviceRegisterToControlSystem<int8_t>(const std::string &deviceAlias, const std::string ®isterName, const std::string& publicName, AccessorBase &trigger);
template void Application::feedDeviceRegisterToControlSystem<uint8_t>(const std::string &deviceAlias, const std::string ®isterName, const std::string& publicName, AccessorBase &trigger);
template void Application::feedDeviceRegisterToControlSystem<int16_t>(const std::string &deviceAlias, const std::string ®isterName, const std::string& publicName, AccessorBase &trigger);
template void Application::feedDeviceRegisterToControlSystem<uint16_t>(const std::string &deviceAlias, const std::string ®isterName, const std::string& publicName, AccessorBase &trigger);
template void Application::feedDeviceRegisterToControlSystem<int32_t>(const std::string &deviceAlias, const std::string ®isterName, const std::string& publicName, AccessorBase &trigger);
template void Application::feedDeviceRegisterToControlSystem<uint32_t>(const std::string &deviceAlias, const std::string ®isterName, const std::string& publicName, AccessorBase &trigger);
template void Application::feedDeviceRegisterToControlSystem<float>(const std::string &deviceAlias, const std::string ®isterName, const std::string& publicName, AccessorBase &trigger);
template void Application::feedDeviceRegisterToControlSystem<double>(const std::string &deviceAlias, const std::string ®isterName, const std::string& publicName, AccessorBase &trigger);
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
template<typename UserType>
Martin Christoph Hierholzer
committed
void Application::consumeDeviceRegisterFromControlSystem(const std::string &deviceAlias, const std::string ®isterName,
Martin Christoph Hierholzer
committed
const std::string& publicName) {
Martin Christoph Hierholzer
committed
networkList.emplace_back();
VariableNetwork& network = networkList.back();
Martin Christoph Hierholzer
committed
network.addFeedingPublication(typeid(UserType), "arbitrary", publicName);
Martin Christoph Hierholzer
committed
network.addConsumingDeviceRegister(deviceAlias, registerName);
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
template void Application::consumeDeviceRegisterFromControlSystem<int8_t>(const std::string &deviceAlias, const std::string ®isterName, const std::string& publicName);
template void Application::consumeDeviceRegisterFromControlSystem<uint8_t>(const std::string &deviceAlias, const std::string ®isterName, const std::string& publicName);
template void Application::consumeDeviceRegisterFromControlSystem<int16_t>(const std::string &deviceAlias, const std::string ®isterName, const std::string& publicName);
template void Application::consumeDeviceRegisterFromControlSystem<uint16_t>(const std::string &deviceAlias, const std::string ®isterName, const std::string& publicName);
template void Application::consumeDeviceRegisterFromControlSystem<int32_t>(const std::string &deviceAlias, const std::string ®isterName, const std::string& publicName);
template void Application::consumeDeviceRegisterFromControlSystem<uint32_t>(const std::string &deviceAlias, const std::string ®isterName, const std::string& publicName);
template void Application::consumeDeviceRegisterFromControlSystem<float>(const std::string &deviceAlias, const std::string ®isterName, const std::string& publicName);
template void Application::consumeDeviceRegisterFromControlSystem<double>(const std::string &deviceAlias, const std::string ®isterName, const std::string& publicName);
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/