Newer
Older
Martin Christoph Hierholzer
committed
/*
* VariableNetwork.cc
*
* Created on: Jun 14, 2016
* Author: Martin Hierholzer
*/
Martin Christoph Hierholzer
committed
#include <libxml++/libxml++.h>
Martin Christoph Hierholzer
committed
#include "VariableNetwork.h"
#include "Accessor.h"
Martin Christoph Hierholzer
committed
#include "Application.h"
Martin Christoph Hierholzer
committed
namespace ChimeraTK {
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
bool VariableNetwork::hasFeedingNode() const {
Martin Christoph Hierholzer
committed
auto n = std::count_if( nodeList.begin(), nodeList.end(),
[](const VariableNetworkNode n) {
return n.getDirection() == VariableDirection::feeding;
} );
assert(n < 2);
return n == 1;
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
size_t VariableNetwork::countConsumingNodes() const {
Martin Christoph Hierholzer
committed
return nodeList.size() - (hasFeedingNode() ? 1 : 0);
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
void VariableNetwork::addNode(VariableNetworkNode &a) {
if(a.hasOwner()) { // already in the network
assert( &(a.getOwner()) == this ); /// @todo TODO merge networks?
return;
}
Martin Christoph Hierholzer
committed
// change owner of the node: erase from Application's unconnectedNodeList and set this as owner
Martin Christoph Hierholzer
committed
a.setOwner(this);
// if node is feeding, save as feeder for this network
if(a.getDirection() == VariableDirection::feeding) {
// make sure we only have one feeding node per network
if(hasFeedingNode()) {
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(
"Trying to add a feeding accessor to a network already having a feeding accessor.");
}
Martin Christoph Hierholzer
committed
// force value type and engineering unit of the network if set in this feeding node
Martin Christoph Hierholzer
committed
if(a.getValueType() != typeid(AnyType)) valueType = &(a.getValueType());
if(a.getUnit() != "arbitrary") engineeringUnit = a.getUnit();
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
else {
// update value type and engineering unit, if not yet set
if(valueType == &typeid(AnyType)) valueType = &(a.getValueType());
if(engineeringUnit == "arbitrary") engineeringUnit = a.getUnit();
}
Martin Christoph Hierholzer
committed
// add node to node list
nodeList.push_back(a);
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
void VariableNetwork::dump(const std::string& linePrefix) const {
std::cout << linePrefix << "VariableNetwork {" << std::endl;
std::cout << linePrefix << " value type = " << valueType->name() << ", engineering unit = " << engineeringUnit << std::endl;
std::cout << linePrefix << " trigger type = ";
try {
TriggerType tt = getTriggerType();
if(tt == TriggerType::feeder) std::cout << "feeder" << std::endl;
if(tt == TriggerType::pollingConsumer) std::cout << "pollingConsumer" << std::endl;
if(tt == TriggerType::external) std::cout << "external" << std::endl;
if(tt == TriggerType::none) std::cout << "none" << std::endl;
}
catch(ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork> &e) {
std::cout << "**error**" << std::endl;
}
std::cout << linePrefix << " feeder";
Martin Christoph Hierholzer
committed
if(hasFeedingNode()) {
getFeedingNode().dump();
}
else {
std::cout << " **error, no feeder found**" << std::endl;
}
Martin Christoph Hierholzer
committed
std::cout << linePrefix << " consumers: " << countConsumingNodes() << std::endl;
Martin Christoph Hierholzer
committed
size_t count = 0;
Martin Christoph Hierholzer
committed
for(auto &consumer : nodeList) {
if(consumer.getDirection() != VariableDirection::consuming) continue;
Martin Christoph Hierholzer
committed
std::cout << linePrefix << " # " << ++count << ":";
Martin Christoph Hierholzer
committed
consumer.dump();
}
if(getFeedingNode().hasExternalTrigger()) {
std::cout << linePrefix << " external trigger node: ";
getFeedingNode().getExternalTrigger().dump();
Martin Christoph Hierholzer
committed
}
std::cout << linePrefix << "}" << std::endl;
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
void VariableNetwork::addTriggerReceiver(VariableNetworkNode& nodeToTrigger) {
VariableNetworkNode node(nodeToTrigger, 0);
Martin Christoph Hierholzer
committed
node.setOwner(this);
Martin Christoph Hierholzer
committed
nodeList.push_back(node);
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
VariableNetwork::TriggerType VariableNetwork::getTriggerType() const {
Martin Christoph Hierholzer
committed
const auto &feeder = getFeedingNode();
Martin Christoph Hierholzer
committed
// network has an external trigger
if(feeder.hasExternalTrigger()) {
Martin Christoph Hierholzer
committed
if(feeder.getMode() == UpdateMode::push) {
Martin Christoph Hierholzer
committed
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(
"Providing an external trigger to a variable network which is fed by a pushing variable is not allowed.");
}
return TriggerType::external;
}
// network is fed by a pushing node
Martin Christoph Hierholzer
committed
if(feeder.getMode() == UpdateMode::push) {
Martin Christoph Hierholzer
committed
return TriggerType::feeder;
}
// network is fed by a poll-type node: must have exactly one polling consumer
Martin Christoph Hierholzer
committed
size_t nPollingConsumers = count_if( nodeList.begin(), nodeList.end(),
[](const VariableNetworkNode n) {
return n.getDirection() == VariableDirection::consuming && n.getMode() == UpdateMode::poll;
} );
Martin Christoph Hierholzer
committed
if(nPollingConsumers != 1) {
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(
"In a network with a poll-type feeder and no external trigger, there must be exactly one polling consumer.");
}
return TriggerType::pollingConsumer;
}
/*********************************************************************************************************************/
void VariableNetwork::check() {
// must have consuming nodes
if(countConsumingNodes() == 0) {
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(
Martin Christoph Hierholzer
committed
"No consuming nodes connected to this network!");
Martin Christoph Hierholzer
committed
}
// must have a feeding node
if(!hasFeedingNode()) {
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(
Martin Christoph Hierholzer
committed
"No feeding node connected to this network!");
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
// the network's value type must be correctly set
if(*valueType == typeid(AnyType)) {
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(
"No data type specified for any of the nodes in this network!");
}
// all nodes must have this network as the owner and a value type equal the network's value type
Martin Christoph Hierholzer
committed
for(auto &node : nodeList) {
assert(&(node.getOwner()) == this);
if(node.getValueType() == typeid(AnyType)) node.setValueType(*valueType);
assert(node.getValueType() == *valueType);
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
// if the feeder is an application node, it must be in push mode
Martin Christoph Hierholzer
committed
if(getFeedingNode().getType() == NodeType::Application) {
assert(getFeedingNode().getMode() == UpdateMode::push);
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
// check if trigger is correctly defined (the return type doesn't matter, only the checks done in the function are needed)
getTriggerType();
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
VariableNetworkNode VariableNetwork::getFeedingNode() const {
auto iter = std::find_if( nodeList.begin(), nodeList.end(),
[](const VariableNetworkNode n) {
return n.getDirection() == VariableDirection::feeding;
} );
Martin Christoph Hierholzer
committed
if(iter == nodeList.end()) {
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(
"No feeding node in this network!");
}
Martin Christoph Hierholzer
committed
return *iter;
}
/*********************************************************************************************************************/
std::list<VariableNetworkNode> VariableNetwork::getConsumingNodes() const{
std::list<VariableNetworkNode> consumers;
Martin Christoph Hierholzer
committed
for(auto &n : nodeList) if(n.getDirection() == VariableDirection::consuming) consumers.push_back(n);
Martin Christoph Hierholzer
committed
return consumers;
}