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::hasAppNode(AccessorBase *a, AccessorBase *b) const {
Martin Christoph Hierholzer
committed
if(feeder.getType() == NodeType::Application) {
if(a == &(feeder.getAppAccessor()) || (b != nullptr && b == &(feeder.getAppAccessor())) ) return true;
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
// search for a and b in the inputAccessorList
Martin Christoph Hierholzer
committed
size_t c = count_if( consumerList.begin(), consumerList.end(),
Martin Christoph Hierholzer
committed
[a,b](const VariableNetworkNode n) {
Martin Christoph Hierholzer
committed
if(n.getType() != NodeType::Application) return false;
return a == &(n.getAppAccessor()) || ( b != nullptr && b == &(n.getAppAccessor()) );
Martin Christoph Hierholzer
committed
} );
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
if(c > 0) return true;
Martin Christoph Hierholzer
committed
return false;
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
bool VariableNetwork::hasFeedingNode() const {
Martin Christoph Hierholzer
committed
if(feeder.getType() == NodeType::invalid) return false;
Martin Christoph Hierholzer
committed
return true;
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
size_t VariableNetwork::countConsumingNodes() const {
return consumerList.size();
}
/*********************************************************************************************************************/
size_t VariableNetwork::countFixedImplementations() const {
Martin Christoph Hierholzer
committed
size_t count = 0;
Martin Christoph Hierholzer
committed
if(feeder.hasImplementation()) count++;
count += count_if( consumerList.begin(), consumerList.end(),
Martin Christoph Hierholzer
committed
[](const VariableNetworkNode n) {
Martin Christoph Hierholzer
committed
return n.hasImplementation();
} );
Martin Christoph Hierholzer
committed
return count;
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
void VariableNetwork::addAppNode(AccessorBase &a) {
if(hasAppNode(&a)) return; // already in the network
Martin Christoph Hierholzer
committed
// create Node structure
Martin Christoph Hierholzer
committed
VariableNetworkNode node(a);
node.setOwner(this);
Martin Christoph Hierholzer
committed
// if node is feeding, save as feeder for this network
Martin Christoph Hierholzer
committed
if(a.isFeeding()) {
Martin Christoph Hierholzer
committed
// make sure we only have one feeding node per network
Martin Christoph Hierholzer
committed
if(hasFeedingNode()) {
Martin Christoph Hierholzer
committed
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(
"Trying to add a feeding accessor to a network already having a feeding accessor.");
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
// update value type
Martin Christoph Hierholzer
committed
valueType = &(a.getValueType());
Martin Christoph Hierholzer
committed
// update engineering unit
engineeringUnit = a.getUnit();
Martin Christoph Hierholzer
committed
// update feeder
feeder = node;
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
// not is not feeding, add it to list of consumers
Martin Christoph Hierholzer
committed
else {
Martin Christoph Hierholzer
committed
// add node to consumer list
Martin Christoph Hierholzer
committed
consumerList.push_back(node);
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
//auto &list = Application::getInstance().nodeList;
//list.erase(std::find(list.begin(), list.end(), a));
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.");
}
// update value type
valueType = &(a.getValueType());
// update engineering unit
engineeringUnit = a.getUnit();
// update feeder
feeder = a;
}
// not is not feeding, add it to list of consumers
else {
// add node to consumer list
consumerList.push_back(a);
}
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
void VariableNetwork::addConsumingPublication(const std::string& name) {
Martin Christoph Hierholzer
committed
VariableNetworkNode node(name, VariableDirection::consuming);
Martin Christoph Hierholzer
committed
node.setOwner(this);
Martin Christoph Hierholzer
committed
consumerList.push_back(node);
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
void VariableNetwork::addFeedingPublication(AccessorBase &a, const std::string& name) {
Martin Christoph Hierholzer
committed
addFeedingPublication(a.getValueType(), a.getUnit(), name);
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
void VariableNetwork::addFeedingPublication(const std::type_info &typeInfo, const std::string& unit, const std::string& name) {
Martin Christoph Hierholzer
committed
if(hasFeedingNode()) {
Martin Christoph Hierholzer
committed
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(
"Trying to add control-system-to-device publication to a network already having a feeding accessor.");
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
feeder = VariableNetworkNode(name, VariableDirection::feeding);
Martin Christoph Hierholzer
committed
feeder.setOwner(this);
Martin Christoph Hierholzer
committed
valueType = &typeInfo;
Martin Christoph Hierholzer
committed
engineeringUnit = unit;
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
void VariableNetwork::addConsumingDeviceRegister(const std::string &deviceAlias, const std::string ®isterName) {
Martin Christoph Hierholzer
committed
VariableNetworkNode node(deviceAlias, registerName, UpdateMode::push, VariableDirection::consuming);
Martin Christoph Hierholzer
committed
node.setOwner(this);
Martin Christoph Hierholzer
committed
consumerList.push_back(node);
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
void VariableNetwork::addFeedingDeviceRegister(AccessorBase &a, const std::string &deviceAlias,
Martin Christoph Hierholzer
committed
const std::string ®isterName, UpdateMode mode) {
Martin Christoph Hierholzer
committed
addFeedingDeviceRegister(a.getValueType(), a.getUnit(), deviceAlias, registerName, mode);
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
void VariableNetwork::addFeedingDeviceRegister(const std::type_info &typeInfo, const std::string& unit
, const std::string &deviceAlias, const std::string ®isterName, UpdateMode mode) {
Martin Christoph Hierholzer
committed
if(hasFeedingNode()) {
Martin Christoph Hierholzer
committed
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(
"Trying to add a feeding device register to a network already having a feeding accessor.");
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
feeder = VariableNetworkNode(deviceAlias, registerName, mode, VariableDirection::feeding);
Martin Christoph Hierholzer
committed
feeder.setOwner(this);
Martin Christoph Hierholzer
committed
valueType = &typeInfo;
Martin Christoph Hierholzer
committed
engineeringUnit = unit;
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
feeder.dump();
Martin Christoph Hierholzer
committed
std::cout << linePrefix << " consumers: " << consumerList.size() << std::endl;
Martin Christoph Hierholzer
committed
size_t count = 0;
for(auto &consumer : consumerList) {
Martin Christoph Hierholzer
committed
std::cout << linePrefix << " # " << ++count << ":";
Martin Christoph Hierholzer
committed
consumer.dump();
}
Martin Christoph Hierholzer
committed
if(hasExternalTrigger) {
std::cout << linePrefix << " external trigger network:" << std::endl;;
assert(externalTrigger != nullptr);
externalTrigger->dump(" ");
}
std::cout << linePrefix << "}" << std::endl;
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
/*********************************************************************************************************************/
void VariableNetwork::addTriggerReceiver(VariableNetwork *network) {
Martin Christoph Hierholzer
committed
VariableNetworkNode node(network);
node.setOwner(this);
Martin Christoph Hierholzer
committed
consumerList.push_back(node);
}
/*********************************************************************************************************************/
void VariableNetwork::addTrigger(AccessorBase &trigger) {
if(hasExternalTrigger) {
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(
"Only one external trigger per variable network is allowed.");
}
// find the network the triggering accessor is connected to
VariableNetwork &otherNetwork = Application::getInstance().findOrCreateNetwork(&trigger);
otherNetwork.addAppNode(trigger);
Martin Christoph Hierholzer
committed
// add the found network as trigger
addTrigger(otherNetwork);
}
/*********************************************************************************************************************/
void VariableNetwork::addTrigger(VariableNetwork &trigger) {
if(hasExternalTrigger) {
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(
"Only one external trigger per variable network is allowed.");
}
Martin Christoph Hierholzer
committed
// add ourselves as a trigger receiver to the other network
Martin Christoph Hierholzer
committed
trigger.addTriggerReceiver(this);
Martin Christoph Hierholzer
committed
// set flag and store pointer to other network
hasExternalTrigger = true;
Martin Christoph Hierholzer
committed
externalTrigger = &trigger;
Martin Christoph Hierholzer
committed
}
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
VariableNetwork::TriggerType VariableNetwork::getTriggerType() const {
Martin Christoph Hierholzer
committed
// network has an external trigger
if(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
size_t nPollingConsumers = count_if( consumerList.begin(), consumerList.end(),
Martin Christoph Hierholzer
committed
[](const VariableNetworkNode n) { return 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>(
"Illegal variable network found: no consuming nodes connected!");
}
// must have a feeding node
if(!hasFeedingNode()) {
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalVariableNetwork>(
"Illegal variable network found: no feeding node connected!");
}
Martin Christoph Hierholzer
committed
// all nodes must have this network as the owner
Martin Christoph Hierholzer
committed
assert(&(feeder.getOwner()) == this);
Martin Christoph Hierholzer
committed
for(auto &consumer : consumerList) {
Martin Christoph Hierholzer
committed
assert(&(consumer.getOwner()) == this);
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(feeder.getType() == NodeType::Application) {
assert(feeder.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();
}
/*********************************************************************************************************************/
VariableNetwork& VariableNetwork::getExternalTrigger() {
if(getTriggerType() != TriggerType::external) {
throw ApplicationExceptionWithID<ApplicationExceptionID::illegalParameter>(
"VariableNetwork::getExternalTrigger() may only be called if the trigger type is external.");
}
return *externalTrigger;
}