/*
- * Copyright 2006,2007 Free Software Foundation, Inc.
+ * Copyright 2006,2007,2009 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
gr_hier_block2_detail::gr_hier_block2_detail(gr_hier_block2 *owner) :
d_owner(owner),
d_parent_detail(0),
- d_fg(gr_make_flowgraph()),
- d_inputs(owner->input_signature()->max_streams()),
- d_outputs(owner->output_signature()->max_streams())
+ d_fg(gr_make_flowgraph())
{
+ int min_inputs = owner->input_signature()->min_streams();
+ int max_inputs = owner->input_signature()->max_streams();
+ int min_outputs = owner->output_signature()->min_streams();
+ int max_outputs = owner->output_signature()->max_streams();
+
+ if (max_inputs == gr_io_signature::IO_INFINITE ||
+ max_outputs == gr_io_signature::IO_INFINITE ||
+ (min_inputs != max_inputs) ||(min_outputs != max_outputs) ) {
+ std::stringstream msg;
+ msg << "Hierarchical blocks do not yet support arbitrary or"
+ << " variable numbers of inputs or outputs (" << d_owner->name() << ")";
+ throw std::runtime_error(msg.str());
+ }
+
+ d_inputs = std::vector<gr_endpoint_vector_t>(max_inputs);
+ d_outputs = gr_endpoint_vector_t(max_outputs);
}
gr_hier_block2_detail::~gr_hier_block2_detail()
d_owner = 0; // Don't use delete, we didn't allocate
}
+void
+gr_hier_block2_detail::connect(gr_basic_block_sptr block)
+{
+ std::stringstream msg;
+
+ // Check if duplicate
+ if (std::find(d_blocks.begin(), d_blocks.end(), block) != d_blocks.end()) {
+ msg << "Block " << block << " already connected.";
+ throw std::invalid_argument(msg.str());
+ }
+
+ // Check if has inputs or outputs
+ if (block->input_signature()->max_streams() != 0 ||
+ block->output_signature()->max_streams() != 0) {
+ msg << "Block " << block << " must not have any input or output ports";
+ throw std::invalid_argument(msg.str());
+ }
+
+ gr_hier_block2_sptr hblock(cast_to_hier_block2_sptr(block));
+
+ if (hblock && hblock.get() != d_owner) {
+ if (GR_HIER_BLOCK2_DETAIL_DEBUG)
+ std::cout << "connect: block is hierarchical, setting parent to " << this << std::endl;
+ hblock->d_detail->d_parent_detail = this;
+ }
+
+ d_blocks.push_back(block);
+}
+
void
gr_hier_block2_detail::connect(gr_basic_block_sptr src, int src_port,
gr_basic_block_sptr dst, int dst_port)
if (src.get() == dst.get())
throw std::invalid_argument("connect: src and destination blocks cannot be the same");
- gr_hier_block2_sptr src_block(boost::dynamic_pointer_cast<gr_hier_block2, gr_basic_block>(src));
- gr_hier_block2_sptr dst_block(boost::dynamic_pointer_cast<gr_hier_block2, gr_basic_block>(dst));
+ gr_hier_block2_sptr src_block(cast_to_hier_block2_sptr(src));
+ gr_hier_block2_sptr dst_block(cast_to_hier_block2_sptr(dst));
if (src_block && src.get() != d_owner) {
if (GR_HIER_BLOCK2_DETAIL_DEBUG)
// TODO: connects to NC
}
+void
+gr_hier_block2_detail::disconnect(gr_basic_block_sptr block)
+{
+ // Check on singleton list
+ for (gr_basic_block_viter_t p = d_blocks.begin(); p != d_blocks.end(); p++) {
+ if (*p == block) {
+ d_blocks.erase(p);
+
+ gr_hier_block2_sptr hblock(cast_to_hier_block2_sptr(block));
+ if (block && block.get() != d_owner) {
+ if (GR_HIER_BLOCK2_DETAIL_DEBUG)
+ std::cout << "disconnect: block is hierarchical, clearing parent" << std::endl;
+ hblock->d_detail->d_parent_detail = 0;
+ }
+
+ return;
+ }
+ }
+
+ // Otherwise find all edges containing block
+ gr_edge_vector_t edges, tmp = d_fg->edges();
+ gr_edge_vector_t::iterator p;
+ for (p = tmp.begin(); p != tmp.end(); p++) {
+ if ((*p).src().block() == block || (*p).dst().block() == block) {
+ edges.push_back(*p);
+
+ if (GR_HIER_BLOCK2_DETAIL_DEBUG)
+ std::cout << "disconnect: block found in edge " << (*p) << std::endl;
+ }
+ }
+
+ if (edges.size() == 0) {
+ std::stringstream msg;
+ msg << "cannot disconnect block " << block << ", not found";
+ throw std::invalid_argument(msg.str());
+ }
+
+ for (p = edges.begin(); p != edges.end(); p++) {
+ disconnect((*p).src().block(), (*p).src().port(),
+ (*p).dst().block(), (*p).dst().port());
+ }
+}
+
void
gr_hier_block2_detail::disconnect(gr_basic_block_sptr src, int src_port,
gr_basic_block_sptr dst, int dst_port)
if (src.get() == dst.get())
throw std::invalid_argument("disconnect: source and destination blocks cannot be the same");
- gr_hier_block2_sptr src_block(boost::dynamic_pointer_cast<gr_hier_block2, gr_basic_block>(src));
- gr_hier_block2_sptr dst_block(boost::dynamic_pointer_cast<gr_hier_block2, gr_basic_block>(dst));
+ gr_hier_block2_sptr src_block(cast_to_hier_block2_sptr(src));
+ gr_hier_block2_sptr dst_block(cast_to_hier_block2_sptr(dst));
if (src_block && src.get() != d_owner) {
if (GR_HIER_BLOCK2_DETAIL_DEBUG)
- std::cout << "connect: src is hierarchical, clearing parent" << std::endl;
+ std::cout << "disconnect: src is hierarchical, clearing parent" << std::endl;
src_block->d_detail->d_parent_detail = 0;
}
if (dst_block && dst.get() != d_owner) {
if (GR_HIER_BLOCK2_DETAIL_DEBUG)
- std::cout << "connect: dst is hierarchical, clearing parent" << std::endl;
+ std::cout << "disconnect: dst is hierarchical, clearing parent" << std::endl;
dst_block->d_detail->d_parent_detail = 0;
}
d_fg->disconnect(src, src_port, dst, dst_port);
}
-// FIXME: ticket:161 will be implemented here
void
gr_hier_block2_detail::connect_input(int my_port, int port, gr_basic_block_sptr block)
{
throw std::invalid_argument(msg.str());
}
- if (d_inputs[my_port].block()) {
- msg << "external input port " << my_port << " already wired to "
- << d_inputs[my_port];
+ gr_endpoint_vector_t &endps = d_inputs[my_port];
+ gr_endpoint endp(block, port);
+
+ gr_endpoint_viter_t p = std::find(endps.begin(), endps.end(), endp);
+ if (p != endps.end()) {
+ msg << "external input port " << my_port << " already wired to " << endp;
throw std::invalid_argument(msg.str());
}
-
- d_inputs[my_port] = gr_endpoint(block, port);
+
+ endps.push_back(endp);
}
void
throw std::invalid_argument(msg.str());
}
- if (d_inputs[my_port].block() != block) {
- msg << "block " << block << " not assigned to input "
- << my_port << ", can't disconnect";
+ gr_endpoint_vector_t &endps = d_inputs[my_port];
+ gr_endpoint endp(block, port);
+
+ gr_endpoint_viter_t p = std::find(endps.begin(), endps.end(), endp);
+ if (p == endps.end()) {
+ msg << "external input port " << my_port << " not connected to " << endp;
throw std::invalid_argument(msg.str());
}
-
- d_inputs[my_port] = gr_endpoint();
+
+ endps.erase(p);
}
void
d_outputs[my_port] = gr_endpoint();
}
-gr_endpoint
+gr_endpoint_vector_t
gr_hier_block2_detail::resolve_port(int port, bool is_input)
{
std::stringstream msg;
<< (is_input ? "input" : "output")
<< " of " << d_owner->name() << std::endl;
- gr_endpoint result;
+ gr_endpoint_vector_t result;
if (is_input) {
if (port < 0 || port >= (signed)d_inputs.size()) {
throw std::runtime_error(msg.str());
}
- result = resolve_endpoint(d_inputs[port], true);
+ if (d_inputs[port].empty()) {
+ msg << "hierarchical block '" << d_owner->name() << "' input " << port
+ << " is not connected internally";
+ throw std::runtime_error(msg.str());
+ }
+
+ gr_endpoint_vector_t &endps = d_inputs[port];
+ gr_endpoint_viter_t p;
+ for (p = endps.begin(); p != endps.end(); p++) {
+ gr_endpoint_vector_t tmp = resolve_endpoint(*p, true);
+ std::copy(tmp.begin(), tmp.end(), back_inserter(result));
+ }
}
else {
if (port < 0 || port >= (signed)d_outputs.size()) {
throw std::runtime_error(msg.str());
}
+ if (d_outputs[port] == gr_endpoint()) {
+ msg << "hierarchical block '" << d_owner->name() << "' output " << port
+ << " is not connected internally";
+ throw std::runtime_error(msg.str());
+ }
+
result = resolve_endpoint(d_outputs[port], false);
}
- if (!result.block()) {
+ if (result.empty()) {
msg << "unable to resolve "
<< (is_input ? "input port " : "output port ")
<< port;
return result;
}
-gr_endpoint
+void
+gr_hier_block2_detail::disconnect_all()
+{
+ d_fg->clear();
+ d_blocks.clear();
+ d_inputs.clear();
+ d_outputs.clear();
+}
+
+gr_endpoint_vector_t
gr_hier_block2_detail::resolve_endpoint(const gr_endpoint &endp, bool is_input) const
{
std::stringstream msg;
+ gr_endpoint_vector_t result;
// Check if endpoint is a leaf node
- if (boost::dynamic_pointer_cast<gr_block, gr_basic_block>(endp.block()))
- return endp;
-
+ if (cast_to_block_sptr(endp.block())) {
+ if (GR_HIER_BLOCK2_DETAIL_DEBUG)
+ std::cout << "Block " << endp.block() << " is a leaf node, returning." << std::endl;
+ result.push_back(endp);
+ return result;
+ }
+
// Check if endpoint is a hierarchical block
- gr_hier_block2_sptr hier_block2(boost::dynamic_pointer_cast<gr_hier_block2, gr_basic_block>(endp.block()));
+ gr_hier_block2_sptr hier_block2(cast_to_hier_block2_sptr(endp.block()));
if (hier_block2) {
if (GR_HIER_BLOCK2_DETAIL_DEBUG)
std::cout << "Resolving endpoint " << endp << " as an "
gr_hier_block2_detail::flatten_aux(gr_flat_flowgraph_sptr sfg) const
{
if (GR_HIER_BLOCK2_DETAIL_DEBUG)
- std::cout << "flattening " << d_owner->name() << std::endl;
+ std::cout << "Flattening " << d_owner->name() << std::endl;
// Add my edges to the flow graph, resolving references to actual endpoints
gr_edge_vector_t edges = d_fg->edges();
+ gr_edge_viter_t p;
- for (gr_edge_viter_t p = edges.begin(); p != edges.end(); p++) {
+ for (p = edges.begin(); p != edges.end(); p++) {
if (GR_HIER_BLOCK2_DETAIL_DEBUG)
std::cout << "Flattening edge " << (*p) << std::endl;
- gr_endpoint src_endp = resolve_endpoint(p->src(), false);
- gr_endpoint dst_endp = resolve_endpoint(p->dst(), true);
- sfg->connect(src_endp, dst_endp);
+ gr_endpoint_vector_t src_endps = resolve_endpoint(p->src(), false);
+ gr_endpoint_vector_t dst_endps = resolve_endpoint(p->dst(), true);
+
+ gr_endpoint_viter_t s, d;
+ for (s = src_endps.begin(); s != src_endps.end(); s++) {
+ for (d = dst_endps.begin(); d != dst_endps.end(); d++) {
+ if (GR_HIER_BLOCK2_DETAIL_DEBUG)
+ std::cout << (*s) << "->" << (*d) << std::endl;
+ sfg->connect(*s, *d);
+ }
+ }
}
- gr_basic_block_vector_t blocks = d_fg->calc_used_blocks();
+ // Construct unique list of blocks used either in edges, inputs,
+ // outputs, or by themselves. I still hate STL.
+ gr_basic_block_vector_t blocks; // unique list of used blocks
+ gr_basic_block_vector_t tmp = d_fg->calc_used_blocks();
+
+ // First add the list of singleton blocks
+ std::vector<gr_basic_block_sptr>::const_iterator b; // Because flatten_aux is const
+ for (b = d_blocks.begin(); b != d_blocks.end(); b++)
+ tmp.push_back(*b);
+
+ // Now add the list of connected input blocks
+ std::stringstream msg;
+ for (unsigned int i = 0; i < d_inputs.size(); i++) {
+ if (d_inputs[i].size() == 0) {
+ msg << "In hierarchical block " << d_owner->name() << ", input " << i
+ << " is not connected internally";
+ throw std::runtime_error(msg.str());
+ }
+
+ for (unsigned int j = 0; j < d_inputs[i].size(); j++)
+ tmp.push_back(d_inputs[i][j].block());
+ }
+
+ for (unsigned int i = 0; i < d_outputs.size(); i++) {
+ gr_basic_block_sptr blk = d_outputs[i].block();
+ if (!blk) {
+ msg << "In hierarchical block " << d_owner->name() << ", output " << i
+ << " is not connected internally";
+ throw std::runtime_error(msg.str());
+ }
+ tmp.push_back(blk);
+ }
+ sort(tmp.begin(), tmp.end());
+
+ std::insert_iterator<gr_basic_block_vector_t> inserter(blocks, blocks.begin());
+ unique_copy(tmp.begin(), tmp.end(), inserter);
// Recurse hierarchical children
for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) {
- gr_hier_block2_sptr hier_block2(boost::dynamic_pointer_cast<gr_hier_block2, gr_basic_block>(*p));
- if (hier_block2)
+ gr_hier_block2_sptr hier_block2(cast_to_hier_block2_sptr(*p));
+ if (hier_block2) {
+ if (GR_HIER_BLOCK2_DETAIL_DEBUG)
+ std::cout << "flatten_aux: recursing into hierarchical block " << hier_block2 << std::endl;
hier_block2->d_detail->flatten_aux(sfg);
+ }
}
}