Newer
Older
Martin Christoph Hierholzer
committed
/*
* Application.cc
*
* Created on: Jun 10, 2016
* Author: Martin Hierholzer
*/
#include <string>
#include <thread>
Martin Christoph Hierholzer
committed
#include <exception>
Martin Christoph Hierholzer
committed
#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
#include "VariableNetworkNode.h"
Martin Christoph Hierholzer
committed
using namespace ChimeraTK;
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
void Application::run() {
Martin Christoph Hierholzer
committed
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
// check if the application name has been set
if(applicationName == "") {
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalParameter>(
"Error: An instance of Application must have its applicationName set.");
}
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
Martin Christoph Hierholzer
committed
// start the necessary threads for the FanOuts etc.
for(auto &adapter : adapterList) {
adapter->activate();
}
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
// start the threads for the modules
Martin Christoph Hierholzer
committed
for(auto module : moduleList) {
module->run();
}
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
void Application::shutdown() {
// deactivate the FanOuts first, since they have running threads inside accessing the modules etc.
// (note: the modules are members of the Application implementation and thus get destroyed after this destructor)
for(auto &adapter : adapterList) {
adapter->deactivate();
}
// next deactivate the modules, as they have running threads inside as well
for(auto &module : moduleList) {
module->terminate();
}
ApplicationBase::shutdown();
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
void Application::generateXML() {
initialise();
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
// check if the application name has been set
if(applicationName == "") {
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalParameter>(
"Error: An instance of Application must have its applicationName set.");
}
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
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
VariableNetwork& Application::connect(VariableNetworkNode a, VariableNetworkNode b) {
// if one of the nodes has the value type AnyType, set it to the type of the other
Martin Christoph Hierholzer
committed
// if both are AnyType, nothing changes.
if(a.getValueType() == typeid(AnyType)) {
a.setValueType(b.getValueType());
}
else if(b.getValueType() == typeid(AnyType)) {
b.setValueType(a.getValueType());
}
// if one of the nodes has not yet a defined number of elements, set it to the number of elements of the other.
// if both are undefined, nothing changes.
if(a.getNumberOfElements() == 0) {
a.setNumberOfElements(b.getNumberOfElements());
}
else if(b.getNumberOfElements() == 0) {
b.setNumberOfElements(a.getNumberOfElements());
}
if(a.getNumberOfElements() != b.getNumberOfElements()) {
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalParameter>(
"Error: Cannot connect array variables with difference number of elements!");
}
Martin Christoph Hierholzer
committed
// if both nodes already have an owner, we are done
if(a.hasOwner() && b.hasOwner()) {
assert( &(a.getOwner()) == &(b.getOwner()) ); /// @todo TODO merge networks?
}
Martin Christoph Hierholzer
committed
// add b to the existing network of a
Martin Christoph Hierholzer
committed
else if(a.hasOwner()) {
a.getOwner().addNode(b);
}
Martin Christoph Hierholzer
committed
// add a to the existing network of b
Martin Christoph Hierholzer
committed
else if(b.hasOwner()) {
b.getOwner().addNode(a);
}
Martin Christoph Hierholzer
committed
// create new network
Martin Christoph Hierholzer
committed
else {
networkList.emplace_back();
networkList.back().addNode(a);
networkList.back().addNode(b);
}
Martin Christoph Hierholzer
committed
return a.getOwner();
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
template<typename UserType>
boost::shared_ptr<mtca4u::NDRegisterAccessor<UserType>> Application::createDeviceVariable(const std::string &deviceAlias,
Martin Christoph Hierholzer
committed
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::NDRegisterAccessor<UserType>> 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::NDRegisterAccessor<UserType>> Application::createProcessVariable(VariableDirection direction,
const std::string &name, size_t nElements) {
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
return _processVariableManager->createProcessArray<UserType>(dir, name, nElements);
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
template<typename UserType>
std::pair< boost::shared_ptr<mtca4u::NDRegisterAccessor<UserType>>, boost::shared_ptr<mtca4u::NDRegisterAccessor<UserType>> >
Application::createApplicationVariable(size_t nElements) {
Martin Christoph Hierholzer
committed
// create the ProcessScalar for the proper UserType
return createSynchronizedProcessArray<UserType>(nElements);
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::dumpConnections() {
std::cout << "==== List of all variable connections of the current Application ====" << std::endl;
Martin Christoph Hierholzer
committed
for(auto &network : networkList) {
network.dump();
}
std::cout << "=====================================================================" << std::endl;
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.getFeedingNode().hasExternalTrigger()) {
VariableNetwork &dependency = network.getFeedingNode().getExternalTrigger().getOwner();
Martin Christoph Hierholzer
committed
if(!dependency.isCreated()) makeConnectionsForNetwork(dependency);
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
// 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;
Martin Christoph Hierholzer
committed
auto feeder = network.getFeedingNode();
Martin Christoph Hierholzer
committed
auto consumers = network.getConsumingNodes();
Martin Christoph Hierholzer
committed
bool useExternalTrigger = network.getTriggerType() == VariableNetwork::TriggerType::external;
Martin Christoph Hierholzer
committed
bool useFeederTrigger = network.getTriggerType() == VariableNetwork::TriggerType::feeder;
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::NDRegisterAccessor<UserType>> feedingImpl;
Martin Christoph Hierholzer
committed
if(feeder.getType() == NodeType::Device) {
feedingImpl = createDeviceVariable<UserType>(feeder.getDeviceAlias(), feeder.getRegisterName(),
Martin Christoph Hierholzer
committed
VariableDirection::consuming, feeder.getMode());
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
else if(feeder.getType() == NodeType::ControlSystem) {
feedingImpl = createProcessVariable<UserType>(VariableDirection::consuming, feeder.getPublicName(), feeder.getNumberOfElements());
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();
Martin Christoph Hierholzer
committed
if(consumer.getType() == NodeType::Application) {
consumer.getAppAccessor().useProcessVariable(feedingImpl);
Martin Christoph Hierholzer
committed
connectionMade = true;
}
Martin Christoph Hierholzer
committed
else if(consumer.getType() == NodeType::Device) {
auto consumingImpl = createDeviceVariable<UserType>(consumer.getDeviceAlias(), consumer.getRegisterName(),
Martin Christoph Hierholzer
committed
VariableDirection::feeding, consumer.getMode());
Martin Christoph Hierholzer
committed
// 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;
}
Martin Christoph Hierholzer
committed
else if(consumer.getType() == NodeType::ControlSystem) {
auto consumingImpl = createProcessVariable<UserType>(VariableDirection::feeding, consumer.getPublicName(),
consumer.getNumberOfElements());
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.getType() == NodeType::TriggerReceiver) {
consumer.getTriggerReceiver().getOwner().setExternalTriggerImpl(feedingImpl);
Martin Christoph Hierholzer
committed
connectionMade = true;
}
Martin Christoph Hierholzer
committed
else {
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalParameter>("Unexpected node type!");
}
}
Martin Christoph Hierholzer
committed
else { /* !(nNodes == 2 && !useExternalTrigger) */
Martin Christoph Hierholzer
committed
// 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
// if the trigger is provided by the pushing feeder, use the treaded version of the FanOut to distribute
// new values immediately to all consumers.
else if(useFeederTrigger) {
isFirst = false;
}
Martin Christoph Hierholzer
committed
for(auto &consumer : consumers) {
Martin Christoph Hierholzer
committed
if(consumer.getType() == NodeType::Application) {
Martin Christoph Hierholzer
committed
if(isFirst) {
Martin Christoph Hierholzer
committed
consumer.getAppAccessor().useProcessVariable(fanOut);
Martin Christoph Hierholzer
committed
isFirst = false;
}
else {
auto impls = createApplicationVariable<UserType>(consumer.getNumberOfElements());
Martin Christoph Hierholzer
committed
fanOut->addSlave(impls.first);
Martin Christoph Hierholzer
committed
consumer.getAppAccessor().useProcessVariable(impls.second);
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
else if(consumer.getType() == NodeType::ControlSystem) {
auto impl = createProcessVariable<UserType>(VariableDirection::feeding, consumer.getPublicName(),
consumer.getNumberOfElements());
Martin Christoph Hierholzer
committed
fanOut->addSlave(impl);
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
else if(consumer.getType() == NodeType::Device) {
auto impl = createDeviceVariable<UserType>(consumer.getDeviceAlias(), consumer.getRegisterName(),
Martin Christoph Hierholzer
committed
VariableDirection::feeding, consumer.getMode());
Martin Christoph Hierholzer
committed
fanOut->addSlave(impl);
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
else if(consumer.getType() == NodeType::TriggerReceiver) {
auto impls = createApplicationVariable<UserType>(consumer.getNumberOfElements());
Martin Christoph Hierholzer
committed
fanOut->addSlave(impls.first);
consumer.getTriggerReceiver().getOwner().setExternalTriggerImpl(impls.second);
Martin Christoph Hierholzer
committed
}
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 || useFeederTrigger) { // 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
Martin Christoph Hierholzer
committed
else { /* !feeder.hasImplementation() */
Martin Christoph Hierholzer
committed
// we should be left with an application feeder node
Martin Christoph Hierholzer
committed
if(feeder.getType() != NodeType::Application) {
Martin Christoph Hierholzer
committed
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();
Martin Christoph Hierholzer
committed
if(consumer.getType() == NodeType::Application) {
auto impls = createApplicationVariable<UserType>(consumer.getNumberOfElements());
Martin Christoph Hierholzer
committed
feeder.getAppAccessor().useProcessVariable(impls.first);
consumer.getAppAccessor().useProcessVariable(impls.second);
Martin Christoph Hierholzer
committed
connectionMade = true;
}
Martin Christoph Hierholzer
committed
else if(consumer.getType() == NodeType::ControlSystem) {
auto impl = createProcessVariable<UserType>(VariableDirection::feeding, consumer.getPublicName(),
consumer.getNumberOfElements()
);
Martin Christoph Hierholzer
committed
feeder.getAppAccessor().useProcessVariable(impl);
Martin Christoph Hierholzer
committed
connectionMade = true;
}
Martin Christoph Hierholzer
committed
else if(consumer.getType() == NodeType::Device) {
auto impl = createDeviceVariable<UserType>(consumer.getDeviceAlias(), consumer.getRegisterName(),
Martin Christoph Hierholzer
committed
VariableDirection::feeding, consumer.getMode());
feeder.getAppAccessor().useProcessVariable(impl);
Martin Christoph Hierholzer
committed
connectionMade = true;
}
Martin Christoph Hierholzer
committed
else if(consumer.getType() == NodeType::TriggerReceiver) {
auto impls = createApplicationVariable<UserType>(consumer.getNumberOfElements());
Martin Christoph Hierholzer
committed
feeder.getAppAccessor().useProcessVariable(impls.first);
consumer.getTriggerReceiver().getOwner().setExternalTriggerImpl(impls.second);
Martin Christoph Hierholzer
committed
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>());
Martin Christoph Hierholzer
committed
feeder.getAppAccessor().useProcessVariable(fanOut);
Martin Christoph Hierholzer
committed
for(auto &consumer : consumers) {
Martin Christoph Hierholzer
committed
if(consumer.getType() == NodeType::Application) {
auto impls = createApplicationVariable<UserType>(consumer.getNumberOfElements());
Martin Christoph Hierholzer
committed
fanOut->addSlave(impls.first);
Martin Christoph Hierholzer
committed
consumer.getAppAccessor().useProcessVariable(impls.second);
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
else if(consumer.getType() == NodeType::ControlSystem) {
auto impl = createProcessVariable<UserType>(VariableDirection::feeding, consumer.getPublicName(),
consumer.getNumberOfElements());
Martin Christoph Hierholzer
committed
fanOut->addSlave(impl);
}
Martin Christoph Hierholzer
committed
else if(consumer.getType() == NodeType::Device) {
auto impl = createDeviceVariable<UserType>(consumer.getDeviceAlias(), consumer.getRegisterName(),
Martin Christoph Hierholzer
committed
VariableDirection::feeding, consumer.getMode());
Martin Christoph Hierholzer
committed
fanOut->addSlave(impl);
}
Martin Christoph Hierholzer
committed
else if(consumer.getType() == NodeType::TriggerReceiver) {
auto impls = createApplicationVariable<UserType>(consumer.getNumberOfElements());
Martin Christoph Hierholzer
committed
fanOut->addSlave(impls.first);
consumer.getTriggerReceiver().getOwner().setExternalTriggerImpl(impls.second);
Martin Christoph Hierholzer
committed
}
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
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
VariableNetwork& Application::createNetwork() {
networkList.emplace_back();
return networkList.back();
}
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
Application& Application::getInstance() {
return dynamic_cast<Application&>(ApplicationBase::getInstance());
}