Newer
Older
Martin Christoph Hierholzer
committed
/*
* VariableNetwork.cc
*
* Created on: Jun 14, 2016
* Author: Martin Hierholzer
*/
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
#include <libxml++/libxml++.h>
Martin Christoph Hierholzer
committed
#include "VariableNetwork.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()) {
Martin Christoph Hierholzer
committed
std::stringstream msg;
Martin Christoph Hierholzer
committed
msg << "Trying to add a feeding accessor to a network already having a feeding accessor." << std::endl;
msg << "The network you were trying to add the new accessor to:" << std::endl;
dump("", msg);
msg << "The node you were trying to add:" << std::endl;
a.dump(msg);
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(msg.str());
Martin Christoph Hierholzer
committed
}
// force value type, engineering unit and description of the network if set in this feeding node
Martin Christoph Hierholzer
committed
if(a.getValueType() != typeid(AnyType)) valueType = &(a.getValueType());
Martin Christoph Hierholzer
committed
if(a.getUnit() != mtca4u::TransferElement::unitNotSet) engineeringUnit = a.getUnit();
if(a.getDescription() != "") description = a.getDescription();
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());
Martin Christoph Hierholzer
committed
if(engineeringUnit == mtca4u::TransferElement::unitNotSet) engineeringUnit = a.getUnit();
if(description == "") description = a.getDescription();
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
// add node to node list
nodeList.push_back(a);
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
void VariableNetwork::removeNode(const VariableNetworkNode &a) {
auto nNodes = nodeList.size();
nodeList.remove(a);
// check if a node was actually removed, if not, throw exception
if(nodeList.size() == nNodes) {
std::stringstream msg;
msg << "Trying remove a VariableNetworkNode from the VariableNetwork which is not in the network." << std::endl;
msg << "The network you were trying to remove the node from:" << std::endl;
dump("", msg);
msg << "The node you were trying to remove:" << std::endl;
a.dump(msg);
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalParameter>(msg.str());
}
}
/*********************************************************************************************************************/
void VariableNetwork::removeNodeToTrigger(const VariableNetworkNode &nodeToNoLongerTrigger) {
for(auto &node : nodeList) {
if(node.getType() != NodeType::TriggerReceiver) continue;
if(node.getNodeToTrigger() == nodeToNoLongerTrigger) {
removeNode(node);
break;
}
}
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
void VariableNetwork::dump(const std::string& linePrefix, std::ostream& stream) const {
stream << linePrefix << "VariableNetwork";
stream << " [ptr: " << this << "]";
stream << " {" << std::endl;
stream << linePrefix << " value type = " << valueType->name() << ", engineering unit = " << engineeringUnit << std::endl;
stream << linePrefix << " trigger type = ";
Martin Christoph Hierholzer
committed
try {
TriggerType tt = getTriggerType(false);
Martin Christoph Hierholzer
committed
if(tt == TriggerType::feeder) stream << "feeder" << std::endl;
if(tt == TriggerType::pollingConsumer) stream << "pollingConsumer" << std::endl;
if(tt == TriggerType::external) stream << "external" << std::endl;
if(tt == TriggerType::none) stream << "none" << std::endl;
Martin Christoph Hierholzer
committed
}
catch(ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork> &e) {
Martin Christoph Hierholzer
committed
stream << "**error**" << std::endl;
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
stream << linePrefix << " feeder";
Martin Christoph Hierholzer
committed
if(hasFeedingNode()) {
Martin Christoph Hierholzer
committed
getFeedingNode().dump(stream);
Martin Christoph Hierholzer
committed
}
else {
Martin Christoph Hierholzer
committed
stream << " **error, no feeder found**" << std::endl;
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
stream << 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
stream << linePrefix << " # " << ++count << ":";
Martin Christoph Hierholzer
committed
consumer.dump(stream);
Martin Christoph Hierholzer
committed
}
if(hasFeedingNode()) {
if(getFeedingNode().hasExternalTrigger()) {
stream << linePrefix << " external trigger node: ";
getFeedingNode().getExternalTrigger().dump(stream);
}
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
stream << linePrefix << "}" << std::endl;
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
void VariableNetwork::addNodeToTrigger(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
/*********************************************************************************************************************/
VariableNetwork::TriggerType VariableNetwork::getTriggerType(bool verboseExceptions) const {
if(!hasFeedingNode()) return TriggerType::none;
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
std::stringstream msg;
Martin Christoph Hierholzer
committed
msg << "Providing an external trigger to a variable network which is fed by a pushing variable is not allowed." << std::endl;
if(verboseExceptions) {
msg << "The illegal network:" << std::endl;
dump("", msg);
}
Martin Christoph Hierholzer
committed
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(msg.str());
Martin Christoph Hierholzer
committed
}
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(),
Martin Christoph Hierholzer
committed
[](const VariableNetworkNode &n) {
Martin Christoph Hierholzer
committed
return n.getDirection() == VariableDirection::consuming && n.getMode() == UpdateMode::poll;
} );
Martin Christoph Hierholzer
committed
if(nPollingConsumers != 1) {
Martin Christoph Hierholzer
committed
std::stringstream msg;
msg << "In a network with a poll-type feeder and no external trigger, there must be exactly one polling consumer. Maybe you forgot to add a trigger?" << std::endl;
if(verboseExceptions) {
msg << "The illegal network:" << std::endl;
dump("", msg);
}
Martin Christoph Hierholzer
committed
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(msg.str());
Martin Christoph Hierholzer
committed
}
return TriggerType::pollingConsumer;
}
/*********************************************************************************************************************/
void VariableNetwork::check() {
// must have consuming nodes
if(countConsumingNodes() == 0) {
std::stringstream msg;
msg << "No consuming nodes connected to this network!" << std::endl;
msg << "The illegal network:" << std::endl;
dump("", msg);
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(msg.str());
Martin Christoph Hierholzer
committed
}
// must have a feeding node
if(!hasFeedingNode()) {
std::stringstream msg;
msg << "No feeding node connected to this network!" << std::endl;
msg << "The illegal network:" << std::endl;
dump("", msg);
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(msg.str());
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
// the network's value type must be correctly set
if(*valueType == typeid(AnyType)) {
std::stringstream msg;
msg << "No data type specified for any of the nodes in this network!" << std::endl;
msg << "The illegal network:" << std::endl;
dump("", msg);
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(msg.str());
Martin Christoph Hierholzer
committed
}
// the feeder node must have a non-zero length
size_t length = getFeedingNode().getNumberOfElements();
if(length == 0) {
std::stringstream msg;
msg << "The feeding node has zero (or undefined) length!" << std::endl;
msg << "The illegal network:" << std::endl;
dump("", msg);
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(msg.str());
}
// all consumers must have the same length as the feeder or a zero length for trigger receivers
for(auto &node : nodeList) {
if(node.getType() != NodeType::TriggerReceiver) {
if(node.getNumberOfElements() != length) {
std::stringstream msg;
msg << "The network contains a node with a different length than the feeding node!" << std::endl;
msg << "The illegal network:" << std::endl;
dump("", msg);
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(msg.str());
}
}
else {
if(node.getNumberOfElements() != 0) {
std::stringstream msg;
msg << "The network contains a trigger receiver node with a non-zero length!" << std::endl;
msg << "The illegal network:" << std::endl;
dump("", msg);
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(msg.str());
}
}
}
Martin Christoph Hierholzer
committed
// 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);
Martin Christoph Hierholzer
committed
if(node.getValueType() != *valueType) {
std::stringstream msg;
msg << "The network contains variables of different value types, which is not supported!" << std::endl;
msg << "The illegal network:" << std::endl;
dump("", msg);
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(msg.str());
}
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()) {
std::stringstream msg;
msg << "No feeding node in this network!" << std::endl;
msg << "The illegal network:" << std::endl;
if(!hasFeedingNode()) dump("", msg);
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(msg.str());
Martin Christoph Hierholzer
committed
}
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;
}
/*********************************************************************************************************************/
bool VariableNetwork::hasApplicationConsumer() const {
for(auto &n : nodeList) {
if(n.getDirection() == VariableDirection::consuming && n.getType() == NodeType::Application) return true;
}
return false;
}