Skip to content
Snippets Groups Projects
Commit 8eeca818 authored by Martin Christoph Hierholzer's avatar Martin Christoph Hierholzer
Browse files

add return channel to FeedingFanOut (and enable test for it)

parent 22a3cf41
No related branches found
No related tags found
No related merge requests found
...@@ -23,9 +23,10 @@ namespace ChimeraTK { ...@@ -23,9 +23,10 @@ namespace ChimeraTK {
public: public:
FeedingFanOut(std::string const &name, std::string const &unit, std::string const &description, FeedingFanOut(std::string const &name, std::string const &unit, std::string const &description,
size_t numberOfElements) size_t numberOfElements, bool withReturn)
: FanOut<UserType>(boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>>()), : FanOut<UserType>(boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>>()),
ChimeraTK::NDRegisterAccessor<UserType>(name, unit, description) ChimeraTK::NDRegisterAccessor<UserType>("FeedingFanOut:"+name, unit, description),
_withReturn(withReturn)
{ {
ChimeraTK::NDRegisterAccessor<UserType>::buffer_2D.resize(1); ChimeraTK::NDRegisterAccessor<UserType>::buffer_2D.resize(1);
ChimeraTK::NDRegisterAccessor<UserType>::buffer_2D[0].resize(numberOfElements); ChimeraTK::NDRegisterAccessor<UserType>::buffer_2D[0].resize(numberOfElements);
...@@ -33,9 +34,7 @@ namespace ChimeraTK { ...@@ -33,9 +34,7 @@ namespace ChimeraTK {
/** Add a slave to the FanOut. Only sending end-points of a consuming node may be added. */ /** Add a slave to the FanOut. Only sending end-points of a consuming node may be added. */
void addSlave(boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>> slave) override { void addSlave(boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>> slave) override {
if(!slave->isWriteable()) {
throw ChimeraTK::logic_error("FeedingFanOut::addSlave() has been called with a receiving implementation!");
}
// check if array shape is compatible, unless the receiver is a trigger node, so no data is expected // check if array shape is compatible, unless the receiver is a trigger node, so no data is expected
if( slave->getNumberOfSamples() != 0 && if( slave->getNumberOfSamples() != 0 &&
( slave->getNumberOfChannels() != 1 || slave->getNumberOfSamples() != this->getNumberOfSamples() ) ) { ( slave->getNumberOfChannels() != 1 || slave->getNumberOfSamples() != this->getNumberOfSamples() ) ) {
...@@ -43,11 +42,35 @@ namespace ChimeraTK { ...@@ -43,11 +42,35 @@ namespace ChimeraTK {
what += "' with incompatible array shape! Name of fan out: '" + this->getName() + "'"; what += "' with incompatible array shape! Name of fan out: '" + this->getName() + "'";
throw ChimeraTK::logic_error(what.c_str()); throw ChimeraTK::logic_error(what.c_str());
} }
// make sure slave is writeable
if(!slave->isWriteable()) {
throw ChimeraTK::logic_error("FeedingFanOut::addSlave() has been called with a receiving implementation!");
}
// handle return channels
if(_withReturn) {
if(slave->isReadable()) {
if(_hasReturnSlave) {
throw ChimeraTK::logic_error("FeedingFanOut: Cannot add multiple slaves with return channel!");
}
_hasReturnSlave = true;
_returnSlave = slave;
}
}
else {
if(slave->isReadable()) {
throw ChimeraTK::logic_error("FeedingFanOut: Cannot add slaves with return channel to FeedingFanOuts "
"without return channel!");
}
}
// add the slave
FanOut<UserType>::slaves.push_back(slave); FanOut<UserType>::slaves.push_back(slave);
} }
bool isReadable() const override { bool isReadable() const override {
return false; return _withReturn;
} }
bool isReadOnly() const override { bool isReadOnly() const override {
...@@ -59,27 +82,45 @@ namespace ChimeraTK { ...@@ -59,27 +82,45 @@ namespace ChimeraTK {
} }
void doReadTransfer() override { void doReadTransfer() override {
throw ChimeraTK::logic_error("Read operation called on write-only variable."); if(!_withReturn) throw ChimeraTK::logic_error("Read operation called on write-only variable.");
_returnSlave->doReadTransfer();
} }
bool doReadTransferNonBlocking() override { bool doReadTransferNonBlocking() override {
throw ChimeraTK::logic_error("Read operation called on write-only variable."); if(!_withReturn) throw ChimeraTK::logic_error("Read operation called on write-only variable.");
return _returnSlave->doReadTransferNonBlocking();
} }
bool doReadTransferLatest() override { bool doReadTransferLatest() override {
throw ChimeraTK::logic_error("Read operation called on write-only variable."); if(!_withReturn) throw ChimeraTK::logic_error("Read operation called on write-only variable.");
return _returnSlave->doReadTransferLatest();
} }
void doPreRead() override { void doPreRead() override {
throw ChimeraTK::logic_error("Read operation called on write-only variable."); if(!_withReturn) throw ChimeraTK::logic_error("Read operation called on write-only variable.");
_returnSlave->accessChannel(0).swap(ChimeraTK::NDRegisterAccessor<UserType>::buffer_2D[0]);
_returnSlave->preRead();
} }
void doPostRead() override { void doPostRead() override {
throw ChimeraTK::logic_error("Read operation called on write-only variable."); if(!_withReturn) throw ChimeraTK::logic_error("Read operation called on write-only variable.");
assert(_hasReturnSlave);
_returnSlave->postRead();
_returnSlave->accessChannel(0).swap(ChimeraTK::NDRegisterAccessor<UserType>::buffer_2D[0]);
// distribute return-channel update to the other slaves
for(auto &slave : FanOut<UserType>::slaves) { // send out copies to slaves
if(slave == _returnSlave) continue;
if(slave->getNumberOfSamples() != 0) { // do not send copy if no data is expected (e.g. trigger)
slave->accessChannel(0) = ChimeraTK::NDRegisterAccessor<UserType>::buffer_2D[0];
}
slave->write(_returnSlave->getVersionNumber());
}
} }
ChimeraTK::TransferFuture doReadTransferAsync() override { ChimeraTK::TransferFuture doReadTransferAsync() override {
throw ChimeraTK::logic_error("Read operation called on write-only variable."); if(!_withReturn) throw ChimeraTK::logic_error("Read operation called on write-only variable.");
return {_returnSlave->doReadTransferAsync(), this};
} }
void doPreWrite() override { void doPreWrite() override {
...@@ -133,10 +174,26 @@ namespace ChimeraTK { ...@@ -133,10 +174,26 @@ namespace ChimeraTK {
/// @todo implement properly? /// @todo implement properly?
} }
AccessModeFlags getAccessModeFlags() const override { return {}; } AccessModeFlags getAccessModeFlags() const override { return {AccessMode::wait_for_new_data}; }
VersionNumber getVersionNumber() const override { return FanOut<UserType>::slaves.front()->getVersionNumber(); } VersionNumber getVersionNumber() const override { return FanOut<UserType>::slaves.front()->getVersionNumber(); }
boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>> getReturnSlave() {
return _returnSlave;
}
protected:
/// Flag whether this FeedingFanOut has a return channel. Is specified in the constructor
bool _withReturn;
/// Used if _withReturn is true: flag whether the corresponding slave with the return channel has already been
/// added.
bool _hasReturnSlave{false};
/// The slave with return channel
boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>> _returnSlave;
}; };
} /* namespace ChimeraTK */ } /* namespace ChimeraTK */
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <ChimeraTK/NDRegisterAccessorDecorator.h> #include <ChimeraTK/NDRegisterAccessorDecorator.h>
#include "Application.h" #include "Application.h"
#include "FeedingFanOut.h"
namespace ChimeraTK { namespace ChimeraTK {
...@@ -37,7 +38,9 @@ namespace ChimeraTK { ...@@ -37,7 +38,9 @@ namespace ChimeraTK {
// if this decorating a bidirectional process variable, set the valueRejectCallback // if this decorating a bidirectional process variable, set the valueRejectCallback
auto bidir = boost::dynamic_pointer_cast<BidirectionalProcessArray<UserType>>(accessor); auto bidir = boost::dynamic_pointer_cast<BidirectionalProcessArray<UserType>>(accessor);
if(bidir) { if(bidir) {
bidir->setValueRejectCallback([this]{decrementCounter();}); bidir->setValueRejectCallback([this]{
decrementCounter();
});
} }
} }
......
...@@ -54,7 +54,7 @@ namespace ChimeraTK { ...@@ -54,7 +54,7 @@ namespace ChimeraTK {
assert(feedingNode.get() != nullptr); assert(feedingNode.get() != nullptr);
transferGroup.addAccessor(feedingNode); transferGroup.addAccessor(feedingNode);
auto feedingFanOut = boost::make_shared<FeedingFanOut<UserType>>( feedingNode->getName(), feedingNode->getUnit(), auto feedingFanOut = boost::make_shared<FeedingFanOut<UserType>>( feedingNode->getName(), feedingNode->getUnit(),
feedingNode->getDescription(), feedingNode->getNumberOfSamples() ); feedingNode->getDescription(), feedingNode->getNumberOfSamples(), false ); // in TriggerFanOuts we cannot have return channels
boost::fusion::at_key<UserType>(fanOutMap.table)[feedingNode] = feedingFanOut; boost::fusion::at_key<UserType>(fanOutMap.table)[feedingNode] = feedingFanOut;
return feedingFanOut; return feedingFanOut;
} }
......
...@@ -101,12 +101,9 @@ BOOST_AUTO_TEST_CASE(testNormalOperation) { ...@@ -101,12 +101,9 @@ BOOST_AUTO_TEST_CASE(testNormalOperation) {
TestApplication app; TestApplication app;
//app.a.connectTo(app.cs); // the connections will result in a FeedingFanOut for var2, as it is connected to the control system as well
//app.b.connectTo(app.cs); app.a.connectTo(app.cs);
app.cs("var1") >> app.a.var1; app.b.connectTo(app.cs);
app.a.var2 >> app.b.var2;
app.cs("max") >> app.b.max;
app.cs("var3") >> app.b.var3;
ctk::TestFacility test; ctk::TestFacility test;
app.initialise(); app.initialise();
app.run(); app.run();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment